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