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