-adapting fcfsd to new namestore API, moving from gns to namestore subsystem
[oweals/gnunet.git] / src / namestore / gnunet-namestore-fcfsd.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012-2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file gnunet-gns-fcfsd.c
22  * @brief HTTP daemon that offers first-come-first-serve GNS domain registration
23  * @author Christian Grothoff
24  *
25  * TODO:
26  * - the code currently contains a 'race' between checking that the
27  *   domain name is available and allocating it to the new public key
28  *   (should this race be solved by namestore or by fcfsd?)
29  * - nicer error reporting to browser
30  */
31 #include "platform.h"
32 #include <microhttpd.h>
33 #include "gnunet_util_lib.h"
34 #include "gnunet_namestore_service.h"
35
36 /**
37  * Invalid method page.
38  */
39 #define METHOD_ERROR "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>Illegal request</title></head><body>Go away.</body></html>"
40
41 /**
42  * Front page. (/)
43  */
44 #define MAIN_PAGE "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>GNUnet FCFS Authority Name Registration Service</title></head><body><form action=\"S\" method=\"post\">What is your desired domain name? (at most 63 lowercase characters, no dots allowed.) <input type=\"text\" name=\"domain\" /> <p> What is your public key? (Copy from gnunet-setup.) <input type=\"text\" name=\"pkey\" /> <input type=\"submit\" value=\"Next\" /><br/><a href=./Zoneinfo> List of all registered names </a></body></html>"
45
46 /**
47  * Second page (/S)
48  */
49 #define SUBMIT_PAGE "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>%s</title></head><body>%s</body></html>"
50
51 /**
52  * Fcfs zoneinfo page (/Zoneinfo)
53  */
54 #define ZONEINFO_PAGE "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>FCFS Zoneinfo</title></head><body><h1> FCFS Zoneinfo </h1><table border=\"1\"><th>name</th><th>PKEY</th>%s</table></body></html>"
55
56 #define FCFS_ZONEINFO_URL "/Zoneinfo"
57
58 /**
59  * Mime type for HTML pages.
60  */
61 #define MIME_HTML "text/html"
62
63 /**
64  * Name of our cookie.
65  */
66 #define COOKIE_NAME "gns-fcfs"
67
68 #define DEFAULT_ZONEINFO_BUFSIZE 2048
69
70 /**
71  * Phases a request goes through.
72  */
73 enum Phase
74   {
75     /**
76      * Start phase (parsing POST, checking).
77      */
78     RP_START = 0,
79
80     /**
81      * Lookup to see if the domain name is taken.
82      */
83     RP_LOOKUP,
84
85     /**
86      * Storing of the record.
87      */
88     RP_PUT,
89
90     /**
91      * We're done with success.
92      */
93     RP_SUCCESS,
94
95     /**
96      * Send failure message.
97      */
98     RP_FAIL
99   };
100
101
102 /**
103  * Data kept per request.
104  */
105 struct Request
106 {
107
108   /**
109    * Associated session.
110    */
111   struct Session *session;
112
113   /**
114    * Post processor handling form data (IF this is
115    * a POST request).
116    */
117   struct MHD_PostProcessor *pp;
118
119   /**
120    * URL to serve in response to this POST (if this request 
121    * was a 'POST')
122    */
123   const char *post_url;
124
125   /**
126    * Active request with the namestore.
127    */
128   struct GNUNET_NAMESTORE_QueueEntry *qe;
129   
130   /**
131    * Current processing phase.
132    */
133   enum Phase phase;
134
135   /**
136    * Domain name submitted via form.
137    */
138   char domain_name[64];
139
140   /**
141    * Public key submitted via form.
142    */
143   char public_key[128];
144
145 };
146
147 /**
148  * Zoneinfo request
149  */
150 struct ZoneinfoRequest
151 {
152   /**
153    * Connection
154    */
155   struct MHD_Connection *connection;
156
157   /**
158    * List iterator
159    */
160   struct GNUNET_NAMESTORE_ZoneIterator *list_it;
161
162   /**
163    * Buffer
164    */
165   char* zoneinfo;
166
167   /**
168    * Buffer length
169    */
170   size_t buf_len;
171   
172   /**
173    * Buffer write offset
174    */
175   size_t write_offset;
176 };
177
178 /**
179  * MHD deamon reference.
180  */
181 static struct MHD_Daemon *httpd;
182
183 /**
184  * Main HTTP task.
185  */
186 static GNUNET_SCHEDULER_TaskIdentifier httpd_task;
187
188 /**
189  * Handle to the namestore.
190  */
191 static struct GNUNET_NAMESTORE_Handle *ns;
192
193 /**
194  * Private key for the fcfsd zone.
195  */
196 static struct GNUNET_CRYPTO_EccPrivateKey *fcfs_zone_pkey;
197                         
198
199 /**
200  * Task run whenever HTTP server operations are pending.
201  *
202  * @param cls unused
203  * @param tc scheduler context
204  */
205 static void
206 do_httpd (void *cls,
207           const struct GNUNET_SCHEDULER_TaskContext *tc);
208
209
210 /**
211  * Schedule task to run MHD server now.
212  */
213 static void
214 run_httpd_now ()
215 {
216   if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
217   {
218     GNUNET_SCHEDULER_cancel (httpd_task);
219     httpd_task = GNUNET_SCHEDULER_NO_TASK;
220   }
221   httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
222 }
223
224
225 static void
226 iterate_cb (void *cls,
227             const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
228             const char *name,
229             unsigned int rd_len,
230             const struct GNUNET_NAMESTORE_RecordData *rd)
231 {
232   struct ZoneinfoRequest *zr = cls;
233   struct MHD_Response *response;
234   char* full_page;
235   size_t bytes_free;
236   char* pkey;
237   char* new_buf;
238
239
240   if (NULL == name)
241   {
242     zr->list_it = NULL;
243
244     /* return static form */
245     GNUNET_asprintf (&full_page,
246                      ZONEINFO_PAGE,
247                      zr->zoneinfo,
248                      zr->zoneinfo);
249     response = MHD_create_response_from_buffer (strlen (full_page),
250                                               (void *) full_page,
251                                               MHD_RESPMEM_MUST_FREE);
252     MHD_add_response_header (response,
253                            MHD_HTTP_HEADER_CONTENT_TYPE,
254                            MIME_HTML);
255     MHD_queue_response (zr->connection, 
256                             MHD_HTTP_OK, 
257                             response);
258     MHD_destroy_response (response);
259     GNUNET_free (zr->zoneinfo);
260     GNUNET_free (zr);
261     run_httpd_now ();
262     return;
263   }
264
265   if (1 != rd_len)
266   {
267     GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
268     return;
269   }
270
271   if (GNUNET_NAMESTORE_TYPE_PKEY != rd->record_type)
272   {
273     GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
274     return;
275   }
276
277   bytes_free = zr->buf_len - zr->write_offset;
278   pkey = GNUNET_NAMESTORE_value_to_string (rd->record_type,
279                                            rd->data,
280                                            rd->data_size);
281
282   if (bytes_free < (strlen (name) + strlen (pkey) + 40))
283   {
284     new_buf = GNUNET_malloc (zr->buf_len * 2);
285     memcpy (new_buf, zr->zoneinfo, zr->write_offset);
286     GNUNET_free (zr->zoneinfo);
287     zr->zoneinfo = new_buf;
288     zr->buf_len *= 2;
289   }
290   sprintf (zr->zoneinfo + zr->write_offset, 
291            "<tr><td>%s</td><td>%s</td></tr>", 
292            name, 
293            pkey);
294   zr->write_offset = strlen (zr->zoneinfo);
295   GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
296   GNUNET_free (pkey);
297 }
298
299
300
301 /**
302  * Handler that returns FCFS zoneinfo page.
303  *
304  * @param connection connection to use
305  * @return MHD_YES on success
306  */
307 static int
308 serve_zoneinfo_page (struct MHD_Connection *connection)
309 {
310   struct ZoneinfoRequest *zr;
311
312   zr = GNUNET_new (struct ZoneinfoRequest);
313   zr->zoneinfo = GNUNET_malloc (DEFAULT_ZONEINFO_BUFSIZE);
314   zr->buf_len = DEFAULT_ZONEINFO_BUFSIZE;
315   zr->connection = connection;
316   zr->write_offset = 0;
317   zr->list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
318                                                        fcfs_zone_pkey,
319                                                        &iterate_cb,
320                                                        zr);
321   return MHD_YES;
322 }
323
324
325 /**
326  * Handler that returns a simple static HTTP page.
327  *
328  * @param connection connection to use
329  * @return MHD_YES on success
330  */
331 static int
332 serve_main_page (struct MHD_Connection *connection)
333 {
334   int ret;
335   struct MHD_Response *response;
336
337   /* return static form */
338   response = MHD_create_response_from_buffer (strlen (MAIN_PAGE),
339                                               (void *) MAIN_PAGE,
340                                               MHD_RESPMEM_PERSISTENT);
341   MHD_add_response_header (response,
342                            MHD_HTTP_HEADER_CONTENT_TYPE,
343                            MIME_HTML);
344   ret = MHD_queue_response (connection, 
345                             MHD_HTTP_OK, 
346                             response);
347   MHD_destroy_response (response);
348   return ret;
349 }
350
351
352 /**
353  * Send the 'SUBMIT_PAGE'.
354  *
355  * @param info information string to send to the user
356  * @param request request information
357  * @param connection connection to use
358  */
359 static int
360 fill_s_reply (const char *info,
361               struct Request *request,
362               struct MHD_Connection *connection)
363 {
364   int ret;
365   char *reply;
366   struct MHD_Response *response;
367
368   GNUNET_asprintf (&reply,
369                    SUBMIT_PAGE,
370                    info,
371                    info);
372   /* return static form */
373   response = MHD_create_response_from_buffer (strlen (reply),
374                                               (void *) reply,
375                                               MHD_RESPMEM_MUST_FREE);
376   MHD_add_response_header (response,
377                            MHD_HTTP_HEADER_CONTENT_TYPE,
378                            MIME_HTML);
379   ret = MHD_queue_response (connection, 
380                             MHD_HTTP_OK, 
381                             response);
382   MHD_destroy_response (response);
383   return ret;
384 }
385
386
387 /**
388  * Iterator over key-value pairs where the value
389  * maybe made available in increments and/or may
390  * not be zero-terminated.  Used for processing
391  * POST data.
392  *
393  * @param cls user-specified closure
394  * @param kind type of the value
395  * @param key 0-terminated key for the value
396  * @param filename name of the uploaded file, NULL if not known
397  * @param content_type mime-type of the data, NULL if not known
398  * @param transfer_encoding encoding of the data, NULL if not known
399  * @param data pointer to size bytes of data at the
400  *              specified offset
401  * @param off offset of data in the overall value
402  * @param size number of bytes in data available
403  * @return MHD_YES to continue iterating,
404  *         MHD_NO to abort the iteration
405  */
406 static int
407 post_iterator (void *cls,
408                enum MHD_ValueKind kind,
409                const char *key,
410                const char *filename,
411                const char *content_type,
412                const char *transfer_encoding,
413                const char *data, uint64_t off, size_t size)
414 {
415   struct Request *request = cls;
416
417   if (0 == strcmp ("domain", key))
418     {
419       if (size + off >= sizeof(request->domain_name))
420         size = sizeof (request->domain_name) - off - 1;
421       memcpy (&request->domain_name[off],
422               data,
423               size);
424       request->domain_name[size+off] = '\0';
425       return MHD_YES;
426     }
427   if (0 == strcmp ("pkey", key))
428     {
429       if (size + off >= sizeof(request->public_key))
430         size = sizeof (request->public_key) - off - 1;
431       memcpy (&request->public_key[off],
432               data,
433               size);
434       request->public_key[size+off] = '\0';
435       return MHD_YES;
436     }
437   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
438               _("Unsupported form value `%s'\n"),
439               key);
440   return MHD_YES;
441 }
442
443
444
445
446 /**
447  * Continuation called to notify client about result of the
448  * operation.
449  *
450  * @param cls closure
451  * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
452  *                GNUNET_NO if content was already there
453  *                GNUNET_YES (or other positive value) on success
454  * @param emsg NULL on success, otherwise an error message
455  */
456 static void 
457 put_continuation (void *cls,
458                   int32_t success,
459                   const char *emsg)
460 {
461   struct Request *request = cls;
462
463   request->qe = NULL;
464   if (0 >= success)
465   {
466     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
467                 _("Failed to create record for domain `%s': %s\n"),
468                 request->domain_name,
469                 emsg);
470     request->phase = RP_FAIL;
471   }
472   else
473     request->phase = RP_SUCCESS;
474   run_httpd_now ();
475 }
476
477
478 /**
479  * Test if a name mapping was found, if so, refuse.  If not, initiate storing of the record.
480  *
481  * @param cls closure
482  * @param zone_key public key of the zone
483  * @param name name that is being mapped (at most 255 characters long)
484  * @param rd_count number of entries in 'rd' array
485  * @param rd array of records with data to store
486  */
487 static void 
488 zone_to_name_cb (void *cls,
489                  const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
490                  const char *name,
491                  unsigned int rd_count,
492                  const struct GNUNET_NAMESTORE_RecordData *rd)
493 {
494   struct Request *request = cls;
495   struct GNUNET_NAMESTORE_RecordData r;
496   struct GNUNET_CRYPTO_ShortHashCode pub;
497   
498   request->qe = NULL;
499   if (NULL != name)
500   {
501     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
502                 _("Found existing name `%s' for the given key\n"),
503                 name);
504     request->phase = RP_FAIL;
505     run_httpd_now ();
506     return;
507   }
508   r.data = &pub;
509   r.data_size = sizeof (pub);
510   r.expiration_time = UINT64_MAX;
511   r.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
512   r.flags = GNUNET_NAMESTORE_RF_AUTHORITY;
513   request->qe = GNUNET_NAMESTORE_records_store (ns,
514                                                 fcfs_zone_pkey,
515                                                 request->domain_name,
516                                                 1, &r,
517                                                 &put_continuation,
518                                                 request);
519 }
520
521
522 /**
523  * Process a record that was stored in the namestore.  Used to check if
524  * the requested name already exists in the namestore.  If not,
525  * proceed to check if the requested key already exists.
526  *
527  * @param cls closure
528  * @param zone_key private key of the zone
529  * @param name name that is being mapped (at most 255 characters long)
530  * @param rd_count number of entries in 'rd' array
531  * @param rd array of records with data to store
532  */
533 static void 
534 lookup_result_processor (void *cls,
535                          const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
536                          const char *name,
537                          unsigned int rd_count,
538                          const struct GNUNET_NAMESTORE_RecordData *rd)
539 {
540   struct Request *request = cls;
541   struct GNUNET_CRYPTO_EccPublicKey pub;
542   
543   request->qe = NULL;
544   if (0 != rd_count)
545   {
546     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
547                 _("Found %u existing records for domain `%s'\n"),
548                 rd_count,
549                 request->domain_name);
550     request->phase = RP_FAIL;
551     run_httpd_now ();
552     return;
553   }
554   if (GNUNET_OK !=
555       GNUNET_CRYPTO_ecc_public_key_from_string (request->public_key,
556                                                 strlen (request->public_key),
557                                                 &pub))
558   {
559     GNUNET_break (0);
560     request->phase = RP_FAIL;
561     run_httpd_now ();
562     return;
563   }
564   request->qe = GNUNET_NAMESTORE_zone_to_name (ns,
565                                                fcfs_zone_pkey,
566                                                &pub,
567                                                &zone_to_name_cb,
568                                                request);
569 }
570
571
572 /**
573  * Main MHD callback for handling requests.
574  *
575  * @param cls unused
576  * @param connection MHD connection handle
577  * @param url the requested url
578  * @param method the HTTP method used ("GET", "PUT", etc.)
579  * @param version the HTTP version string (i.e. "HTTP/1.1")
580  * @param upload_data the data being uploaded (excluding HEADERS,
581  *        for a POST that fits into memory and that is encoded
582  *        with a supported encoding, the POST data will NOT be
583  *        given in upload_data and is instead available as
584  *        part of MHD_get_connection_values; very large POST
585  *        data *will* be made available incrementally in
586  *        upload_data)
587  * @param upload_data_size set initially to the size of the
588  *        upload_data provided; the method must update this
589  *        value to the number of bytes NOT processed;
590  * @param ptr pointer to location where we store the 'struct Request'
591  * @return MHD_YES if the connection was handled successfully,
592  *         MHD_NO if the socket must be closed due to a serious
593  *         error while handling the request
594  */
595 static int
596 create_response (void *cls,
597                  struct MHD_Connection *connection,
598                  const char *url,
599                  const char *method,
600                  const char *version,
601                  const char *upload_data, 
602                  size_t *upload_data_size,
603                  void **ptr)
604 {
605   struct MHD_Response *response;
606   struct Request *request;
607   int ret;
608   struct GNUNET_CRYPTO_EccPublicKey pub;
609
610   if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
611        (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
612     {
613       if (0 == strcmp (url, FCFS_ZONEINFO_URL))
614         ret = serve_zoneinfo_page (connection);
615       else
616         ret = serve_main_page (connection);
617       if (ret != MHD_YES)
618         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
619                     _("Failed to create page for `%s'\n"),
620                     url);
621       return ret;
622     }
623   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
624     {   
625       request = *ptr;
626       if (NULL == request)
627       {
628         request = GNUNET_malloc (sizeof (struct Request));
629         *ptr = request;
630         request->pp = MHD_create_post_processor (connection, 1024,
631                                                  &post_iterator, request);
632         if (NULL == request->pp)
633           {
634             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
635                         _("Failed to setup post processor for `%s'\n"),
636                         url);
637             return MHD_NO; /* internal error */
638           }    
639         return MHD_YES;
640       }
641       if (NULL != request->pp)
642       {
643         /* evaluate POST data */
644         MHD_post_process (request->pp,
645                           upload_data,
646                           *upload_data_size);
647         if (0 != *upload_data_size)
648           {
649             *upload_data_size = 0;
650             return MHD_YES;
651           }
652         /* done with POST data, serve response */
653         MHD_destroy_post_processor (request->pp);
654         request->pp = NULL;
655       }
656       if (GNUNET_OK !=
657           GNUNET_CRYPTO_ecc_public_key_from_string (request->public_key,
658                                                     strlen (request->public_key),
659                                                     &pub))
660       {
661         /* parse error */
662         return fill_s_reply ("Failed to parse given public key",
663                              request, connection);
664       }
665       switch (request->phase)
666         {
667         case RP_START:
668           if (NULL != strchr (request->domain_name, (int) '.'))
669           {
670             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
671                         _("Domain name must not contain `.'\n"));
672             request->phase = RP_FAIL;
673             return fill_s_reply ("Domain name must not contain `.', sorry.",
674                                  request, connection);
675           }
676           if (NULL != strchr (request->domain_name, (int) '+'))
677           {
678             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
679                         _("Domain name must not contain `+'\n"));
680             request->phase = RP_FAIL;
681             return fill_s_reply ("Domain name must not contain `+', sorry.",
682                                  request, connection);
683           }
684           request->phase = RP_LOOKUP;
685           GNUNET_CRYPTO_ecc_key_get_public (fcfs_zone_pkey,
686                                             &pub);
687           request->qe = GNUNET_NAMESTORE_lookup (ns,
688                                                  &pub,
689                                                  request->domain_name,
690                                                  &lookup_result_processor,
691                                                  request);
692           break;
693         case RP_LOOKUP:
694           break;
695         case RP_PUT:
696           break;
697         case RP_FAIL:
698           return fill_s_reply ("Request failed, sorry.",
699                                request, connection);
700         case RP_SUCCESS:
701           return fill_s_reply ("Success.",
702                                request, connection);
703         default:
704           GNUNET_break (0);
705           return MHD_NO;
706         }
707         return MHD_YES; /* will have a reply later... */    
708     }
709   /* unsupported HTTP method */
710   response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
711                                               (void *) METHOD_ERROR,
712                                               MHD_RESPMEM_PERSISTENT);
713   ret = MHD_queue_response (connection, 
714                             MHD_HTTP_METHOD_NOT_ACCEPTABLE, 
715                             response);
716   MHD_destroy_response (response);
717   return ret;
718 }
719
720
721 /**
722  * Callback called upon completion of a request.
723  * Decrements session reference counter.
724  *
725  * @param cls not used
726  * @param connection connection that completed
727  * @param con_cls session handle
728  * @param toe status code
729  */
730 static void
731 request_completed_callback (void *cls,
732                             struct MHD_Connection *connection,
733                             void **con_cls,
734                             enum MHD_RequestTerminationCode toe)
735 {
736   struct Request *request = *con_cls;
737
738   if (NULL == request)
739     return;
740   if (NULL != request->pp)
741     MHD_destroy_post_processor (request->pp);
742   if (NULL != request->qe)
743     GNUNET_NAMESTORE_cancel (request->qe);
744   GNUNET_free (request);
745 }
746
747
748 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
749
750
751 /**
752  * Schedule tasks to run MHD server.
753  */
754 static void
755 run_httpd ()
756 {
757   fd_set rs;
758   fd_set ws;
759   fd_set es;
760   struct GNUNET_NETWORK_FDSet *wrs;
761   struct GNUNET_NETWORK_FDSet *wws;
762   struct GNUNET_NETWORK_FDSet *wes;
763   int max;
764   int haveto;
765   UNSIGNED_MHD_LONG_LONG timeout;
766   struct GNUNET_TIME_Relative tv;
767
768   FD_ZERO (&rs);
769   FD_ZERO (&ws);
770   FD_ZERO (&es);
771   wrs = GNUNET_NETWORK_fdset_create ();
772   wes = GNUNET_NETWORK_fdset_create ();
773   wws = GNUNET_NETWORK_fdset_create ();
774   max = -1;
775   GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
776   haveto = MHD_get_timeout (httpd, &timeout);
777   if (haveto == MHD_YES)
778     tv.rel_value_us = (uint64_t) timeout * 1000LL;
779   else
780     tv = GNUNET_TIME_UNIT_FOREVER_REL;
781   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
782   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
783   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
784   httpd_task =
785       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
786                                    tv, wrs, wws,
787                                    &do_httpd, NULL);
788   GNUNET_NETWORK_fdset_destroy (wrs);
789   GNUNET_NETWORK_fdset_destroy (wws);
790   GNUNET_NETWORK_fdset_destroy (wes);
791 }
792
793
794 /**
795  * Task run whenever HTTP server operations are pending.
796  *
797  * @param cls unused
798  * @param tc scheduler context
799  */
800 static void
801 do_httpd (void *cls,
802           const struct GNUNET_SCHEDULER_TaskContext *tc)
803 {
804   httpd_task = GNUNET_SCHEDULER_NO_TASK;
805   MHD_run (httpd);
806   run_httpd ();
807 }
808
809
810 /**
811  * Task run on shutdown.  Cleans up everything.
812  *
813  * @param cls unused
814  * @param tc scheduler context
815  */
816 static void
817 do_shutdown (void *cls,
818              const struct GNUNET_SCHEDULER_TaskContext *tc)
819 {
820   if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
821   {
822     GNUNET_SCHEDULER_cancel (httpd_task);
823     httpd_task = GNUNET_SCHEDULER_NO_TASK;
824   }
825   if (NULL != ns)
826   {
827     GNUNET_NAMESTORE_disconnect (ns);
828     ns = NULL;
829   }
830   if (NULL != httpd)
831   {
832     MHD_stop_daemon (httpd);
833     httpd = NULL;
834   }
835   if (NULL != fcfs_zone_pkey)
836   {
837     GNUNET_CRYPTO_ecc_key_free (fcfs_zone_pkey);
838     fcfs_zone_pkey = NULL;
839   }
840 }
841
842
843 /**
844  * Main function that will be run.
845  *
846  * @param cls closure
847  * @param args remaining command-line arguments
848  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
849  * @param cfg configuration
850  */
851 static void
852 run (void *cls, char *const *args, const char *cfgfile,
853      const struct GNUNET_CONFIGURATION_Handle *cfg)
854 {
855   char *keyfile;
856   unsigned long long port;
857
858   if (GNUNET_OK !=
859       GNUNET_CONFIGURATION_get_value_number (cfg,
860                                              "fcfsd",
861                                              "HTTPPORT",
862                                              &port))
863   {
864     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
865                                "fcfsd", "HTTPPORT");
866     return;
867   }
868   if (GNUNET_OK !=
869       GNUNET_CONFIGURATION_get_value_filename (cfg,
870                                                "fcfsd",
871                                                "ZONEKEY",
872                                                &keyfile))
873   {
874     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
875                                "fcfsd", "ZONEKEY");
876     return;
877   }
878   fcfs_zone_pkey = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile);
879   GNUNET_free (keyfile);
880   if (NULL == fcfs_zone_pkey)
881   {
882     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
883                 _("Failed to read or create private zone key\n"));
884     return;
885   }
886   ns = GNUNET_NAMESTORE_connect (cfg);
887   if (NULL == ns)
888     {
889       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
890                   _("Failed to connect to namestore\n"));
891       return;
892     }
893   httpd = MHD_start_daemon (MHD_USE_DEBUG,
894                             (uint16_t) port,
895                             NULL, NULL, 
896                             &create_response, NULL, 
897                             MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
898                             MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
899                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
900                             MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4 * 1024), 
901                             MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
902                             MHD_OPTION_END);
903   if (NULL == httpd)
904   {
905     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
906                 _("Failed to start HTTP server\n"));
907     GNUNET_NAMESTORE_disconnect (ns);
908     ns = NULL;
909     return;
910   }
911   run_httpd ();
912   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
913                                 &do_shutdown, NULL);
914 }
915
916
917 /**
918  * The main function for the fcfs daemon.
919  *
920  * @param argc number of arguments from the command line
921  * @param argv command line arguments
922  * @return 0 ok, 1 on error
923  */
924 int
925 main (int argc, char *const *argv)
926 {
927   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
928     GNUNET_GETOPT_OPTION_END
929   };
930
931   int ret;
932
933   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
934     return 2;
935
936   GNUNET_log_setup ("fcfsd", "WARNING", NULL);
937   ret =
938       (GNUNET_OK ==
939        GNUNET_PROGRAM_run (argc, argv, "fcfsd",
940                            _("GNUnet GNS first come first serve registration service"), 
941                            options,
942                            &run, NULL)) ? 0 : 1;
943   GNUNET_free ((void*) argv);
944   return ret;
945 }
946
947 /* end of gnunet-gns-fcfsd.c */