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