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