-improving gnunet-identity command line options and man pages
[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_identity_service.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 "namestore-fcfsd"
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[128];
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  * Private key for the fcfsd zone.
196  */
197 static struct GNUNET_CRYPTO_EccPrivateKey fcfs_zone_pkey;
198
199 /**
200  * Connection to identity service.
201  */
202 static struct GNUNET_IDENTITY_Handle *identity;
203
204 /**
205  * Request for our ego.
206  */
207 static struct GNUNET_IDENTITY_Operation *id_op;
208
209 /**
210  * Port we use for the HTTP server.
211  */
212 static unsigned long long port;
213                         
214
215 /**
216  * Task run whenever HTTP server operations are pending.
217  *
218  * @param cls unused
219  * @param tc scheduler context
220  */
221 static void
222 do_httpd (void *cls,
223           const struct GNUNET_SCHEDULER_TaskContext *tc);
224
225
226 /**
227  * Schedule task to run MHD server now.
228  */
229 static void
230 run_httpd_now ()
231 {
232   if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
233   {
234     GNUNET_SCHEDULER_cancel (httpd_task);
235     httpd_task = GNUNET_SCHEDULER_NO_TASK;
236   }
237   httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
238 }
239
240
241 static void
242 iterate_cb (void *cls,
243             const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
244             const char *name,
245             unsigned int rd_len,
246             const struct GNUNET_NAMESTORE_RecordData *rd)
247 {
248   struct ZoneinfoRequest *zr = cls;
249   struct MHD_Response *response;
250   char* full_page;
251   size_t bytes_free;
252   char* pkey;
253   char* new_buf;
254
255
256   if (NULL == name)
257   {
258     zr->list_it = NULL;
259
260     /* return static form */
261     GNUNET_asprintf (&full_page,
262                      ZONEINFO_PAGE,
263                      zr->zoneinfo,
264                      zr->zoneinfo);
265     response = MHD_create_response_from_buffer (strlen (full_page),
266                                               (void *) full_page,
267                                               MHD_RESPMEM_MUST_FREE);
268     MHD_add_response_header (response,
269                            MHD_HTTP_HEADER_CONTENT_TYPE,
270                            MIME_HTML);
271     MHD_queue_response (zr->connection, 
272                             MHD_HTTP_OK, 
273                             response);
274     MHD_destroy_response (response);
275     GNUNET_free (zr->zoneinfo);
276     GNUNET_free (zr);
277     run_httpd_now ();
278     return;
279   }
280
281   if (1 != rd_len)
282   {
283     GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
284     return;
285   }
286
287   if (GNUNET_NAMESTORE_TYPE_PKEY != rd->record_type)
288   {
289     GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
290     return;
291   }
292
293   bytes_free = zr->buf_len - zr->write_offset;
294   pkey = GNUNET_NAMESTORE_value_to_string (rd->record_type,
295                                            rd->data,
296                                            rd->data_size);
297
298   if (bytes_free < (strlen (name) + strlen (pkey) + 40))
299   {
300     new_buf = GNUNET_malloc (zr->buf_len * 2);
301     memcpy (new_buf, zr->zoneinfo, zr->write_offset);
302     GNUNET_free (zr->zoneinfo);
303     zr->zoneinfo = new_buf;
304     zr->buf_len *= 2;
305   }
306   sprintf (zr->zoneinfo + zr->write_offset, 
307            "<tr><td>%s</td><td>%s</td></tr>", 
308            name, 
309            pkey);
310   zr->write_offset = strlen (zr->zoneinfo);
311   GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
312   GNUNET_free (pkey);
313 }
314
315
316
317 /**
318  * Handler that returns FCFS zoneinfo page.
319  *
320  * @param connection connection to use
321  * @return MHD_YES on success
322  */
323 static int
324 serve_zoneinfo_page (struct MHD_Connection *connection)
325 {
326   struct ZoneinfoRequest *zr;
327
328   zr = GNUNET_new (struct ZoneinfoRequest);
329   zr->zoneinfo = GNUNET_malloc (DEFAULT_ZONEINFO_BUFSIZE);
330   zr->buf_len = DEFAULT_ZONEINFO_BUFSIZE;
331   zr->connection = connection;
332   zr->write_offset = 0;
333   zr->list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
334                                                        &fcfs_zone_pkey,
335                                                        &iterate_cb,
336                                                        zr);
337   return MHD_YES;
338 }
339
340
341 /**
342  * Handler that returns a simple static HTTP page.
343  *
344  * @param connection connection to use
345  * @return MHD_YES on success
346  */
347 static int
348 serve_main_page (struct MHD_Connection *connection)
349 {
350   int ret;
351   struct MHD_Response *response;
352
353   /* return static form */
354   response = MHD_create_response_from_buffer (strlen (MAIN_PAGE),
355                                               (void *) MAIN_PAGE,
356                                               MHD_RESPMEM_PERSISTENT);
357   MHD_add_response_header (response,
358                            MHD_HTTP_HEADER_CONTENT_TYPE,
359                            MIME_HTML);
360   ret = MHD_queue_response (connection, 
361                             MHD_HTTP_OK, 
362                             response);
363   MHD_destroy_response (response);
364   return ret;
365 }
366
367
368 /**
369  * Send the 'SUBMIT_PAGE'.
370  *
371  * @param info information string to send to the user
372  * @param request request information
373  * @param connection connection to use
374  */
375 static int
376 fill_s_reply (const char *info,
377               struct Request *request,
378               struct MHD_Connection *connection)
379 {
380   int ret;
381   char *reply;
382   struct MHD_Response *response;
383
384   GNUNET_asprintf (&reply,
385                    SUBMIT_PAGE,
386                    info,
387                    info);
388   /* return static form */
389   response = MHD_create_response_from_buffer (strlen (reply),
390                                               (void *) reply,
391                                               MHD_RESPMEM_MUST_FREE);
392   MHD_add_response_header (response,
393                            MHD_HTTP_HEADER_CONTENT_TYPE,
394                            MIME_HTML);
395   ret = MHD_queue_response (connection, 
396                             MHD_HTTP_OK, 
397                             response);
398   MHD_destroy_response (response);
399   return ret;
400 }
401
402
403 /**
404  * Iterator over key-value pairs where the value
405  * maybe made available in increments and/or may
406  * not be zero-terminated.  Used for processing
407  * POST data.
408  *
409  * @param cls user-specified closure
410  * @param kind type of the value
411  * @param key 0-terminated key for the value
412  * @param filename name of the uploaded file, NULL if not known
413  * @param content_type mime-type of the data, NULL if not known
414  * @param transfer_encoding encoding of the data, NULL if not known
415  * @param data pointer to size bytes of data at the
416  *              specified offset
417  * @param off offset of data in the overall value
418  * @param size number of bytes in data available
419  * @return MHD_YES to continue iterating,
420  *         MHD_NO to abort the iteration
421  */
422 static int
423 post_iterator (void *cls,
424                enum MHD_ValueKind kind,
425                const char *key,
426                const char *filename,
427                const char *content_type,
428                const char *transfer_encoding,
429                const char *data, uint64_t off, size_t size)
430 {
431   struct Request *request = cls;
432
433   if (0 == strcmp ("domain", key))
434     {
435       if (size + off >= sizeof(request->domain_name))
436         size = sizeof (request->domain_name) - off - 1;
437       memcpy (&request->domain_name[off],
438               data,
439               size);
440       request->domain_name[size+off] = '\0';
441       return MHD_YES;
442     }
443   if (0 == strcmp ("pkey", key))
444     {
445       if (size + off >= sizeof(request->public_key))
446         size = sizeof (request->public_key) - off - 1;
447       memcpy (&request->public_key[off],
448               data,
449               size);
450       request->public_key[size+off] = '\0';
451       return MHD_YES;
452     }
453   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
454               _("Unsupported form value `%s'\n"),
455               key);
456   return MHD_YES;
457 }
458
459
460
461
462 /**
463  * Continuation called to notify client about result of the
464  * operation.
465  *
466  * @param cls closure
467  * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
468  *                GNUNET_NO if content was already there
469  *                GNUNET_YES (or other positive value) on success
470  * @param emsg NULL on success, otherwise an error message
471  */
472 static void 
473 put_continuation (void *cls,
474                   int32_t success,
475                   const char *emsg)
476 {
477   struct Request *request = cls;
478
479   request->qe = NULL;
480   if (0 >= success)
481   {
482     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
483                 _("Failed to create record for domain `%s': %s\n"),
484                 request->domain_name,
485                 emsg);
486     request->phase = RP_FAIL;
487   }
488   else
489     request->phase = RP_SUCCESS;
490   run_httpd_now ();
491 }
492
493
494 /**
495  * Test if a name mapping was found, if so, refuse.  If not, initiate storing of the record.
496  *
497  * @param cls closure
498  * @param zone_key public key of the zone
499  * @param name name that is being mapped (at most 255 characters long)
500  * @param rd_count number of entries in 'rd' array
501  * @param rd array of records with data to store
502  */
503 static void 
504 zone_to_name_cb (void *cls,
505                  const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
506                  const char *name,
507                  unsigned int rd_count,
508                  const struct GNUNET_NAMESTORE_RecordData *rd)
509 {
510   struct Request *request = cls;
511   struct GNUNET_NAMESTORE_RecordData r;
512   struct GNUNET_CRYPTO_ShortHashCode pub;
513   
514   request->qe = NULL;
515   if (NULL != name)
516   {
517     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
518                 _("Found existing name `%s' for the given key\n"),
519                 name);
520     request->phase = RP_FAIL;
521     run_httpd_now ();
522     return;
523   }
524   r.data = &pub;
525   r.data_size = sizeof (pub);
526   r.expiration_time = UINT64_MAX;
527   r.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
528   r.flags = GNUNET_NAMESTORE_RF_AUTHORITY;
529   request->qe = GNUNET_NAMESTORE_records_store (ns,
530                                                 &fcfs_zone_pkey,
531                                                 request->domain_name,
532                                                 1, &r,
533                                                 &put_continuation,
534                                                 request);
535 }
536
537
538 /**
539  * Process a record that was stored in the namestore.  Used to check if
540  * the requested name already exists in the namestore.  If not,
541  * proceed to check if the requested key already exists.
542  *
543  * @param cls closure
544  * @param rd_count number of entries in 'rd' array
545  * @param rd array of records with data to store
546  */
547 static void 
548 lookup_result_processor (void *cls,
549                          unsigned int rd_count,
550                          const struct GNUNET_NAMESTORE_RecordData *rd)
551 {
552   struct Request *request = cls;
553   struct GNUNET_CRYPTO_EccPublicKey pub;
554   
555   if (0 != rd_count)
556   {
557     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
558                 _("Found %u existing records for domain `%s'\n"),
559                 rd_count,
560                 request->domain_name);
561     request->phase = RP_FAIL;
562     run_httpd_now ();
563     return;
564   }
565   if (GNUNET_OK !=
566       GNUNET_CRYPTO_ecc_public_key_from_string (request->public_key,
567                                                 strlen (request->public_key),
568                                                 &pub))
569   {
570     GNUNET_break (0);
571     request->phase = RP_FAIL;
572     run_httpd_now ();
573     return;
574   }
575   request->qe = GNUNET_NAMESTORE_zone_to_name (ns,
576                                                &fcfs_zone_pkey,
577                                                &pub,
578                                                &zone_to_name_cb,
579                                                request);
580 }
581
582
583 /**
584  * We got a block back from the namestore.  Decrypt it
585  * and continue to process the result.
586  *
587  * @param cls the 'struct Request' we are processing
588  * @param block block returned form namestore, NULL on error
589  */
590 static void
591 lookup_block_processor (void *cls,
592                         const struct GNUNET_NAMESTORE_Block *block)
593 {
594   struct Request *request = cls;
595   struct GNUNET_CRYPTO_EccPublicKey pub;
596
597   request->qe = NULL;
598   if (NULL == block)
599   {
600     lookup_result_processor (request, 0, NULL);
601     return;
602   }
603   GNUNET_CRYPTO_ecc_key_get_public (&fcfs_zone_pkey,
604                                     &pub);
605   if (GNUNET_OK != 
606       GNUNET_NAMESTORE_block_decrypt (block,
607                                       &pub,
608                                       request->domain_name,
609                                       &lookup_result_processor,
610                                       request))
611   {
612     GNUNET_break (0);
613     request->phase = RP_FAIL;
614     run_httpd_now ();
615     return;
616   }
617 }
618
619
620 /**
621  * Main MHD callback for handling requests.
622  *
623  * @param cls unused
624  * @param connection MHD connection handle
625  * @param url the requested url
626  * @param method the HTTP method used ("GET", "PUT", etc.)
627  * @param version the HTTP version string (i.e. "HTTP/1.1")
628  * @param upload_data the data being uploaded (excluding HEADERS,
629  *        for a POST that fits into memory and that is encoded
630  *        with a supported encoding, the POST data will NOT be
631  *        given in upload_data and is instead available as
632  *        part of MHD_get_connection_values; very large POST
633  *        data *will* be made available incrementally in
634  *        upload_data)
635  * @param upload_data_size set initially to the size of the
636  *        upload_data provided; the method must update this
637  *        value to the number of bytes NOT processed;
638  * @param ptr pointer to location where we store the 'struct Request'
639  * @return MHD_YES if the connection was handled successfully,
640  *         MHD_NO if the socket must be closed due to a serious
641  *         error while handling the request
642  */
643 static int
644 create_response (void *cls,
645                  struct MHD_Connection *connection,
646                  const char *url,
647                  const char *method,
648                  const char *version,
649                  const char *upload_data, 
650                  size_t *upload_data_size,
651                  void **ptr)
652 {
653   struct MHD_Response *response;
654   struct Request *request;
655   int ret;
656   struct GNUNET_CRYPTO_EccPublicKey pub;
657   struct GNUNET_HashCode query;
658
659   if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
660        (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
661     {
662       if (0 == strcmp (url, FCFS_ZONEINFO_URL))
663         ret = serve_zoneinfo_page (connection);
664       else
665         ret = serve_main_page (connection);
666       if (ret != MHD_YES)
667         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
668                     _("Failed to create page for `%s'\n"),
669                     url);
670       return ret;
671     }
672   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
673     {   
674       request = *ptr;
675       if (NULL == request)
676       {
677         request = GNUNET_malloc (sizeof (struct Request));
678         *ptr = request;
679         request->pp = MHD_create_post_processor (connection, 1024,
680                                                  &post_iterator, request);
681         if (NULL == request->pp)
682           {
683             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
684                         _("Failed to setup post processor for `%s'\n"),
685                         url);
686             return MHD_NO; /* internal error */
687           }    
688         return MHD_YES;
689       }
690       if (NULL != request->pp)
691       {
692         /* evaluate POST data */
693         MHD_post_process (request->pp,
694                           upload_data,
695                           *upload_data_size);
696         if (0 != *upload_data_size)
697           {
698             *upload_data_size = 0;
699             return MHD_YES;
700           }
701         /* done with POST data, serve response */
702         MHD_destroy_post_processor (request->pp);
703         request->pp = NULL;
704       }
705       if (GNUNET_OK !=
706           GNUNET_CRYPTO_ecc_public_key_from_string (request->public_key,
707                                                     strlen (request->public_key),
708                                                     &pub))
709       {
710         /* parse error */
711         return fill_s_reply ("Failed to parse given public key",
712                              request, connection);
713       }
714       switch (request->phase)
715         {
716         case RP_START:
717           if (NULL != strchr (request->domain_name, (int) '.'))
718           {
719             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
720                         _("Domain name must not contain `.'\n"));
721             request->phase = RP_FAIL;
722             return fill_s_reply ("Domain name must not contain `.', sorry.",
723                                  request, connection);
724           }
725           if (NULL != strchr (request->domain_name, (int) '+'))
726           {
727             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
728                         _("Domain name must not contain `+'\n"));
729             request->phase = RP_FAIL;
730             return fill_s_reply ("Domain name must not contain `+', sorry.",
731                                  request, connection);
732           }
733           request->phase = RP_LOOKUP;
734           GNUNET_CRYPTO_ecc_key_get_public (&fcfs_zone_pkey,
735                                             &pub);
736           GNUNET_NAMESTORE_query_from_public_key (&pub,
737                                                   request->domain_name,
738                                                   &query);
739           request->qe = GNUNET_NAMESTORE_lookup_block (ns,
740                                                        &query,
741                                                        &lookup_block_processor,
742                                                        request);
743           break;
744         case RP_LOOKUP:
745           break;
746         case RP_PUT:
747           break;
748         case RP_FAIL:
749           return fill_s_reply ("Request failed, sorry.",
750                                request, connection);
751         case RP_SUCCESS:
752           return fill_s_reply ("Success.",
753                                request, connection);
754         default:
755           GNUNET_break (0);
756           return MHD_NO;
757         }
758         return MHD_YES; /* will have a reply later... */    
759     }
760   /* unsupported HTTP method */
761   response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
762                                               (void *) METHOD_ERROR,
763                                               MHD_RESPMEM_PERSISTENT);
764   ret = MHD_queue_response (connection, 
765                             MHD_HTTP_METHOD_NOT_ACCEPTABLE, 
766                             response);
767   MHD_destroy_response (response);
768   return ret;
769 }
770
771
772 /**
773  * Callback called upon completion of a request.
774  * Decrements session reference counter.
775  *
776  * @param cls not used
777  * @param connection connection that completed
778  * @param con_cls session handle
779  * @param toe status code
780  */
781 static void
782 request_completed_callback (void *cls,
783                             struct MHD_Connection *connection,
784                             void **con_cls,
785                             enum MHD_RequestTerminationCode toe)
786 {
787   struct Request *request = *con_cls;
788
789   if (NULL == request)
790     return;
791   if (NULL != request->pp)
792     MHD_destroy_post_processor (request->pp);
793   if (NULL != request->qe)
794     GNUNET_NAMESTORE_cancel (request->qe);
795   GNUNET_free (request);
796 }
797
798
799 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
800
801
802 /**
803  * Schedule tasks to run MHD server.
804  */
805 static void
806 run_httpd ()
807 {
808   fd_set rs;
809   fd_set ws;
810   fd_set es;
811   struct GNUNET_NETWORK_FDSet *wrs;
812   struct GNUNET_NETWORK_FDSet *wws;
813   struct GNUNET_NETWORK_FDSet *wes;
814   int max;
815   int haveto;
816   UNSIGNED_MHD_LONG_LONG timeout;
817   struct GNUNET_TIME_Relative tv;
818
819   FD_ZERO (&rs);
820   FD_ZERO (&ws);
821   FD_ZERO (&es);
822   wrs = GNUNET_NETWORK_fdset_create ();
823   wes = GNUNET_NETWORK_fdset_create ();
824   wws = GNUNET_NETWORK_fdset_create ();
825   max = -1;
826   GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
827   haveto = MHD_get_timeout (httpd, &timeout);
828   if (haveto == MHD_YES)
829     tv.rel_value_us = (uint64_t) timeout * 1000LL;
830   else
831     tv = GNUNET_TIME_UNIT_FOREVER_REL;
832   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
833   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
834   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
835   httpd_task =
836       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
837                                    tv, wrs, wws,
838                                    &do_httpd, NULL);
839   GNUNET_NETWORK_fdset_destroy (wrs);
840   GNUNET_NETWORK_fdset_destroy (wws);
841   GNUNET_NETWORK_fdset_destroy (wes);
842 }
843
844
845 /**
846  * Task run whenever HTTP server operations are pending.
847  *
848  * @param cls unused
849  * @param tc scheduler context
850  */
851 static void
852 do_httpd (void *cls,
853           const struct GNUNET_SCHEDULER_TaskContext *tc)
854 {
855   httpd_task = GNUNET_SCHEDULER_NO_TASK;
856   MHD_run (httpd);
857   run_httpd ();
858 }
859
860
861 /**
862  * Task run on shutdown.  Cleans up everything.
863  *
864  * @param cls unused
865  * @param tc scheduler context
866  */
867 static void
868 do_shutdown (void *cls,
869              const struct GNUNET_SCHEDULER_TaskContext *tc)
870 {
871   if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
872   {
873     GNUNET_SCHEDULER_cancel (httpd_task);
874     httpd_task = GNUNET_SCHEDULER_NO_TASK;
875   }
876   if (NULL != ns)
877   {
878     GNUNET_NAMESTORE_disconnect (ns);
879     ns = NULL;
880   }
881   if (NULL != httpd)
882   {
883     MHD_stop_daemon (httpd);
884     httpd = NULL;
885   }
886   if (NULL != id_op)
887   {
888     GNUNET_IDENTITY_cancel (id_op);
889     id_op = NULL;
890   }
891   if (NULL != identity)
892   {
893     GNUNET_IDENTITY_disconnect (identity);
894     identity = NULL;
895   }
896 }
897
898
899 /** 
900  * Method called to inform about the egos of this peer.
901  *
902  * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
903  * this function is only called ONCE, and 'NULL' being passed in
904  * @a ego does indicate an error (i.e. name is taken or no default
905  * value is known).  If @a ego is non-NULL and if '*ctx'
906  * is set in those callbacks, the value WILL be passed to a subsequent
907  * call to the identity callback of #GNUNET_IDENTITY_connect (if 
908  * that one was not NULL).
909  *
910  * @param cls closure, NULL
911  * @param ego ego handle
912  * @param ctx context for application to store data for this ego
913  *                 (during the lifetime of this process, initially NULL)
914  * @param name name assigned by the user for this ego,
915  *                   NULL if the user just deleted the ego and it
916  *                   must thus no longer be used
917  */
918 static void
919 identity_cb (void *cls,
920              struct GNUNET_IDENTITY_Ego *ego,
921              void **ctx,
922              const char *name)
923 {
924   id_op = NULL;
925   if (NULL == ego)
926   {
927     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
928                 _("No ego configured for `fcfsd` subsystem\n"));
929     return;
930   }
931   fcfs_zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
932   httpd = MHD_start_daemon (MHD_USE_DUAL_STACK | MHD_USE_DEBUG,
933                             (uint16_t) port,
934                             NULL, NULL, 
935                             &create_response, NULL, 
936                             MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
937                             MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
938                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
939                             MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4 * 1024), 
940                             MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
941                             MHD_OPTION_END);
942   if (NULL == httpd)
943   {
944     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
945                 _("Failed to start HTTP server\n"));
946     GNUNET_SCHEDULER_shutdown ();
947     return;
948   }
949   run_httpd ();
950 }
951
952
953 /**
954  * Main function that will be run.
955  *
956  * @param cls closure
957  * @param args remaining command-line arguments
958  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
959  * @param cfg configuration
960  */
961 static void
962 run (void *cls, char *const *args, const char *cfgfile,
963      const struct GNUNET_CONFIGURATION_Handle *cfg)
964 {
965   if (GNUNET_OK !=
966       GNUNET_CONFIGURATION_get_value_number (cfg,
967                                              "fcfsd",
968                                              "HTTPPORT",
969                                              &port))
970   {
971     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
972                                "fcfsd", "HTTPPORT");
973     return;
974   }
975   ns = GNUNET_NAMESTORE_connect (cfg);
976   if (NULL == ns)
977     {
978       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
979                   _("Failed to connect to namestore\n"));
980       return;
981     }
982   identity = GNUNET_IDENTITY_connect (cfg,
983                                       NULL, NULL);
984   id_op = GNUNET_IDENTITY_get (identity, "fcfsd",
985                                &identity_cb, NULL);
986   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
987                                 &do_shutdown, NULL);
988 }
989
990
991 /**
992  * The main function for the fcfs daemon.
993  *
994  * @param argc number of arguments from the command line
995  * @param argv command line arguments
996  * @return 0 ok, 1 on error
997  */
998 int
999 main (int argc, char *const *argv)
1000 {
1001   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1002     GNUNET_GETOPT_OPTION_END
1003   };
1004
1005   int ret;
1006
1007   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1008     return 2;
1009
1010   GNUNET_log_setup ("fcfsd", "WARNING", NULL);
1011   ret =
1012       (GNUNET_OK ==
1013        GNUNET_PROGRAM_run (argc, argv, "fcfsd",
1014                            _("GNU Name System First Come First Serve name registration service"), 
1015                            options,
1016                            &run, NULL)) ? 0 : 1;
1017   GNUNET_free ((void*) argv);
1018   return ret;
1019 }
1020
1021 /* end of gnunet-namestore-fcfsd.c */