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