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