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