-doxygen
[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-namestore-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 rd_count number of entries in 'rd' array
529  * @param rd array of records with data to store
530  */
531 static void 
532 lookup_result_processor (void *cls,
533                          unsigned int rd_count,
534                          const struct GNUNET_NAMESTORE_RecordData *rd)
535 {
536   struct Request *request = cls;
537   struct GNUNET_CRYPTO_EccPublicKey pub;
538   
539   if (0 != rd_count)
540   {
541     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
542                 _("Found %u existing records for domain `%s'\n"),
543                 rd_count,
544                 request->domain_name);
545     request->phase = RP_FAIL;
546     run_httpd_now ();
547     return;
548   }
549   if (GNUNET_OK !=
550       GNUNET_CRYPTO_ecc_public_key_from_string (request->public_key,
551                                                 strlen (request->public_key),
552                                                 &pub))
553   {
554     GNUNET_break (0);
555     request->phase = RP_FAIL;
556     run_httpd_now ();
557     return;
558   }
559   request->qe = GNUNET_NAMESTORE_zone_to_name (ns,
560                                                fcfs_zone_pkey,
561                                                &pub,
562                                                &zone_to_name_cb,
563                                                request);
564 }
565
566
567 /**
568  * We got a block back from the namestore.  Decrypt it
569  * and continue to process the result.
570  *
571  * @param cls the 'struct Request' we are processing
572  * @param block block returned form namestore, NULL on error
573  */
574 static void
575 lookup_block_processor (void *cls,
576                         const struct GNUNET_NAMESTORE_Block *block)
577 {
578   struct Request *request = cls;
579   struct GNUNET_CRYPTO_EccPublicKey pub;
580
581   request->qe = NULL;
582   if (NULL == block)
583   {
584     lookup_result_processor (request, 0, NULL);
585     return;
586   }
587   GNUNET_CRYPTO_ecc_key_get_public (fcfs_zone_pkey,
588                                     &pub);
589   if (GNUNET_OK != 
590       GNUNET_NAMESTORE_block_decrypt (block,
591                                       &pub,
592                                       request->domain_name,
593                                       &lookup_result_processor,
594                                       request))
595   {
596     GNUNET_break (0);
597     request->phase = RP_FAIL;
598     run_httpd_now ();
599     return;
600   }
601 }
602
603
604 /**
605  * Main MHD callback for handling requests.
606  *
607  * @param cls unused
608  * @param connection MHD connection handle
609  * @param url the requested url
610  * @param method the HTTP method used ("GET", "PUT", etc.)
611  * @param version the HTTP version string (i.e. "HTTP/1.1")
612  * @param upload_data the data being uploaded (excluding HEADERS,
613  *        for a POST that fits into memory and that is encoded
614  *        with a supported encoding, the POST data will NOT be
615  *        given in upload_data and is instead available as
616  *        part of MHD_get_connection_values; very large POST
617  *        data *will* be made available incrementally in
618  *        upload_data)
619  * @param upload_data_size set initially to the size of the
620  *        upload_data provided; the method must update this
621  *        value to the number of bytes NOT processed;
622  * @param ptr pointer to location where we store the 'struct Request'
623  * @return MHD_YES if the connection was handled successfully,
624  *         MHD_NO if the socket must be closed due to a serious
625  *         error while handling the request
626  */
627 static int
628 create_response (void *cls,
629                  struct MHD_Connection *connection,
630                  const char *url,
631                  const char *method,
632                  const char *version,
633                  const char *upload_data, 
634                  size_t *upload_data_size,
635                  void **ptr)
636 {
637   struct MHD_Response *response;
638   struct Request *request;
639   int ret;
640   struct GNUNET_CRYPTO_EccPublicKey pub;
641   struct GNUNET_HashCode query;
642
643   if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
644        (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
645     {
646       if (0 == strcmp (url, FCFS_ZONEINFO_URL))
647         ret = serve_zoneinfo_page (connection);
648       else
649         ret = serve_main_page (connection);
650       if (ret != MHD_YES)
651         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
652                     _("Failed to create page for `%s'\n"),
653                     url);
654       return ret;
655     }
656   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
657     {   
658       request = *ptr;
659       if (NULL == request)
660       {
661         request = GNUNET_malloc (sizeof (struct Request));
662         *ptr = request;
663         request->pp = MHD_create_post_processor (connection, 1024,
664                                                  &post_iterator, request);
665         if (NULL == request->pp)
666           {
667             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
668                         _("Failed to setup post processor for `%s'\n"),
669                         url);
670             return MHD_NO; /* internal error */
671           }    
672         return MHD_YES;
673       }
674       if (NULL != request->pp)
675       {
676         /* evaluate POST data */
677         MHD_post_process (request->pp,
678                           upload_data,
679                           *upload_data_size);
680         if (0 != *upload_data_size)
681           {
682             *upload_data_size = 0;
683             return MHD_YES;
684           }
685         /* done with POST data, serve response */
686         MHD_destroy_post_processor (request->pp);
687         request->pp = NULL;
688       }
689       if (GNUNET_OK !=
690           GNUNET_CRYPTO_ecc_public_key_from_string (request->public_key,
691                                                     strlen (request->public_key),
692                                                     &pub))
693       {
694         /* parse error */
695         return fill_s_reply ("Failed to parse given public key",
696                              request, connection);
697       }
698       switch (request->phase)
699         {
700         case RP_START:
701           if (NULL != strchr (request->domain_name, (int) '.'))
702           {
703             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
704                         _("Domain name must not contain `.'\n"));
705             request->phase = RP_FAIL;
706             return fill_s_reply ("Domain name must not contain `.', sorry.",
707                                  request, connection);
708           }
709           if (NULL != strchr (request->domain_name, (int) '+'))
710           {
711             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
712                         _("Domain name must not contain `+'\n"));
713             request->phase = RP_FAIL;
714             return fill_s_reply ("Domain name must not contain `+', sorry.",
715                                  request, connection);
716           }
717           request->phase = RP_LOOKUP;
718           GNUNET_CRYPTO_ecc_key_get_public (fcfs_zone_pkey,
719                                             &pub);
720           GNUNET_NAMESTORE_query_from_public_key (&pub,
721                                                   request->domain_name,
722                                                   &query);
723           request->qe = GNUNET_NAMESTORE_lookup_block (ns,
724                                                        &query,
725                                                        &lookup_block_processor,
726                                                        request);
727           break;
728         case RP_LOOKUP:
729           break;
730         case RP_PUT:
731           break;
732         case RP_FAIL:
733           return fill_s_reply ("Request failed, sorry.",
734                                request, connection);
735         case RP_SUCCESS:
736           return fill_s_reply ("Success.",
737                                request, connection);
738         default:
739           GNUNET_break (0);
740           return MHD_NO;
741         }
742         return MHD_YES; /* will have a reply later... */    
743     }
744   /* unsupported HTTP method */
745   response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
746                                               (void *) METHOD_ERROR,
747                                               MHD_RESPMEM_PERSISTENT);
748   ret = MHD_queue_response (connection, 
749                             MHD_HTTP_METHOD_NOT_ACCEPTABLE, 
750                             response);
751   MHD_destroy_response (response);
752   return ret;
753 }
754
755
756 /**
757  * Callback called upon completion of a request.
758  * Decrements session reference counter.
759  *
760  * @param cls not used
761  * @param connection connection that completed
762  * @param con_cls session handle
763  * @param toe status code
764  */
765 static void
766 request_completed_callback (void *cls,
767                             struct MHD_Connection *connection,
768                             void **con_cls,
769                             enum MHD_RequestTerminationCode toe)
770 {
771   struct Request *request = *con_cls;
772
773   if (NULL == request)
774     return;
775   if (NULL != request->pp)
776     MHD_destroy_post_processor (request->pp);
777   if (NULL != request->qe)
778     GNUNET_NAMESTORE_cancel (request->qe);
779   GNUNET_free (request);
780 }
781
782
783 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
784
785
786 /**
787  * Schedule tasks to run MHD server.
788  */
789 static void
790 run_httpd ()
791 {
792   fd_set rs;
793   fd_set ws;
794   fd_set es;
795   struct GNUNET_NETWORK_FDSet *wrs;
796   struct GNUNET_NETWORK_FDSet *wws;
797   struct GNUNET_NETWORK_FDSet *wes;
798   int max;
799   int haveto;
800   UNSIGNED_MHD_LONG_LONG timeout;
801   struct GNUNET_TIME_Relative tv;
802
803   FD_ZERO (&rs);
804   FD_ZERO (&ws);
805   FD_ZERO (&es);
806   wrs = GNUNET_NETWORK_fdset_create ();
807   wes = GNUNET_NETWORK_fdset_create ();
808   wws = GNUNET_NETWORK_fdset_create ();
809   max = -1;
810   GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
811   haveto = MHD_get_timeout (httpd, &timeout);
812   if (haveto == MHD_YES)
813     tv.rel_value_us = (uint64_t) timeout * 1000LL;
814   else
815     tv = GNUNET_TIME_UNIT_FOREVER_REL;
816   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
817   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
818   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
819   httpd_task =
820       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
821                                    tv, wrs, wws,
822                                    &do_httpd, NULL);
823   GNUNET_NETWORK_fdset_destroy (wrs);
824   GNUNET_NETWORK_fdset_destroy (wws);
825   GNUNET_NETWORK_fdset_destroy (wes);
826 }
827
828
829 /**
830  * Task run whenever HTTP server operations are pending.
831  *
832  * @param cls unused
833  * @param tc scheduler context
834  */
835 static void
836 do_httpd (void *cls,
837           const struct GNUNET_SCHEDULER_TaskContext *tc)
838 {
839   httpd_task = GNUNET_SCHEDULER_NO_TASK;
840   MHD_run (httpd);
841   run_httpd ();
842 }
843
844
845 /**
846  * Task run on shutdown.  Cleans up everything.
847  *
848  * @param cls unused
849  * @param tc scheduler context
850  */
851 static void
852 do_shutdown (void *cls,
853              const struct GNUNET_SCHEDULER_TaskContext *tc)
854 {
855   if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
856   {
857     GNUNET_SCHEDULER_cancel (httpd_task);
858     httpd_task = GNUNET_SCHEDULER_NO_TASK;
859   }
860   if (NULL != ns)
861   {
862     GNUNET_NAMESTORE_disconnect (ns);
863     ns = NULL;
864   }
865   if (NULL != httpd)
866   {
867     MHD_stop_daemon (httpd);
868     httpd = NULL;
869   }
870   if (NULL != fcfs_zone_pkey)
871   {
872     GNUNET_CRYPTO_ecc_key_free (fcfs_zone_pkey);
873     fcfs_zone_pkey = NULL;
874   }
875 }
876
877
878 /**
879  * Main function that will be run.
880  *
881  * @param cls closure
882  * @param args remaining command-line arguments
883  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
884  * @param cfg configuration
885  */
886 static void
887 run (void *cls, char *const *args, const char *cfgfile,
888      const struct GNUNET_CONFIGURATION_Handle *cfg)
889 {
890   char *keyfile;
891   unsigned long long port;
892
893   if (GNUNET_OK !=
894       GNUNET_CONFIGURATION_get_value_number (cfg,
895                                              "fcfsd",
896                                              "HTTPPORT",
897                                              &port))
898   {
899     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
900                                "fcfsd", "HTTPPORT");
901     return;
902   }
903   if (GNUNET_OK !=
904       GNUNET_CONFIGURATION_get_value_filename (cfg,
905                                                "fcfsd",
906                                                "ZONEKEY",
907                                                &keyfile))
908   {
909     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
910                                "fcfsd", "ZONEKEY");
911     return;
912   }
913   fcfs_zone_pkey = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile);
914   GNUNET_free (keyfile);
915   if (NULL == fcfs_zone_pkey)
916   {
917     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
918                 _("Failed to read or create private zone key\n"));
919     return;
920   }
921   ns = GNUNET_NAMESTORE_connect (cfg);
922   if (NULL == ns)
923     {
924       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
925                   _("Failed to connect to namestore\n"));
926       return;
927     }
928   httpd = MHD_start_daemon (MHD_USE_DEBUG,
929                             (uint16_t) port,
930                             NULL, NULL, 
931                             &create_response, NULL, 
932                             MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
933                             MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
934                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
935                             MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4 * 1024), 
936                             MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
937                             MHD_OPTION_END);
938   if (NULL == httpd)
939   {
940     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
941                 _("Failed to start HTTP server\n"));
942     GNUNET_NAMESTORE_disconnect (ns);
943     ns = NULL;
944     return;
945   }
946   run_httpd ();
947   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
948                                 &do_shutdown, NULL);
949 }
950
951
952 /**
953  * The main function for the fcfs daemon.
954  *
955  * @param argc number of arguments from the command line
956  * @param argv command line arguments
957  * @return 0 ok, 1 on error
958  */
959 int
960 main (int argc, char *const *argv)
961 {
962   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
963     GNUNET_GETOPT_OPTION_END
964   };
965
966   int ret;
967
968   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
969     return 2;
970
971   GNUNET_log_setup ("fcfsd", "WARNING", NULL);
972   ret =
973       (GNUNET_OK ==
974        GNUNET_PROGRAM_run (argc, argv, "fcfsd",
975                            _("GNUnet GNS first come first serve registration service"), 
976                            options,
977                            &run, NULL)) ? 0 : 1;
978   GNUNET_free ((void*) argv);
979   return ret;
980 }
981
982 /* end of gnunet-gns-fcfsd.c */