fixing multiple segfaults
[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   fprintf (stderr, "PUB %s\n", GNUNET_CRYPTO_ecdsa_public_key_to_string(&request->pub));
541   r.data = &request->pub;
542   r.data_size = sizeof (request->pub);
543   r.expiration_time = UINT64_MAX;
544   r.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
545   r.flags = GNUNET_GNSRECORD_RF_NONE;
546   request->qe = GNUNET_NAMESTORE_records_store (ns,
547                                                 &fcfs_zone_pkey,
548                                                 request->domain_name,
549                                                 1, &r,
550                                                 &put_continuation,
551                                                 request);
552 }
553
554
555 /**
556  * We got a block back from the namestore.  Decrypt it
557  * and continue to process the result.
558  *
559  * @param cls the 'struct Request' we are processing
560  * @param zone private key of the zone; NULL on disconnect
561  * @param label label of the records; NULL on disconnect
562  * @param rd_count number of entries in @a rd array, 0 if label was deleted
563  * @param rd array of records with data to store
564  */
565 static void
566 lookup_block_processor (void *cls,
567                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
568                         const char *label,
569                         unsigned int rd_count,
570                         const struct GNUNET_GNSRECORD_Data *rd)
571 {
572   struct Request *request = cls;
573
574   request->qe = NULL;
575   if (0 == rd_count)
576   {
577
578     if (GNUNET_OK !=
579         GNUNET_CRYPTO_ecdsa_public_key_from_string (request->public_key,
580                                                     strlen (request->public_key),
581                                                     &request->pub))
582     {
583       GNUNET_break (0);
584       request->phase = RP_FAIL;
585       run_httpd_now ();
586       return;
587     }
588     fprintf (stderr, "PUB1 %s\n", GNUNET_CRYPTO_ecdsa_public_key_to_string(&request->pub));
589     request->qe = GNUNET_NAMESTORE_zone_to_name (ns,
590                                                  &fcfs_zone_pkey,
591                                                  &request->pub,
592                                                  &zone_to_name_cb,
593                                                  request);
594     return;
595   }
596   GNUNET_break (0 != rd_count);
597   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
598               _("Found %u existing records for domain `%s'\n"),
599               rd_count,
600               request->domain_name);
601   request->phase = RP_FAIL;
602   run_httpd_now ();
603   return;
604 }
605
606
607 /**
608  * Main MHD callback for handling requests.
609  *
610  * @param cls unused
611  * @param connection MHD connection handle
612  * @param url the requested url
613  * @param method the HTTP method used ("GET", "PUT", etc.)
614  * @param version the HTTP version string (i.e. "HTTP/1.1")
615  * @param upload_data the data being uploaded (excluding HEADERS,
616  *        for a POST that fits into memory and that is encoded
617  *        with a supported encoding, the POST data will NOT be
618  *        given in upload_data and is instead available as
619  *        part of MHD_get_connection_values; very large POST
620  *        data *will* be made available incrementally in
621  *        upload_data)
622  * @param upload_data_size set initially to the size of the
623  *        @a upload_data provided; the method must update this
624  *        value to the number of bytes NOT processed;
625  * @param ptr pointer to location where we store the 'struct Request'
626  * @return MHD_YES if the connection was handled successfully,
627  *         MHD_NO if the socket must be closed due to a serious
628  *         error while handling the request
629  */
630 static int
631 create_response (void *cls,
632                  struct MHD_Connection *connection,
633                  const char *url,
634                  const char *method,
635                  const char *version,
636                  const char *upload_data,
637                  size_t *upload_data_size,
638                  void **ptr)
639 {
640   struct MHD_Response *response;
641   struct Request *request;
642   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
643   int ret;
644
645   if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
646        (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
647     {
648       if (0 == strcmp (url, FCFS_ZONEINFO_URL))
649         ret = serve_zoneinfo_page (connection);
650       else
651         ret = serve_main_page (connection);
652       if (ret != MHD_YES)
653         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
654                     _("Failed to create page for `%s'\n"),
655                     url);
656       return ret;
657     }
658   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
659     {
660       request = *ptr;
661       if (NULL == request)
662       {
663         request = GNUNET_malloc (sizeof (struct Request));
664         *ptr = request;
665         request->pp = MHD_create_post_processor (connection, 1024,
666                                                  &post_iterator, request);
667         if (NULL == request->pp)
668           {
669             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
670                         _("Failed to setup post processor for `%s'\n"),
671                         url);
672             return MHD_NO; /* internal error */
673           }
674         return MHD_YES;
675       }
676       if (NULL != request->pp)
677       {
678         /* evaluate POST data */
679         MHD_post_process (request->pp,
680                           upload_data,
681                           *upload_data_size);
682         if (0 != *upload_data_size)
683           {
684             *upload_data_size = 0;
685             return MHD_YES;
686           }
687         /* done with POST data, serve response */
688         MHD_destroy_post_processor (request->pp);
689         request->pp = NULL;
690       }
691       if (GNUNET_OK !=
692           GNUNET_CRYPTO_ecdsa_public_key_from_string (request->public_key,
693                                                       strlen (request->public_key),
694                                                       &pub))
695       {
696         /* parse error */
697         return fill_s_reply ("Failed to parse given public key",
698                              request, connection);
699       }
700       switch (request->phase)
701         {
702         case RP_START:
703           if (NULL != strchr (request->domain_name, (int) '.'))
704           {
705             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
706                         _("Domain name must not contain `.'\n"));
707             request->phase = RP_FAIL;
708             return fill_s_reply ("Domain name must not contain `.', sorry.",
709                                  request, connection);
710           }
711           if (NULL != strchr (request->domain_name, (int) '+'))
712           {
713             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
714                         _("Domain name must not contain `+'\n"));
715             request->phase = RP_FAIL;
716             return fill_s_reply ("Domain name must not contain `+', sorry.",
717                                  request, connection);
718           }
719           request->phase = RP_LOOKUP;
720           request->qe = GNUNET_NAMESTORE_records_lookup (ns,
721                                                        &fcfs_zone_pkey,
722                                                        request->domain_name,
723                                                        &lookup_block_processor,
724                                                        request);
725           break;
726         case RP_LOOKUP:
727           break;
728         case RP_PUT:
729           break;
730         case RP_FAIL:
731           return fill_s_reply ("Request failed, sorry.",
732                                request, connection);
733         case RP_SUCCESS:
734           return fill_s_reply ("Success.",
735                                request, connection);
736         default:
737           GNUNET_break (0);
738           return MHD_NO;
739         }
740         return MHD_YES; /* will have a reply later... */
741     }
742   /* unsupported HTTP method */
743   response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
744                                               (void *) METHOD_ERROR,
745                                               MHD_RESPMEM_PERSISTENT);
746   ret = MHD_queue_response (connection,
747                             MHD_HTTP_METHOD_NOT_ACCEPTABLE,
748                             response);
749   MHD_destroy_response (response);
750   return ret;
751 }
752
753
754 /**
755  * Callback called upon completion of a request.
756  * Decrements session reference counter.
757  *
758  * @param cls not used
759  * @param connection connection that completed
760  * @param con_cls session handle
761  * @param toe status code
762  */
763 static void
764 request_completed_callback (void *cls,
765                             struct MHD_Connection *connection,
766                             void **con_cls,
767                             enum MHD_RequestTerminationCode toe)
768 {
769   struct Request *request = *con_cls;
770
771   if (NULL == request)
772     return;
773   if (NULL != request->pp)
774     MHD_destroy_post_processor (request->pp);
775   if (NULL != request->qe)
776     GNUNET_NAMESTORE_cancel (request->qe);
777   GNUNET_free (request);
778 }
779
780
781 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
782
783
784 /**
785  * Schedule tasks to run MHD server.
786  */
787 static void
788 run_httpd ()
789 {
790   fd_set rs;
791   fd_set ws;
792   fd_set es;
793   struct GNUNET_NETWORK_FDSet *wrs;
794   struct GNUNET_NETWORK_FDSet *wws;
795   struct GNUNET_NETWORK_FDSet *wes;
796   int max;
797   int haveto;
798   UNSIGNED_MHD_LONG_LONG timeout;
799   struct GNUNET_TIME_Relative tv;
800
801   FD_ZERO (&rs);
802   FD_ZERO (&ws);
803   FD_ZERO (&es);
804   wrs = GNUNET_NETWORK_fdset_create ();
805   wes = GNUNET_NETWORK_fdset_create ();
806   wws = GNUNET_NETWORK_fdset_create ();
807   max = -1;
808   GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
809   haveto = MHD_get_timeout (httpd, &timeout);
810   if (haveto == MHD_YES)
811     tv.rel_value_us = (uint64_t) timeout * 1000LL;
812   else
813     tv = GNUNET_TIME_UNIT_FOREVER_REL;
814   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
815   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
816   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
817   httpd_task =
818       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
819                                    tv, wrs, wws,
820                                    &do_httpd, NULL);
821   GNUNET_NETWORK_fdset_destroy (wrs);
822   GNUNET_NETWORK_fdset_destroy (wws);
823   GNUNET_NETWORK_fdset_destroy (wes);
824 }
825
826
827 /**
828  * Task run whenever HTTP server operations are pending.
829  *
830  * @param cls unused
831  * @param tc scheduler context
832  */
833 static void
834 do_httpd (void *cls,
835           const struct GNUNET_SCHEDULER_TaskContext *tc)
836 {
837   httpd_task = GNUNET_SCHEDULER_NO_TASK;
838   MHD_run (httpd);
839   run_httpd ();
840 }
841
842
843 /**
844  * Task run on shutdown.  Cleans up everything.
845  *
846  * @param cls unused
847  * @param tc scheduler context
848  */
849 static void
850 do_shutdown (void *cls,
851              const struct GNUNET_SCHEDULER_TaskContext *tc)
852 {
853   if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
854   {
855     GNUNET_SCHEDULER_cancel (httpd_task);
856     httpd_task = GNUNET_SCHEDULER_NO_TASK;
857   }
858   if (NULL != ns)
859   {
860     GNUNET_NAMESTORE_disconnect (ns);
861     ns = NULL;
862   }
863   if (NULL != httpd)
864   {
865     MHD_stop_daemon (httpd);
866     httpd = NULL;
867   }
868   if (NULL != id_op)
869   {
870     GNUNET_IDENTITY_cancel (id_op);
871     id_op = NULL;
872   }
873   if (NULL != identity)
874   {
875     GNUNET_IDENTITY_disconnect (identity);
876     identity = NULL;
877   }
878 }
879
880
881 /**
882  * Method called to inform about the egos of this peer.
883  *
884  * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
885  * this function is only called ONCE, and 'NULL' being passed in
886  * @a ego does indicate an error (i.e. name is taken or no default
887  * value is known).  If @a ego is non-NULL and if '*ctx'
888  * is set in those callbacks, the value WILL be passed to a subsequent
889  * call to the identity callback of #GNUNET_IDENTITY_connect (if
890  * that one was not NULL).
891  *
892  * @param cls closure, NULL
893  * @param ego ego handle
894  * @param ctx context for application to store data for this ego
895  *                 (during the lifetime of this process, initially NULL)
896  * @param name name assigned by the user for this ego,
897  *                   NULL if the user just deleted the ego and it
898  *                   must thus no longer be used
899  */
900 static void
901 identity_cb (void *cls,
902              struct GNUNET_IDENTITY_Ego *ego,
903              void **ctx,
904              const char *name)
905 {
906   int options;
907
908   id_op = NULL;
909   if (NULL == ego)
910   {
911     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
912                 _("No ego configured for `fcfsd` subsystem\n"));
913     return;
914   }
915   fcfs_zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
916
917   options = MHD_USE_DUAL_STACK | MHD_USE_DEBUG;
918   do
919     {
920       httpd = MHD_start_daemon (options,
921                                 (uint16_t) port,
922                                 NULL, NULL,
923                                 &create_response, NULL,
924                                 MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
925                                 MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
926                                 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
927                                 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4 * 1024),
928                                 MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
929                                 MHD_OPTION_END);
930       if (MHD_USE_DEBUG == options)
931         break;
932       options = MHD_USE_DEBUG;
933     }
934   while (NULL == httpd);
935   if (NULL == httpd)
936   {
937     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
938                 _("Failed to start HTTP server\n"));
939     GNUNET_SCHEDULER_shutdown ();
940     return;
941   }
942   run_httpd ();
943 }
944
945
946 /**
947  * Main function that will be run.
948  *
949  * @param cls closure
950  * @param args remaining command-line arguments
951  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
952  * @param cfg configuration
953  */
954 static void
955 run (void *cls, char *const *args, const char *cfgfile,
956      const struct GNUNET_CONFIGURATION_Handle *cfg)
957 {
958   if (GNUNET_OK !=
959       GNUNET_CONFIGURATION_get_value_number (cfg,
960                                              "fcfsd",
961                                              "HTTPPORT",
962                                              &port))
963   {
964     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
965                                "fcfsd", "HTTPPORT");
966     return;
967   }
968   ns = GNUNET_NAMESTORE_connect (cfg);
969   if (NULL == ns)
970     {
971       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
972                   _("Failed to connect to namestore\n"));
973       return;
974     }
975   identity = GNUNET_IDENTITY_connect (cfg,
976                                       NULL, NULL);
977   if (NULL == identity)
978   {
979     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
980                 _("Failed to connect to identity\n"));
981     return;
982   }
983   id_op = GNUNET_IDENTITY_get (identity, "fcfsd",
984                                &identity_cb, NULL);
985   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
986                                 &do_shutdown, NULL);
987 }
988
989
990 /**
991  * The main function for the fcfs daemon.
992  *
993  * @param argc number of arguments from the command line
994  * @param argv command line arguments
995  * @return 0 ok, 1 on error
996  */
997 int
998 main (int argc, char *const *argv)
999 {
1000   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1001     GNUNET_GETOPT_OPTION_END
1002   };
1003
1004   int ret;
1005
1006   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1007     return 2;
1008
1009   GNUNET_log_setup ("fcfsd", "WARNING", NULL);
1010   ret =
1011       (GNUNET_OK ==
1012        GNUNET_PROGRAM_run (argc, argv, "fcfsd",
1013                            _("GNU Name System First Come First Serve name registration service"),
1014                            options,
1015                            &run, NULL)) ? 0 : 1;
1016   GNUNET_free ((void*) argv);
1017   GNUNET_CRYPTO_ecdsa_key_clear (&fcfs_zone_pkey);
1018   return ret;
1019 }
1020
1021 /* end of gnunet-namestore-fcfsd.c */