(no commit message)
[oweals/gnunet.git] / src / transport / plugin_transport_http.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 2, 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 /**
22  * @file transport/plugin_transport_http.c
23  * @brief http transport service plugin
24  * @author Matthias Wachs
25  */
26
27 #include "platform.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_connection_lib.h"
31 #include "gnunet_server_lib.h"
32 #include "gnunet_service_lib.h"
33 #include "gnunet_statistics_service.h"
34 #include "gnunet_transport_service.h"
35 #include "gnunet_resolver_service.h"
36 #include "gnunet_server_lib.h"
37 #include "gnunet_container_lib.h"
38 #include "plugin_transport.h"
39 #include "gnunet_os_lib.h"
40 #include "microhttpd.h"
41 #include <curl/curl.h>
42
43
44 #define DEBUG_CURL GNUNET_NO
45 #define DEBUG_HTTP GNUNET_NO
46
47 /**
48  * Text of the response sent back after the last bytes of a PUT
49  * request have been received (just to formally obey the HTTP
50  * protocol).
51  */
52 #define HTTP_PUT_RESPONSE "Thank you!"
53
54 /**
55  * After how long do we expire an address that we
56  * learned from another peer if it is not reconfirmed
57  * by anyone?
58  */
59 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
60
61 /**
62  * Page returned if request invalid
63  */
64 #define HTTP_ERROR_RESPONSE "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"><HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H1>Not Found</H1>The requested URL was not found on this server.<P><HR><ADDRESS></ADDRESS></BODY></HTML>"
65
66 /**
67  * Timeout for a http connect
68  */
69 #define HTTP_CONNECT_TIMEOUT 30
70
71 /**
72  * Network format for IPv4 addresses.
73  */
74 struct IPv4HttpAddress
75 {
76   /**
77    * IPv4 address, in network byte order.
78    */
79   uint32_t ipv4_addr;
80
81   /**
82    * Port number, in network byte order.
83    */
84   uint16_t u_port;
85
86 };
87
88
89 /**
90  * Network format for IPv6 addresses.
91  */
92 struct IPv6HttpAddress
93 {
94   /**
95    * IPv6 address.
96    */
97   struct in6_addr ipv6_addr;
98
99   /**
100    * Port number, in network byte order.
101    */
102   uint16_t u6_port;
103
104 };
105
106 struct HTTP_inbound_transmission
107 {
108   /**
109    * bytes received
110    */
111   size_t bytes_recv;
112 };
113
114 /**
115  *  Message to send using http
116  */
117 struct HTTP_Message
118 {
119   /**
120    * next pointer for double linked list
121    */
122   struct HTTP_Message * next;
123
124   /**
125    * previous pointer for double linked list
126    */
127   struct HTTP_Message * prev;
128
129   /**
130    * buffer containing data to send
131    */
132   char *buf;
133
134   /**
135    * amount of data already sent
136    */
137   size_t pos;
138
139   /**
140    * buffer length
141    */
142   size_t size;
143   
144   char * dest_url;
145
146   /**
147    * Continuation function to call once the transmission buffer
148    * has again space available.  NULL if there is no
149    * continuation to call.
150    */
151   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
152
153   /**
154    * Closure for transmit_cont.
155    */
156   void *transmit_cont_cls;
157 };
158
159
160 /**
161  * Session handle for connections.
162  */
163 struct Session
164 {
165
166   /**
167    * Stored in a linked list.
168    */
169   struct Session *next;
170
171   /**
172    * Pointer to the global plugin struct.
173    */
174   struct Plugin *plugin;
175
176   /**
177    * To whom are we talking to (set to our identity
178    * if we are still waiting for the welcome message)
179    */
180   struct GNUNET_PeerIdentity partner;
181
182   /**
183    * Sender's ip address to distinguish between incoming connections
184    */
185   //struct sockaddr_in * addr_inbound;
186
187   /**
188    * Sender's ip address to distinguish between incoming connections
189    */
190   void * addr_in;
191
192   size_t addr_in_len;
193
194   void * addr_out;
195
196   size_t addr_out_len;
197
198   /**
199    * Did we initiate the connection (GNUNET_YES) or the other peer (GNUNET_NO)?
200    */
201   int is_client;
202
203   /**
204    * At what time did we reset last_received last?
205    */
206   struct GNUNET_TIME_Absolute last_quota_update;
207
208   /**
209    * How many bytes have we received since the "last_quota_update"
210    * timestamp?
211    */
212   uint64_t last_received;
213
214   /**
215    * Number of bytes per ms that this peer is allowed
216    * to send to us.
217    */
218   uint32_t quota;
219
220   /**
221    * Is there a HTTP/PUT in progress?
222    */
223   int is_put_in_progress;
224
225   /**
226    * Is the http request invalid?
227    */
228   int is_bad_request;
229
230   /**
231    * Encoded hash
232    */
233   struct GNUNET_CRYPTO_HashAsciiEncoded hash;
234
235   /**
236    * Head of dll used to store outbound messages
237    */
238   struct HTTP_Message * pending_outbound_msg_head;
239
240   /**
241    * Tail of dll used to store outbound messages
242    */
243   struct HTTP_Message * pending_outbound_msg_tail;
244
245   /**
246    * Incoming message
247    */
248   struct HTTP_inbound_transmission pending_inbound_msg;
249
250   /**
251    * curl handle for outbound transmissions
252    */
253   CURL *curl_handle;
254
255   /**
256    * Message tokenizer for incoming data
257    */
258   struct GNUNET_SERVER_MessageStreamTokenizer * msgtok;
259 };
260
261 /**
262  * Encapsulation of all of the state of the plugin.
263  */
264 struct Plugin
265 {
266   /**
267    * Our environment.
268    */
269   struct GNUNET_TRANSPORT_PluginEnvironment *env;
270
271   unsigned int port_inbound;
272
273   /**
274    * Hashmap for all existing sessions.
275    */
276   struct GNUNET_CONTAINER_MultiHashMap *sessions;
277
278   /**
279    * Daemon for listening for new IPv4 connections.
280    */
281   struct MHD_Daemon *http_server_daemon_v4;
282
283   /**
284    * Daemon for listening for new IPv6connections.
285    */
286   struct MHD_Daemon *http_server_daemon_v6;
287
288   /**
289    * Our primary task for http daemon handling IPv4 connections
290    */
291   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v4;
292
293   /**
294    * Our primary task for http daemon handling IPv6 connections
295    */
296   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v6;
297
298   /**
299    * The task sending data
300    */
301   GNUNET_SCHEDULER_TaskIdentifier http_server_task_send;
302
303   /**
304    * cURL Multihandle
305    */
306   CURLM * multi_handle;
307
308   /**
309    * Our ASCII encoded, hashed peer identity
310    * This string is used to distinguish between connections and is added to the urls
311    */
312   struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident;
313 };
314
315 /**
316  * Create a new session
317  *
318  * @param addr_in address the peer is using inbound
319  * @param addr_out address the peer is using outbound
320  * @param peer identity
321  * @return created session object
322  */
323 static struct Session * create_session (void * cls, char * addr_in, size_t addrlen_in, char * addr_out, size_t addrlen_out, const struct GNUNET_PeerIdentity *peer)
324 {
325   struct Plugin *plugin = cls;
326   struct Session * ses = GNUNET_malloc ( sizeof( struct Session) );
327
328   GNUNET_assert(cls !=NULL);
329   if (addrlen_in != 0)
330   {
331     ses->addr_in = GNUNET_malloc (addrlen_in);
332     ses->addr_in_len = addrlen_in;
333     memcpy(ses->addr_in,addr_in,addrlen_in);
334   }
335
336   if (addrlen_out != 0)
337   {
338     ses->addr_out = GNUNET_malloc (addrlen_out);
339     ses->addr_out_len = addrlen_out;
340     memcpy(ses->addr_out,addr_out,addrlen_out);
341   }
342   ses->plugin = plugin;
343   memcpy(&ses->partner, peer, sizeof (struct GNUNET_PeerIdentity));
344   GNUNET_CRYPTO_hash_to_enc(&ses->partner.hashPubKey,&(ses->hash));
345   ses->pending_inbound_msg.bytes_recv = 0;
346   ses->msgtok = NULL;
347   return ses;
348 }
349
350 /**
351  * Callback called by MHD when a connection is terminated
352  */
353 static void requestCompletedCallback (void *cls, struct MHD_Connection * connection, void **httpSessionCache)
354 {
355   struct Session * cs;
356
357   cs = *httpSessionCache;
358   if (cs == NULL)
359     return;
360     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection from peer `%s' was terminated\n",GNUNET_i2s(&cs->partner));
361     /* session set to inactive */
362     cs->is_put_in_progress = GNUNET_NO;
363     cs->is_bad_request = GNUNET_NO;
364 }
365
366
367 static void messageTokenizerCallback (void *cls,
368                                       void *client,
369                                       const struct GNUNET_MessageHeader *message)
370 {
371   struct Session * cs = cls;
372   GNUNET_assert(cs != NULL);
373
374   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
375               "Received message with type %u and size %u from `%s'\n",
376               ntohs(message->type),
377               ntohs(message->size),
378               GNUNET_i2s(&(cs->partner)));
379   cs->plugin->env->receive(cs->plugin->env->cls,
380                            &cs->partner,
381                            message, 1, NULL,
382                            NULL, 0);
383 }
384
385 /**
386  * Check if ip is allowed to connect.
387  */
388 static int
389 acceptPolicyCallback (void *cls,
390                       const struct sockaddr *addr, socklen_t addr_len)
391 {
392 #if 0
393   struct Plugin *plugin = cls;
394 #endif
395   /* Every connection is accepted, nothing more to do here */
396   return MHD_YES;
397 }
398
399 /**
400  * Process GET or PUT request received via MHD.  For
401  * GET, queue response that will send back our pending
402  * messages.  For PUT, process incoming data and send
403  * to GNUnet core.  In either case, check if a session
404  * already exists and create a new one if not.
405  */
406 static int
407 accessHandlerCallback (void *cls,
408                        struct MHD_Connection *session,
409                        const char *url,
410                        const char *method,
411                        const char *version,
412                        const char *upload_data,
413                        size_t * upload_data_size, void **httpSessionCache)
414 {
415   struct Plugin *plugin = cls;
416   struct MHD_Response *response;
417   struct Session * cs;
418   const union MHD_ConnectionInfo * conn_info;
419   struct sockaddr_in  *addrin;
420   struct sockaddr_in6 *addrin6;
421   char address[INET6_ADDRSTRLEN+14];
422   struct GNUNET_PeerIdentity pi_in;
423   int res = GNUNET_NO;
424   int send_error_to_client;
425   struct IPv4HttpAddress ipv4addr;
426   struct IPv6HttpAddress ipv6addr;
427
428   GNUNET_assert(cls !=NULL);
429   send_error_to_client = GNUNET_NO;
430
431   if ( NULL == *httpSessionCache)
432   {
433     /* check url for peer identity */
434     res = GNUNET_CRYPTO_hash_from_string ( &url[1], &(pi_in.hashPubKey));
435     if ( GNUNET_SYSERR == res )
436     {
437       response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
438       res = MHD_queue_response (session, MHD_HTTP_NOT_FOUND, response);
439       MHD_destroy_response (response);
440       if (res == MHD_YES)
441         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, sent HTTP 1.1/404\n");
442       else
443         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, could not send error\n");
444       return res;
445     }
446
447     conn_info = MHD_get_connection_info(session, MHD_CONNECTION_INFO_CLIENT_ADDRESS );
448     /* Incoming IPv4 connection */
449     if ( AF_INET == conn_info->client_addr->sin_family)
450     {
451       addrin = conn_info->client_addr;
452       inet_ntop(addrin->sin_family, &(addrin->sin_addr),address,INET_ADDRSTRLEN);
453       memcpy(&ipv4addr.ipv4_addr,&(addrin->sin_addr),sizeof(struct in_addr));
454       ipv4addr.u_port = addrin->sin_port;
455     }
456     /* Incoming IPv6 connection */
457     if ( AF_INET6 == conn_info->client_addr->sin_family)
458     {
459       addrin6 = (struct sockaddr_in6 *) conn_info->client_addr;
460       inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr),address,INET6_ADDRSTRLEN);
461       memcpy(&ipv6addr.ipv6_addr,&(addrin6->sin6_addr),sizeof(struct in_addr));
462       ipv6addr.u6_port = addrin6->sin6_port;
463     }
464     /* find existing session for address */
465     cs = GNUNET_CONTAINER_multihashmap_get (plugin->sessions, &pi_in.hashPubKey);
466     /* no existing session, create a new one*/
467     if (cs == NULL )
468     {
469       /* create new session object */
470       if ( AF_INET6 == conn_info->client_addr->sin_family)
471         cs = create_session(plugin, (char *) &ipv6addr, sizeof(struct IPv6HttpAddress),NULL, 0, &pi_in);
472       if ( AF_INET == conn_info->client_addr->sin_family)
473         cs = create_session(plugin, (char *) &ipv4addr, sizeof(struct IPv4HttpAddress),NULL, 0, &pi_in);
474
475       /* Insert session into hashmap */
476       GNUNET_CONTAINER_multihashmap_put ( plugin->sessions,
477                                           &cs->partner.hashPubKey,
478                                           cs,
479                                           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
480
481       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"New Session for peer `%s' inserted\n", GNUNET_i2s(&cs->partner));
482     }
483
484     /* Set closure and update current session*/
485     if (*httpSessionCache == NULL)
486     {
487       *httpSessionCache = cs;
488       /* Updating session */
489       /*
490       memcpy(cs->addr_inbound,conn_info->client_addr, sizeof(struct sockaddr_in));
491       if ( AF_INET == cs->addr_inbound->sin_family)
492       {
493         GNUNET_asprintf(&cs->addr_inbound_str,"%s:%u",address,ntohs(cs->addr_inbound->sin_port));
494       }
495
496       if ( AF_INET6 == cs->addr_inbound->sin_family)
497       {
498         GNUNET_asprintf(&cs->addr_inbound_str,"[%s]:%u",address,ntohs(cs->addr_inbound->sin_port));
499
500       }
501       */
502       if (cs->msgtok==NULL)
503         cs->msgtok = GNUNET_SERVER_mst_create (GNUNET_SERVER_MAX_MESSAGE_SIZE, &messageTokenizerCallback, cs);
504     }
505     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Daemon has new an incoming `%s' request from peer `%s'\n",method, GNUNET_i2s(&cs->partner));
506   }
507   else
508   {
509     cs = *httpSessionCache;
510   }
511   /* Is it a PUT or a GET request */
512   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
513   {
514     /* New  */
515     if ((*upload_data_size == 0) && (cs->is_put_in_progress == GNUNET_NO))
516     {
517       if (cs->pending_inbound_msg.bytes_recv !=0 )
518       {
519         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
520                     _("Incoming message from peer `%s', while existing message with %u bytes was not forwarded to transport'\n"),
521                     GNUNET_i2s(&cs->partner), cs->pending_inbound_msg.bytes_recv);
522         cs->pending_inbound_msg.bytes_recv = 0;
523       }
524       /* not yet ready */
525       cs->is_put_in_progress = GNUNET_YES;
526       cs->is_bad_request = GNUNET_NO;
527       return MHD_YES;
528     }
529
530     if ((*upload_data_size > 0) && (cs->is_bad_request != GNUNET_YES))
531     {
532       if (*upload_data_size + cs->pending_inbound_msg.bytes_recv <= GNUNET_SERVER_MAX_MESSAGE_SIZE)
533       {
534         /* copy uploaded data to buffer */
535
536         res = GNUNET_SERVER_mst_receive(cs->msgtok, cs, upload_data,*upload_data_size, GNUNET_YES, GNUNET_NO);
537         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"%u bytes forwarded to MST: result: %u\n",*upload_data_size, res);
538         cs->pending_inbound_msg.bytes_recv += *upload_data_size;
539         *upload_data_size = 0;
540         return MHD_YES;
541       }
542       else
543       {
544         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"%u bytes not added to message of %u bytes, message to big\n",*upload_data_size, cs->pending_inbound_msg.bytes_recv);
545         cs->is_bad_request = GNUNET_YES;
546         /* (*upload_data_size) bytes not processed */
547         return MHD_YES;
548       }
549     }
550
551     if ((cs->is_put_in_progress == GNUNET_YES) && (cs->is_bad_request == GNUNET_YES))
552     {
553       *upload_data_size = 0;
554       response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
555       res = MHD_queue_response (session, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE, response);
556       if (res == MHD_YES)
557       {
558         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 413 Request Entity Too Large as PUT Response\n");
559         cs->is_bad_request = GNUNET_NO;
560         cs->is_put_in_progress =GNUNET_NO;
561         cs->pending_inbound_msg.bytes_recv = 0;
562       }
563       MHD_destroy_response (response);
564       return MHD_YES;
565     }
566
567     /* Transmission of all data complete */
568     if ((*upload_data_size == 0) && (cs->is_put_in_progress == GNUNET_YES) && (cs->is_bad_request == GNUNET_NO))
569     {
570       send_error_to_client = GNUNET_YES;
571       if (cs->pending_inbound_msg.bytes_recv >= sizeof (struct GNUNET_MessageHeader))
572           send_error_to_client = GNUNET_NO;
573
574       if (send_error_to_client == GNUNET_NO)
575       {
576         response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
577         res = MHD_queue_response (session, MHD_HTTP_OK, response);
578         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 200 OK as PUT Response\n",HTTP_PUT_RESPONSE, strlen (HTTP_PUT_RESPONSE), res );
579         MHD_destroy_response (response);
580       }
581       else
582       {
583         response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
584         res = MHD_queue_response (session, MHD_HTTP_BAD_REQUEST, response);
585         MHD_destroy_response (response);
586         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 400 BAD REQUEST as PUT Response\n");
587       }
588       cs->is_put_in_progress = GNUNET_NO;
589       cs->is_bad_request = GNUNET_NO;
590       cs->pending_inbound_msg.bytes_recv = 0;
591       return res;
592     }
593   }
594   if ( 0 == strcmp (MHD_HTTP_METHOD_GET, method) )
595   {
596     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Got GET Request\n");
597     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"URL: `%s'\n",url);
598     response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
599     res = MHD_queue_response (session, MHD_HTTP_OK, response);
600     MHD_destroy_response (response);
601     return res;
602   }
603   return MHD_NO;
604 }
605
606
607 /**
608  * Call MHD to process pending ipv4 requests and then go back
609  * and schedule the next run.
610  */
611 static void http_server_daemon_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
612 /**
613  * Call MHD to process pending ipv6 requests and then go back
614  * and schedule the next run.
615  */
616 static void http_server_daemon_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
617
618 /**
619  * Function that queries MHD's select sets and
620  * starts the task waiting for them.
621  */
622 static GNUNET_SCHEDULER_TaskIdentifier
623 http_server_daemon_prepare (void * cls, struct MHD_Daemon *daemon_handle)
624 {
625   struct Plugin *plugin = cls;
626   GNUNET_SCHEDULER_TaskIdentifier ret;
627   fd_set rs;
628   fd_set ws;
629   fd_set es;
630   struct GNUNET_NETWORK_FDSet *wrs;
631   struct GNUNET_NETWORK_FDSet *wws;
632   struct GNUNET_NETWORK_FDSet *wes;
633   int max;
634   unsigned long long timeout;
635   int haveto;
636   struct GNUNET_TIME_Relative tv;
637
638   GNUNET_assert(cls !=NULL);
639   ret = GNUNET_SCHEDULER_NO_TASK;
640   FD_ZERO(&rs);
641   FD_ZERO(&ws);
642   FD_ZERO(&es);
643   wrs = GNUNET_NETWORK_fdset_create ();
644   wes = GNUNET_NETWORK_fdset_create ();
645   wws = GNUNET_NETWORK_fdset_create ();
646   max = -1;
647   GNUNET_assert (MHD_YES ==
648                  MHD_get_fdset (daemon_handle,
649                                 &rs,
650                                 &ws,
651                                 &es,
652                                 &max));
653   haveto = MHD_get_timeout (daemon_handle, &timeout);
654   if (haveto == MHD_YES)
655     tv.value = (uint64_t) timeout;
656   else
657     tv = GNUNET_TIME_UNIT_FOREVER_REL;
658   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max);
659   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max);
660   GNUNET_NETWORK_fdset_copy_native (wes, &es, max);
661   if (daemon_handle == plugin->http_server_daemon_v4)
662   {
663     ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
664                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
665                                        GNUNET_SCHEDULER_NO_TASK,
666                                        tv,
667                                        wrs,
668                                        wws,
669                                        &http_server_daemon_v4_run,
670                                        plugin);
671   }
672   if (daemon_handle == plugin->http_server_daemon_v6)
673   {
674     ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
675                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
676                                        GNUNET_SCHEDULER_NO_TASK,
677                                        tv,
678                                        wrs,
679                                        wws,
680                                        &http_server_daemon_v6_run,
681                                        plugin);
682   }
683   GNUNET_NETWORK_fdset_destroy (wrs);
684   GNUNET_NETWORK_fdset_destroy (wws);
685   GNUNET_NETWORK_fdset_destroy (wes);
686   return ret;
687 }
688
689 /**
690  * Call MHD to process pending requests and then go back
691  * and schedule the next run.
692  */
693 static void http_server_daemon_v4_run (void *cls,
694                              const struct GNUNET_SCHEDULER_TaskContext *tc)
695 {
696   struct Plugin *plugin = cls;
697
698   GNUNET_assert(cls !=NULL);
699   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
700     plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
701
702   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
703     return;
704
705   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v4));
706   plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
707   return;
708 }
709
710
711 /**
712  * Call MHD to process pending requests and then go back
713  * and schedule the next run.
714  */
715 static void http_server_daemon_v6_run (void *cls,
716                              const struct GNUNET_SCHEDULER_TaskContext *tc)
717 {
718   struct Plugin *plugin = cls;
719
720   GNUNET_assert(cls !=NULL);
721   if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
722     plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
723
724   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
725     return;
726
727   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v6));
728   plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
729   return;
730 }
731
732 /**
733  * Removes a message from the linked list of messages
734  * @param ses session to remove message from
735  * @param msg message to remove
736  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
737  */
738
739 static int remove_http_message(struct Session * ses, struct HTTP_Message * msg)
740 {
741   GNUNET_CONTAINER_DLL_remove(ses->pending_outbound_msg_head,ses->pending_outbound_msg_tail,msg);
742   GNUNET_free (msg->dest_url);
743   GNUNET_free(msg);
744   return GNUNET_OK;
745 }
746
747
748 static size_t header_function( void *ptr, size_t size, size_t nmemb, void *stream)
749 {
750   char * tmp;
751   size_t len = size * nmemb;
752
753   tmp = NULL;
754   if ((size * nmemb) < SIZE_MAX)
755     tmp = GNUNET_malloc (len+1);
756
757   if ((tmp != NULL) && (len > 0))
758   {
759     memcpy(tmp,ptr,len);
760     if (len>=2)
761     {
762       if (tmp[len-2] == 13)
763         tmp[len-2]= '\0';
764     }
765 #if DEBUG_CURL
766     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Header: `%s'\n",tmp);
767 #endif
768   }
769   if (NULL != tmp)
770     GNUNET_free (tmp);
771
772   return size * nmemb;
773 }
774
775 /**
776  * Callback method used with libcurl
777  * Method is called when libcurl needs to read data during sending
778  * @param stream pointer where to write data
779  * @param size size of an individual element
780  * @param nmemb count of elements that can be written to the buffer
781  * @param ptr source pointer, passed to the libcurl handle
782  * @return bytes written to stream
783  */
784 static size_t send_read_callback(void *stream, size_t size, size_t nmemb, void *ptr)
785 {
786   struct Session * ses = ptr;
787   struct HTTP_Message * msg = ses->pending_outbound_msg_tail;
788   size_t bytes_sent;
789   size_t len;
790
791   /* data to send */
792   if (( msg->pos < msg->size))
793   {
794     /* data fit in buffer */
795     if ((msg->size - msg->pos) <= (size * nmemb))
796     {
797       len = (msg->size - msg->pos);
798       memcpy(stream, &msg->buf[msg->pos], len);
799       msg->pos += len;
800       bytes_sent = len;
801     }
802     else
803     {
804       len = size*nmemb;
805       memcpy(stream, &msg->buf[msg->pos], len);
806       msg->pos += len;
807       bytes_sent = len;
808     }
809   }
810   /* no data to send */
811   else
812   {
813     bytes_sent = 0;
814   }
815   return bytes_sent;
816 }
817
818 /**
819 * Callback method used with libcurl
820 * Method is called when libcurl needs to write data during sending
821 * @param stream pointer where to write data
822 * @param size size of an individual element
823 * @param nmemb count of elements that can be written to the buffer
824 * @param ptr destination pointer, passed to the libcurl handle
825 * @return bytes read from stream
826 */
827 static size_t send_write_callback( void *stream, size_t size, size_t nmemb, void *ptr)
828 {
829   char * data = NULL;
830
831   if ((size * nmemb) < SIZE_MAX)
832     data = GNUNET_malloc(size*nmemb +1);
833   if (data != NULL)
834   {
835     memcpy( data, stream, size*nmemb);
836     data[size*nmemb] = '\0';
837     free (data);
838   }
839   return (size * nmemb);
840
841 }
842
843 /**
844  * Function setting up file descriptors and scheduling task to run
845  * @param ses session to send data to
846  * @return bytes sent to peer
847  */
848 static size_t send_prepare(void *cls, struct Session* ses );
849
850 /**
851  * Function setting up curl handle and selecting message to send
852  * @param ses session to send data to
853  * @return bytes sent to peer
854  */
855 static ssize_t send_select_init (void *cls, struct Session* ses )
856 {
857   struct Plugin *plugin = cls;
858   int bytes_sent = 0;
859   CURLMcode mret;
860   struct HTTP_Message * msg;
861
862   GNUNET_assert(cls !=NULL);
863   if ( NULL == ses->curl_handle)
864     ses->curl_handle = curl_easy_init();
865   if( NULL == ses->curl_handle)
866   {
867     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Getting cURL handle failed\n");
868     return -1;
869   }
870   GNUNET_assert (NULL != ses->pending_outbound_msg_tail);
871   msg = ses->pending_outbound_msg_tail;
872
873 #if DEBUG_CURL
874   curl_easy_setopt(ses->curl_handle, CURLOPT_VERBOSE, 1L);
875 #endif
876   curl_easy_setopt(ses->curl_handle, CURLOPT_URL, msg->dest_url);
877   curl_easy_setopt(ses->curl_handle, CURLOPT_PUT, 1L);
878   curl_easy_setopt(ses->curl_handle, CURLOPT_HEADERFUNCTION, &header_function);
879   curl_easy_setopt(ses->curl_handle, CURLOPT_WRITEHEADER, ses);
880   curl_easy_setopt(ses->curl_handle, CURLOPT_READFUNCTION, send_read_callback);
881   curl_easy_setopt(ses->curl_handle, CURLOPT_READDATA, ses);
882   curl_easy_setopt(ses->curl_handle, CURLOPT_WRITEFUNCTION, send_write_callback);
883   curl_easy_setopt(ses->curl_handle, CURLOPT_READDATA, ses);
884   curl_easy_setopt(ses->curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t) msg->size);
885   curl_easy_setopt(ses->curl_handle, CURLOPT_TIMEOUT, GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
886   GNUNET_assert (CURLE_OK == curl_easy_setopt(ses->curl_handle, CURLOPT_PRIVATE, ses));
887   curl_easy_setopt(ses->curl_handle, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
888   curl_easy_setopt(ses->curl_handle, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE);
889
890   mret = curl_multi_add_handle(plugin->multi_handle, ses->curl_handle);
891   if (mret != CURLM_OK)
892   {
893     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
894                 _("%s failed at %s:%d: `%s'\n"),
895                 "curl_multi_add_handle", __FILE__, __LINE__,
896                 curl_multi_strerror (mret));
897     return -1;
898   }
899   bytes_sent = send_prepare (plugin, ses );
900   return bytes_sent;
901 }
902
903 static void send_execute (void *cls,
904              const struct GNUNET_SCHEDULER_TaskContext *tc)
905 {
906   struct Plugin *plugin = cls;
907   static unsigned int handles_last_run;
908   int running;
909   struct CURLMsg *msg;
910   CURLMcode mret;
911   struct Session * cs = NULL;
912   long http_result;
913
914   GNUNET_assert(cls !=NULL);
915   plugin->http_server_task_send = GNUNET_SCHEDULER_NO_TASK;
916   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
917     return;
918
919   do
920     {
921       running = 0;
922       mret = curl_multi_perform (plugin->multi_handle, &running);
923       if (running < handles_last_run)
924         {
925           do
926             {
927
928               msg = curl_multi_info_read (plugin->multi_handle, &running);
929               GNUNET_break (msg != NULL);
930               if (msg == NULL)
931                 break;
932               /* get session for affected curl handle */
933               GNUNET_assert ( msg->easy_handle != NULL );
934               curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &cs);
935               GNUNET_assert ( cs != NULL );
936               GNUNET_assert ( cs->pending_outbound_msg_tail != NULL );
937               switch (msg->msg)
938                 {
939
940                 case CURLMSG_DONE:
941                   if ( (msg->data.result != CURLE_OK) &&
942                        (msg->data.result != CURLE_GOT_NOTHING) )
943                   {
944                     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
945                                _("%s failed for `%s' at %s:%d: `%s'\n"),
946                                "curl_multi_perform",
947                                GNUNET_i2s(&cs->partner),
948                                __FILE__,
949                                __LINE__,
950                                curl_easy_strerror (msg->data.result));
951                     /* sending msg failed*/
952                     if (( NULL != cs->pending_outbound_msg_tail) && ( NULL != cs->pending_outbound_msg_tail->transmit_cont))
953                       cs->pending_outbound_msg_tail->transmit_cont (cs->pending_outbound_msg_tail->transmit_cont_cls,&cs->partner,GNUNET_SYSERR);
954                   }
955                   else
956                   {
957                     GNUNET_assert (CURLE_OK == curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &http_result));
958                     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
959                                 "Send to peer `%s' completed with code %u\n", GNUNET_i2s(&cs->partner), http_result );
960
961                     curl_easy_cleanup(cs->curl_handle);
962                     cs->curl_handle=NULL;
963
964                     /* Calling transmit continuation  */
965                     if (( NULL != cs->pending_outbound_msg_tail) && (NULL != cs->pending_outbound_msg_tail->transmit_cont))
966                     {
967                       /* HTTP 1xx : Last message before here was informational */
968                       if ((http_result >=100) && (http_result < 200))
969                         cs->pending_outbound_msg_tail->transmit_cont (cs->pending_outbound_msg_tail->transmit_cont_cls,&cs->partner,GNUNET_OK);
970                       /* HTTP 2xx: successful operations */
971                       if ((http_result >=200) && (http_result < 300))
972                         cs->pending_outbound_msg_tail->transmit_cont (cs->pending_outbound_msg_tail->transmit_cont_cls,&cs->partner,GNUNET_OK);
973                       /* HTTP 3xx..5xx: error */
974                       if ((http_result >=300) && (http_result < 600))
975                         cs->pending_outbound_msg_tail->transmit_cont (cs->pending_outbound_msg_tail->transmit_cont_cls,&cs->partner,GNUNET_SYSERR);
976                     }
977                   }
978
979                   if (GNUNET_OK != remove_http_message(cs, cs->pending_outbound_msg_tail))
980                     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Message could not be removed from session `%s'", GNUNET_i2s(&cs->partner));
981                   /* send pending messages */
982                   if (cs->pending_outbound_msg_tail!= NULL)
983                   {
984                     send_select_init (plugin, cs);
985                   }
986                   return;
987                 default:
988                   break;
989                 }
990
991             }
992           while ( (running > 0) );
993         }
994       handles_last_run = running;
995     }
996   while (mret == CURLM_CALL_MULTI_PERFORM);
997   send_prepare(plugin, cls);
998 }
999
1000
1001 /**
1002  * Function setting up file descriptors and scheduling task to run
1003  * @param ses session to send data to
1004  * @return bytes sent to peer
1005  */
1006 static size_t send_prepare(void *cls, struct Session* ses )
1007 {
1008   struct Plugin *plugin = cls;
1009   fd_set rs;
1010   fd_set ws;
1011   fd_set es;
1012   int max;
1013   struct GNUNET_NETWORK_FDSet *grs;
1014   struct GNUNET_NETWORK_FDSet *gws;
1015   long to;
1016   CURLMcode mret;
1017
1018   GNUNET_assert(cls !=NULL);
1019   max = -1;
1020   FD_ZERO (&rs);
1021   FD_ZERO (&ws);
1022   FD_ZERO (&es);
1023   mret = curl_multi_fdset (plugin->multi_handle, &rs, &ws, &es, &max);
1024   if (mret != CURLM_OK)
1025     {
1026       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1027                   _("%s failed at %s:%d: `%s'\n"),
1028                   "curl_multi_fdset", __FILE__, __LINE__,
1029                   curl_multi_strerror (mret));
1030       return -1;
1031     }
1032   mret = curl_multi_timeout (plugin->multi_handle, &to);
1033   if (mret != CURLM_OK)
1034     {
1035       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1036                   _("%s failed at %s:%d: `%s'\n"),
1037                   "curl_multi_timeout", __FILE__, __LINE__,
1038                   curl_multi_strerror (mret));
1039       return -1;
1040     }
1041
1042   grs = GNUNET_NETWORK_fdset_create ();
1043   gws = GNUNET_NETWORK_fdset_create ();
1044   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1045   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1046   plugin->http_server_task_send = GNUNET_SCHEDULER_add_select (plugin->env->sched,
1047                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1048                                    GNUNET_SCHEDULER_NO_TASK,
1049                                    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 0),
1050                                    grs,
1051                                    gws,
1052                                    &send_execute,
1053                                    plugin);
1054   GNUNET_NETWORK_fdset_destroy (gws);
1055   GNUNET_NETWORK_fdset_destroy (grs);
1056
1057   /* FIXME: return bytes REALLY sent */
1058   return 0;
1059 }
1060
1061 /**
1062  * Function that can be used by the transport service to transmit
1063  * a message using the plugin.
1064  *
1065  * @param cls closure
1066  * @param target who should receive this message
1067  * @param priority how important is the message
1068  * @param msgbuf the message to transmit
1069  * @param msgbuf_size number of bytes in 'msgbuf'
1070  * @param to when should we time out
1071  * @param session which session must be used (or NULL for "any")
1072  * @param addr the address to use (can be NULL if the plugin
1073  *                is "on its own" (i.e. re-use existing TCP connection))
1074  * @param addrlen length of the address in bytes
1075  * @param force_address GNUNET_YES if the plugin MUST use the given address,
1076  *                otherwise the plugin may use other addresses or
1077  *                existing connections (if available)
1078  * @param cont continuation to call once the message has
1079  *        been transmitted (or if the transport is ready
1080  *        for the next transmission call; or if the
1081  *        peer disconnected...)
1082  * @param cont_cls closure for cont
1083  * @return number of bytes used (on the physical network, with overheads);
1084  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
1085  *         and does NOT mean that the message was not transmitted (DV)
1086  */
1087 static ssize_t
1088 http_plugin_send (void *cls,
1089                   const struct GNUNET_PeerIdentity *target,
1090                   const char *msgbuf,
1091                   size_t msgbuf_size,
1092                   unsigned int priority,
1093                   struct GNUNET_TIME_Relative to,
1094                   struct Session *session,
1095                   const void *addr,
1096                   size_t addrlen,
1097                   int force_address,
1098                   GNUNET_TRANSPORT_TransmitContinuation cont,
1099                   void *cont_cls)
1100 {
1101   struct Plugin *plugin = cls;
1102   char *address;
1103   char *url;
1104   struct Session *cs;
1105   struct HTTP_Message *msg;
1106   unsigned int res;
1107
1108   GNUNET_assert(cls !=NULL);
1109   url = NULL;
1110   address = NULL;
1111
1112   cs = GNUNET_CONTAINER_multihashmap_get (plugin->sessions, &target->hashPubKey);
1113   if (cs != NULL)
1114     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1115                 "Session `%s' found\n", GNUNET_i2s(target));
1116
1117   if ( cs == NULL)
1118   {
1119     cs = create_session(plugin, (char *) addr, addrlen, NULL, 0, target);
1120     res = GNUNET_CONTAINER_multihashmap_put ( plugin->sessions,
1121                                         &cs->partner.hashPubKey,
1122                                         cs,
1123                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
1124     if (res == GNUNET_OK)
1125       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1126                   "New Session `%s' inserted\n", GNUNET_i2s(target));
1127   }
1128
1129   GNUNET_assert ((addr!=NULL) && (addrlen != 0));
1130   if (addrlen == (sizeof (struct IPv4HttpAddress)))
1131   {
1132     address = GNUNET_malloc(INET_ADDRSTRLEN + 1);
1133     inet_ntop(AF_INET, &((struct IPv4HttpAddress *) addr)->ipv4_addr,address,INET_ADDRSTRLEN);
1134     GNUNET_asprintf (&url,
1135                      "http://%s:%u/%s",
1136                      address,
1137                      ntohs(((struct IPv4HttpAddress *) addr)->u_port),
1138                      (char *) (&plugin->my_ascii_hash_ident));
1139     GNUNET_free(address);
1140   }
1141   else if (addrlen == (sizeof (struct IPv6HttpAddress)))
1142   {
1143     address = GNUNET_malloc(INET6_ADDRSTRLEN + 1);
1144     inet_ntop(AF_INET6, &((struct IPv6HttpAddress *) addr)->ipv6_addr,address,INET6_ADDRSTRLEN);
1145     GNUNET_asprintf(&url,
1146                     "http://%s:%u/%s",
1147                     address,
1148                     ntohs(((struct IPv6HttpAddress *) addr)->u6_port),
1149                     (char *) (&plugin->my_ascii_hash_ident));
1150     GNUNET_free(address);
1151   }
1152
1153   /* setting up message */
1154   msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
1155   msg->next = NULL;
1156   msg->size = msgbuf_size;
1157   msg->pos = 0;
1158   msg->buf = (char *) &msg[1];
1159   msg->dest_url = url;
1160   msg->transmit_cont = cont;
1161   msg->transmit_cont_cls = cont_cls;
1162   memcpy (msg->buf,msgbuf, msgbuf_size);
1163   /* insert created message in double linked list of pending messages */
1164   GNUNET_CONTAINER_DLL_insert (cs->pending_outbound_msg_head, cs->pending_outbound_msg_tail, msg);
1165
1166   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Plugin: sending %u bytes of data from peer `%4.4s' to peer `%s'\n",msgbuf_size,(char *) &plugin->my_ascii_hash_ident,GNUNET_i2s(target));
1167   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Plugin: url `%s'\n",url);
1168   if (msg == cs->pending_outbound_msg_tail)
1169   {
1170     return send_select_init (plugin, cs);
1171   }
1172   return msgbuf_size;
1173 }
1174
1175
1176
1177 /**
1178  * Function that can be used to force the plugin to disconnect
1179  * from the given peer and cancel all previous transmissions
1180  * (and their continuationc).
1181  *
1182  * @param cls closure
1183  * @param target peer from which to disconnect
1184  */
1185 static void
1186 http_plugin_disconnect (void *cls,
1187                             const struct GNUNET_PeerIdentity *target)
1188 {
1189   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Plugin: http_plugin_disconnect\n");
1190   // struct Plugin *plugin = cls;
1191   // FIXME
1192 }
1193
1194
1195 /**
1196  * Convert the transports address to a nice, human-readable
1197  * format.
1198  *
1199  * @param cls closure
1200  * @param type name of the transport that generated the address
1201  * @param addr one of the addresses of the host, NULL for the last address
1202  *        the specific address format depends on the transport
1203  * @param addrlen length of the address
1204  * @param numeric should (IP) addresses be displayed in numeric form?
1205  * @param timeout after how long should we give up?
1206  * @param asc function to call on each string
1207  * @param asc_cls closure for asc
1208  */
1209 static void
1210 http_plugin_address_pretty_printer (void *cls,
1211                                         const char *type,
1212                                         const void *addr,
1213                                         size_t addrlen,
1214                                         int numeric,
1215                                         struct GNUNET_TIME_Relative timeout,
1216                                         GNUNET_TRANSPORT_AddressStringCallback
1217                                         asc, void *asc_cls)
1218 {
1219   const struct IPv4HttpAddress *t4;
1220   const struct IPv6HttpAddress *t6;
1221   struct sockaddr_in a4;
1222   struct sockaddr_in6 a6;
1223   char * address;
1224   char * ret;
1225   unsigned int port;
1226   unsigned int res;
1227
1228   GNUNET_assert(cls !=NULL);
1229   if (addrlen == sizeof (struct IPv6HttpAddress))
1230   {
1231     address = GNUNET_malloc (INET6_ADDRSTRLEN);
1232     t6 = addr;
1233     a6.sin6_addr = t6->ipv6_addr;
1234     inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
1235     port = ntohs(t6->u6_port);
1236   }
1237   else if (addrlen == sizeof (struct IPv4HttpAddress))
1238   {
1239     address = GNUNET_malloc (INET_ADDRSTRLEN);
1240     t4 = addr;
1241     a4.sin_addr.s_addr =  t4->ipv4_addr;
1242     inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
1243     port = ntohs(t4->u_port);
1244   }
1245   else
1246   {
1247     /* invalid address */
1248     GNUNET_break_op (0);
1249     asc (asc_cls, NULL);
1250     return;
1251   }
1252   res = GNUNET_asprintf(&ret,"http://%s:%u/",address,port);
1253   GNUNET_free (address);
1254   GNUNET_assert(res != 0);
1255
1256   asc (asc_cls, ret);
1257 }
1258
1259
1260
1261 /**
1262  * Another peer has suggested an address for this
1263  * peer and transport plugin.  Check that this could be a valid
1264  * address.  If so, consider adding it to the list
1265  * of addresses.
1266  *
1267  * @param cls closure
1268  * @param addr pointer to the address
1269  * @param addrlen length of addr
1270  * @return GNUNET_OK if this is a plausible address for this peer
1271  *         and transport
1272  */
1273 static int
1274 http_plugin_address_suggested (void *cls,
1275                                   void *addr, size_t addrlen)
1276 {
1277   struct Plugin *plugin = cls;
1278   struct IPv4HttpAddress *v4;
1279   struct IPv6HttpAddress *v6;
1280   unsigned int port;
1281
1282   GNUNET_assert(cls !=NULL);
1283   if ((addrlen != sizeof (struct IPv4HttpAddress)) &&
1284       (addrlen != sizeof (struct IPv6HttpAddress)))
1285     {
1286       return GNUNET_SYSERR;
1287     }
1288   if (addrlen == sizeof (struct IPv4HttpAddress))
1289     {
1290       v4 = (struct IPv4HttpAddress *) addr;
1291       if (INADDR_LOOPBACK == ntohl(v4->ipv4_addr))
1292       {
1293         return GNUNET_SYSERR;
1294       }
1295       port = ntohs (v4->u_port);
1296       if (port != plugin->port_inbound)
1297       {
1298         return GNUNET_SYSERR;
1299       }
1300     }
1301   else
1302     {
1303       v6 = (struct IPv6HttpAddress *) addr;
1304       if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
1305         {
1306           return GNUNET_SYSERR;
1307         }
1308       port = ntohs (v6->u6_port);
1309       if (port != plugin->port_inbound)
1310       {
1311         return GNUNET_SYSERR;
1312       }
1313     }
1314
1315
1316   return GNUNET_OK;
1317 }
1318
1319
1320 /**
1321  * Function called for a quick conversion of the binary address to
1322  * a numeric address.  Note that the caller must not free the
1323  * address and that the next call to this function is allowed
1324  * to override the address again.
1325  *
1326  * @param cls closure
1327  * @param addr binary address
1328  * @param addrlen length of the address
1329  * @return string representing the same address
1330  */
1331 static const char*
1332 http_plugin_address_to_string (void *cls,
1333                                    const void *addr,
1334                                    size_t addrlen)
1335 {
1336   const struct IPv4HttpAddress *t4;
1337   const struct IPv6HttpAddress *t6;
1338   struct sockaddr_in a4;
1339   struct sockaddr_in6 a6;
1340   char * address;
1341   char * ret;
1342   unsigned int port;
1343   unsigned int res;
1344
1345   GNUNET_assert(cls !=NULL);
1346   if (addrlen == sizeof (struct IPv6HttpAddress))
1347     {
1348       address = GNUNET_malloc (INET6_ADDRSTRLEN);
1349       t6 = addr;
1350       a6.sin6_addr = t6->ipv6_addr;
1351       inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
1352       port = ntohs(t6->u6_port);
1353     }
1354   else if (addrlen == sizeof (struct IPv4HttpAddress))
1355     {
1356       address = GNUNET_malloc (INET_ADDRSTRLEN);
1357       t4 = addr;
1358       a4.sin_addr.s_addr =  t4->ipv4_addr;
1359       inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
1360       port = ntohs(t4->u_port);
1361     }
1362   else
1363     {
1364       /* invalid address */
1365       return NULL;
1366     }
1367   res = GNUNET_asprintf(&ret,"%s:%u",address,port);
1368   GNUNET_free (address);
1369   GNUNET_assert(res != 0);
1370   return ret;
1371 }
1372
1373 /**
1374  * Add the IP of our network interface to the list of
1375  * our external IP addresses.
1376  *
1377  * @param cls the 'struct Plugin*'
1378  * @param name name of the interface
1379  * @param isDefault do we think this may be our default interface
1380  * @param addr address of the interface
1381  * @param addrlen number of bytes in addr
1382  * @return GNUNET_OK to continue iterating
1383  */
1384 static int
1385 process_interfaces (void *cls,
1386                     const char *name,
1387                     int isDefault,
1388                     const struct sockaddr *addr, socklen_t addrlen)
1389 {
1390   struct Plugin *plugin = cls;
1391   struct IPv4HttpAddress t4;
1392   struct IPv6HttpAddress t6;
1393   int af;
1394   void *arg;
1395   uint16_t args;
1396
1397   GNUNET_assert(cls !=NULL);
1398   af = addr->sa_family;
1399   if (af == AF_INET)
1400     {
1401       if (INADDR_LOOPBACK == ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr))
1402       {
1403         /* skip loopback addresses */
1404         return GNUNET_OK;
1405       }
1406       t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
1407       t4.u_port = htons (plugin->port_inbound);
1408       arg = &t4;
1409       args = sizeof (t4);
1410     }
1411   else if (af == AF_INET6)
1412     {
1413       if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
1414         {
1415           /* skip link local addresses */
1416           return GNUNET_OK;
1417         }
1418       if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr))
1419         {
1420           /* skip loopback addresses */
1421           return GNUNET_OK;
1422         }
1423       memcpy (&t6.ipv6_addr,
1424               &((struct sockaddr_in6 *) addr)->sin6_addr,
1425               sizeof (struct in6_addr));
1426       t6.u6_port = htons (plugin->port_inbound);
1427       arg = &t6;
1428       args = sizeof (t6);
1429     }
1430   else
1431     {
1432       GNUNET_break (0);
1433       return GNUNET_OK;
1434     }
1435   plugin->env->notify_address(plugin->env->cls,"http",arg, args, GNUNET_TIME_UNIT_FOREVER_REL);
1436   return GNUNET_OK;
1437 }
1438
1439 int hashMapFreeIterator (void *cls, const GNUNET_HashCode *key, void *value)
1440 {
1441   struct Session * cs = value;
1442
1443   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing session for peer `%s'\n",GNUNET_i2s(&cs->partner));
1444
1445   /* freeing messages */
1446   struct HTTP_Message *cur;
1447   struct HTTP_Message *tmp;
1448   cur = cs->pending_outbound_msg_head;
1449
1450   while (cur != NULL)
1451   {
1452     tmp = cur->next;
1453     GNUNET_free_non_null(cur->dest_url);
1454     GNUNET_free (cur);
1455     cur = tmp;
1456   }
1457   GNUNET_SERVER_mst_destroy (cs->msgtok);
1458   GNUNET_free_non_null (cs->addr_in);
1459   GNUNET_free_non_null (cs->addr_out);
1460   GNUNET_free (cs);
1461   return GNUNET_YES;
1462 }
1463
1464 /**
1465  * Exit point from the plugin.
1466  */
1467 void *
1468 libgnunet_plugin_transport_http_done (void *cls)
1469 {
1470   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1471   struct Plugin *plugin = api->cls;
1472   CURLMcode mret;
1473
1474   GNUNET_assert(cls !=NULL);
1475   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Unloading http plugin...\n");
1476
1477   if ( plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1478   {
1479     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4);
1480     plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
1481   }
1482
1483   if ( plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1484   {
1485     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v6);
1486     plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1487   }
1488
1489   if ( plugin->http_server_task_send != GNUNET_SCHEDULER_NO_TASK)
1490   {
1491     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_send);
1492     plugin->http_server_task_send = GNUNET_SCHEDULER_NO_TASK;
1493   }
1494
1495   if (plugin->http_server_daemon_v4 != NULL)
1496   {
1497     MHD_stop_daemon (plugin->http_server_daemon_v4);
1498     plugin->http_server_daemon_v4 = NULL;
1499   }
1500   if (plugin->http_server_daemon_v6 != NULL)
1501   {
1502     MHD_stop_daemon (plugin->http_server_daemon_v6);
1503     plugin->http_server_daemon_v6 = NULL;
1504   }
1505
1506   /* free all sessions */
1507   GNUNET_CONTAINER_multihashmap_iterate (plugin->sessions,
1508                                          &hashMapFreeIterator,
1509                                          NULL);
1510
1511   GNUNET_CONTAINER_multihashmap_destroy (plugin->sessions);
1512
1513   mret = curl_multi_cleanup(plugin->multi_handle);
1514   if ( CURLM_OK != mret)
1515     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"curl multihandle clean up failed");
1516
1517   GNUNET_free (plugin);
1518   GNUNET_free (api);
1519   return NULL;
1520 }
1521
1522
1523 /**
1524  * Entry point for the plugin.
1525  */
1526 void *
1527 libgnunet_plugin_transport_http_init (void *cls)
1528 {
1529   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1530   struct Plugin *plugin;
1531   struct GNUNET_TRANSPORT_PluginFunctions *api;
1532   struct GNUNET_TIME_Relative gn_timeout;
1533   long long unsigned int port;
1534
1535   GNUNET_assert(cls !=NULL);
1536   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting http plugin...\n");
1537
1538   plugin = GNUNET_malloc (sizeof (struct Plugin));
1539   plugin->env = env;
1540   plugin->sessions = NULL;
1541   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1542   api->cls = plugin;
1543   api->send = &http_plugin_send;
1544   api->disconnect = &http_plugin_disconnect;
1545   api->address_pretty_printer = &http_plugin_address_pretty_printer;
1546   api->check_address = &http_plugin_address_suggested;
1547   api->address_to_string = &http_plugin_address_to_string;
1548
1549   /* Hashing our identity to use it in URLs */
1550   GNUNET_CRYPTO_hash_to_enc ( &(plugin->env->my_identity->hashPubKey), &plugin->my_ascii_hash_ident);
1551
1552   /* Reading port number from config file */
1553   if ((GNUNET_OK !=
1554        GNUNET_CONFIGURATION_get_value_number (env->cfg,
1555                                               "transport-http",
1556                                               "PORT",
1557                                               &port)) ||
1558       (port > 65535) )
1559     {
1560       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1561                        "http",
1562                        _
1563                        ("Require valid port number for transport plugin `%s' in configuration!\n"),
1564                        "transport-http");
1565       libgnunet_plugin_transport_http_done (api);
1566       return NULL;
1567     }
1568   GNUNET_assert ((port > 0) && (port <= 65535));
1569   plugin->port_inbound = port;
1570   gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
1571   if ((plugin->http_server_daemon_v4 == NULL) && (plugin->http_server_daemon_v6 == NULL) && (port != 0))
1572     {
1573     plugin->http_server_daemon_v6 = MHD_start_daemon (MHD_USE_IPv6,
1574                                        port,
1575                                        &acceptPolicyCallback,
1576                                        plugin , &accessHandlerCallback, plugin,
1577                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
1578                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
1579                                        MHD_OPTION_CONNECTION_TIMEOUT, (gn_timeout.value / 1000),
1580                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
1581                                        MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL,
1582                                        MHD_OPTION_END);
1583     plugin->http_server_daemon_v4 = MHD_start_daemon (MHD_NO_FLAG,
1584                                        port,
1585                                        &acceptPolicyCallback,
1586                                        plugin , &accessHandlerCallback, plugin,
1587                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
1588                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
1589                                        MHD_OPTION_CONNECTION_TIMEOUT, (gn_timeout.value / 1000),
1590                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
1591                                        MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL,
1592                                        MHD_OPTION_END);
1593     }
1594   if (plugin->http_server_daemon_v4 != NULL)
1595     plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
1596   if (plugin->http_server_daemon_v6 != NULL)
1597     plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
1598
1599   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1600     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 on port %u\n",port);
1601   else if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1602     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 and IPv6 on port %u\n",port);
1603   else
1604   {
1605     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No MHD was started, transport plugin not functional!\n");
1606     libgnunet_plugin_transport_http_done (api);
1607     return NULL;
1608   }
1609
1610   /* Initializing cURL */
1611   plugin->multi_handle = curl_multi_init();
1612   if ( NULL == plugin->multi_handle )
1613   {
1614     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1615                      "http",
1616                      _("Could not initialize curl multi handle, failed to start http plugin!\n"),
1617                      "transport-http");
1618     libgnunet_plugin_transport_http_done (api);
1619     return NULL;
1620   }
1621
1622   plugin->sessions = GNUNET_CONTAINER_multihashmap_create (10);
1623   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
1624
1625   return api;
1626 }
1627
1628 /* end of plugin_transport_http.c */