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