d654a0c82837940556620bcd2092e140b1c9d567
[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 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
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_service_lib.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet_resolver_service.h"
35 #include "gnunet_server_lib.h"
36 #include "gnunet_container_lib.h"
37 #include "plugin_transport.h"
38 #include "gnunet_os_lib.h"
39 #include "microhttpd.h"
40 #include <curl/curl.h>
41
42 #define DEBUG_CURL GNUNET_NO
43 #define DEBUG_HTTP GNUNET_NO
44 #define DEBUG_CONNECTIONS GNUNET_NO
45
46 #define INBOUND GNUNET_NO
47 #define OUTBOUND GNUNET_YES
48
49 /**
50  * Text of the response sent back after the last bytes of a PUT
51  * request have been received (just to formally obey the HTTP
52  * protocol).
53  */
54 #define HTTP_PUT_RESPONSE "Thank you!"
55
56 /**
57  * After how long do we expire an address that we
58  * learned from another peer if it is not reconfirmed
59  * by anyone?
60  */
61 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
62
63 /**
64  * Page returned if request invalid
65  */
66 #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>"
67
68 /**
69  * Timeout for a http connect
70  */
71 #define HTTP_CONNECT_TIMEOUT 30
72
73
74 /**
75  * Network format for IPv4 addresses.
76  */
77 struct IPv4HttpAddress
78 {
79   /**
80    * IPv4 address, in network byte order.
81    */
82   uint32_t ipv4_addr GNUNET_PACKED;
83
84   /**
85    * Port number, in network byte order.
86    */
87   uint16_t u_port GNUNET_PACKED;
88
89 };
90
91
92 /**
93  * Network format for IPv6 addresses.
94  */
95 struct IPv6HttpAddress
96 {
97   /**
98    * IPv6 address.
99    */
100   struct in6_addr ipv6_addr GNUNET_PACKED;
101
102   /**
103    * Port number, in network byte order.
104    */
105   uint16_t u6_port GNUNET_PACKED;
106
107 };
108
109
110 /**
111  *  Message to send using http
112  */
113 struct HTTP_Message
114 {
115   /**
116    * next pointer for double linked list
117    */
118   struct HTTP_Message * next;
119
120   /**
121    * previous pointer for double linked list
122    */
123   struct HTTP_Message * prev;
124
125   /**
126    * buffer containing data to send
127    */
128   char *buf;
129
130   /**
131    * amount of data already sent
132    */
133   size_t pos;
134
135   /**
136    * buffer length
137    */
138   size_t size;
139
140   /**
141    * Continuation function to call once the transmission buffer
142    * has again space available.  NULL if there is no
143    * continuation to call.
144    */
145   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
146
147   /**
148    * Closure for transmit_cont.
149    */
150   void *transmit_cont_cls;
151 };
152
153
154 struct HTTP_PeerContext
155 {
156   /**
157    * peer's identity
158    */
159   struct GNUNET_PeerIdentity identity;
160
161   /**
162    * Pointer to the global plugin struct.
163    */
164   struct Plugin *plugin;
165
166   /**
167    * Linked list of connections with this peer
168    * head
169    */
170   struct Session * head;
171
172   /**
173    * Linked list of connections with this peer
174    * tail
175    */
176   struct Session * tail;
177
178   /**
179    * id for next session
180    */
181   size_t session_id_counter;
182 };
183
184
185 struct Session
186 {
187   /**
188    * API requirement.
189    */
190   struct SessionHeader header;
191
192   /**
193    * next session in linked list
194    */
195   struct Session * next;
196
197   /**
198    * previous session in linked list
199    */
200   struct Session * prev;
201
202   /**
203    * address of this session
204    */
205   void * addr;
206
207   /**
208    * address length
209    */
210   size_t addrlen;
211
212   /**
213    * target url
214    */
215   char * url;
216
217   /**
218    * Message queue for outbound messages
219    * head of queue
220    */
221   struct HTTP_Message * pending_msgs_head;
222
223   /**
224    * Message queue for outbound messages
225    * tail of queue
226    */
227   struct HTTP_Message * pending_msgs_tail;
228
229   /**
230    * partner peer this connection belongs to
231    */
232   struct HTTP_PeerContext * peercontext;
233
234   /**
235    * message stream tokenizer for incoming data
236    */
237   struct GNUNET_SERVER_MessageStreamTokenizer *msgtok;
238
239   /**
240    * session direction
241    * outbound: OUTBOUND (GNUNET_YES)
242    * inbound : INBOUND (GNUNET_NO)
243    */
244   unsigned int direction;
245
246   /**
247    * is session connected to send data?
248    */
249   unsigned int send_connected;
250
251   /**
252    * is send connection active?
253    */
254   unsigned int send_active;
255
256   /**
257    * connection disconnect forced (e.g. from transport)
258    */
259   unsigned int send_force_disconnect;
260
261   /**
262    * is session connected to receive data?
263    */
264   unsigned int recv_connected;
265
266   /**
267    * is receive connection active?
268    */
269   unsigned int recv_active;
270
271   /**
272    * connection disconnect forced (e.g. from transport)
273    */
274   unsigned int recv_force_disconnect;
275
276   /**
277    * id for next session
278    * NOTE: 0 is not an ID, zero is not defined. A correct ID is always > 0
279    */
280   size_t session_id;
281
282   /**
283    * entity managing sending data
284    * outbound session: CURL *
285    * inbound session: mhd_connection *
286    */
287   void * send_endpoint;
288
289   /**
290    * entity managing recieving data
291    * outbound session: CURL *
292    * inbound session: mhd_connection *
293    */
294   void * recv_endpoint;
295 };
296
297 /**
298  * Encapsulation of all of the state of the plugin.
299  */
300 struct Plugin
301 {
302   /**
303    * Our environment.
304    */
305   struct GNUNET_TRANSPORT_PluginEnvironment *env;
306
307   /**
308    * Handle for reporting statistics.
309    */
310   struct GNUNET_STATISTICS_Handle *stats;
311
312   unsigned int port_inbound;
313
314   struct GNUNET_CONTAINER_MultiHashMap *peers;
315
316   /**
317    * Daemon for listening for new IPv4 connections.
318    */
319   struct MHD_Daemon *http_server_daemon_v4;
320
321   /**
322    * Daemon for listening for new IPv6connections.
323    */
324   struct MHD_Daemon *http_server_daemon_v6;
325
326   /**
327    * Our primary task for http daemon handling IPv4 connections
328    */
329   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v4;
330
331   /**
332    * Our primary task for http daemon handling IPv6 connections
333    */
334   GNUNET_SCHEDULER_TaskIdentifier http_server_task_v6;
335
336   /**
337    * The task sending data
338    */
339   GNUNET_SCHEDULER_TaskIdentifier http_curl_task;
340
341   /**
342    * cURL Multihandle
343    */
344   CURLM * multi_handle;
345
346   /**
347    * Our ASCII encoded, hashed peer identity
348    * This string is used to distinguish between connections and is added to the urls
349    */
350   struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident;
351 };
352
353
354 /**
355  * Function called for a quick conversion of the binary address to
356  * a numeric address.  Note that the caller must not free the
357  * address and that the next call to this function is allowed
358  * to override the address again.
359  *
360  * @param cls closure
361  * @param addr binary address
362  * @param addrlen length of the address
363  * @return string representing the same address
364  */
365 static const char*
366 http_plugin_address_to_string (void *cls,
367                                    const void *addr,
368                                    size_t addrlen);
369
370 static char * create_url(void * cls, const void * addr, size_t addrlen, size_t id)
371 {
372   struct Plugin *plugin = cls;
373   char *url = NULL;
374
375   GNUNET_assert ((addr!=NULL) && (addrlen != 0));
376   GNUNET_asprintf(&url,
377                   "http://%s/%s;%u",
378                   http_plugin_address_to_string(NULL, addr, addrlen),
379                   (char *) (&plugin->my_ascii_hash_ident),id);
380
381   return url;
382 }
383
384 /**
385  * Removes a message from the linked list of messages
386  * @param con connection to remove message from
387  * @param msg message to remove
388  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
389  */
390 static int remove_http_message (struct Session * ps, struct HTTP_Message * msg)
391 {
392   GNUNET_CONTAINER_DLL_remove(ps->pending_msgs_head,ps->pending_msgs_tail,msg);
393   GNUNET_free(msg);
394   return GNUNET_OK;
395 }
396
397 /**
398  * Removes a session from the linked list of sessions
399  * @param pc peer context
400  * @param ps session
401  * @param call_msg_cont GNUNET_YES to call pending message continuations, otherwise no
402  * @param call_msg_cont_result, result to call message continuations with
403  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
404  */
405 static int remove_session (struct HTTP_PeerContext * pc, struct Session * ps,  int call_msg_cont, int call_msg_cont_result)
406 {
407   struct HTTP_Message * msg;
408   struct Plugin * plugin = ps->peercontext->plugin;
409
410   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: removing %s session %X with id %u\n", ps, (ps->direction == INBOUND) ? "inbound" : "outbound", ps, ps->session_id);
411   plugin->env->session_end(plugin, &pc->identity, ps);
412
413   GNUNET_free_non_null (ps->addr);
414   GNUNET_SERVER_mst_destroy (ps->msgtok);
415   GNUNET_free(ps->url);
416
417   msg = ps->pending_msgs_head;
418   while (msg!=NULL)
419   {
420     if ((call_msg_cont == GNUNET_YES) && (msg->transmit_cont!=NULL))
421     {
422       msg->transmit_cont (msg->transmit_cont_cls,&pc->identity,call_msg_cont_result);
423     }
424     GNUNET_free(msg);
425     GNUNET_CONTAINER_DLL_remove(ps->pending_msgs_head,ps->pending_msgs_head,msg);
426     msg = ps->pending_msgs_head;
427   }
428
429   GNUNET_CONTAINER_DLL_remove(pc->head,pc->tail,ps);
430   GNUNET_free(ps);
431   ps = NULL;
432   return GNUNET_OK;
433 }
434
435 static struct Session * get_Session (void * cls, struct HTTP_PeerContext *pc, const void * addr, size_t addr_len)
436 {
437   struct Session * cc = pc->head;
438   struct Session * con = NULL;
439   unsigned int count = 0;
440
441   GNUNET_assert((addr_len == sizeof (struct IPv4HttpAddress)) || (addr_len == sizeof (struct IPv6HttpAddress)));
442   while (cc!=NULL)
443   {
444     if (addr_len == cc->addrlen)
445     {
446       if (0 == memcmp(cc->addr, addr, addr_len))
447       {
448         /* connection can not be used, since it is disconnected */
449         if ((cc->recv_force_disconnect==GNUNET_NO) && (cc->send_force_disconnect==GNUNET_NO))
450           con = cc;
451         break;
452       }
453     }
454     count++;
455     cc=cc->next;
456   }
457   return con;
458 }
459
460
461 /**
462  * Callback called by MHD when a connection is terminated
463  */
464 static void mhd_termination_cb (void *cls, struct MHD_Connection * connection, void **httpSessionCache)
465 {
466   struct Session * ps = *httpSessionCache;
467   if (ps == NULL)
468     return;
469   struct HTTP_PeerContext * pc = ps->peercontext;
470
471   if (connection==ps->recv_endpoint)
472   {
473 #if DEBUG_CONNECTIONS
474     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connection from peer `%s' was terminated\n", ps, GNUNET_i2s(&pc->identity));
475 #endif
476     ps->recv_active = GNUNET_NO;
477     ps->recv_connected = GNUNET_NO;
478     ps->recv_endpoint = NULL;
479   }
480   if (connection==ps->send_endpoint)
481   {
482
483     ps->send_active = GNUNET_NO;
484     ps->send_connected = GNUNET_NO;
485     ps->send_endpoint = NULL;
486 #if DEBUG_CONNECTIONS
487     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound connection from peer `%s' was terminated\n", ps, GNUNET_i2s(&pc->identity));
488 #endif
489   }
490
491   /* if both connections disconnected, remove session */
492   if ((ps->send_connected == GNUNET_NO) && (ps->recv_connected == GNUNET_NO))
493   {
494     remove_session(pc,ps,GNUNET_YES,GNUNET_SYSERR);
495   }
496 }
497
498 static void mhd_write_mst_cb (void *cls,
499                               void *client,
500                               const struct GNUNET_MessageHeader *message)
501 {
502
503   struct Session *ps  = cls;
504   struct HTTP_PeerContext *pc = ps->peercontext;
505   GNUNET_assert(ps != NULL);
506   GNUNET_assert(pc != NULL);
507
508   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
509               "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
510               ps,
511               ntohs(message->type),
512               ntohs(message->size),
513               GNUNET_i2s(&(ps->peercontext)->identity),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen));
514
515   pc->plugin->env->receive (ps->peercontext->plugin->env->cls,
516                             &pc->identity,
517                             message, 1, ps,
518                             NULL,
519                             0);
520 }
521
522 static void curl_receive_mst_cb  (void *cls,
523                                 void *client,
524                                 const struct GNUNET_MessageHeader *message)
525 {
526   struct Session *ps  = cls;
527   struct HTTP_PeerContext *pc = ps->peercontext;
528   GNUNET_assert(ps != NULL);
529   GNUNET_assert(pc != NULL);
530
531   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
532               "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n",
533               ps,
534               ntohs(message->type),
535               ntohs(message->size),
536               GNUNET_i2s(&(pc->identity)),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen));
537
538   pc->plugin->env->receive (pc->plugin->env->cls,
539                             &pc->identity,
540                             message, 1, ps,
541                             ps->addr,
542                             ps->addrlen);
543 }
544
545
546 /**
547  * Check if ip is allowed to connect.
548  */
549 static int
550 mhd_accept_cb (void *cls,
551                       const struct sockaddr *addr, socklen_t addr_len)
552 {
553 #if 0
554   struct Plugin *plugin = cls;
555 #endif
556   /* Every connection is accepted, nothing more to do here */
557   return MHD_YES;
558 }
559
560 int mhd_send_callback (void *cls, uint64_t pos, char *buf, int max)
561 {
562   int bytes_read = 0;
563
564   struct Session * ps = cls;
565   struct HTTP_PeerContext * pc;
566   struct HTTP_Message * msg;
567
568   GNUNET_assert (ps!=NULL);
569   pc = ps->peercontext;
570   msg = ps->pending_msgs_tail;
571   if (ps->send_force_disconnect==GNUNET_YES)
572   {
573 #if DEBUG_CONNECTIONS
574     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound forced to disconnect\n",ps);
575 #endif
576     return -1;
577   }
578
579   if (msg!=NULL)
580   {
581     if ((msg->size-msg->pos) <= max)
582     {
583       memcpy(buf,&msg->buf[msg->pos],(msg->size-msg->pos));
584       bytes_read = msg->size-msg->pos;
585       msg->pos+=(msg->size-msg->pos);
586     }
587     else
588     {
589       memcpy(buf,&msg->buf[msg->pos],max);
590       msg->pos+=max;
591       bytes_read = max;
592     }
593
594     if (msg->pos==msg->size)
595     {
596       if (NULL!=msg->transmit_cont)
597         msg->transmit_cont (msg->transmit_cont_cls,&pc->identity,GNUNET_OK);
598       remove_http_message(ps,msg);
599     }
600   }
601   return bytes_read;
602 }
603
604 /**
605  * Process GET or PUT request received via MHD.  For
606  * GET, queue response that will send back our pending
607  * messages.  For PUT, process incoming data and send
608  * to GNUnet core.  In either case, check if a session
609  * already exists and create a new one if not.
610  */
611 static int
612 mdh_access_cb (void *cls,
613                        struct MHD_Connection *mhd_connection,
614                        const char *url,
615                        const char *method,
616                        const char *version,
617                        const char *upload_data,
618                        size_t * upload_data_size, void **httpSessionCache)
619 {
620   struct Plugin *plugin = cls;
621   struct MHD_Response *response;
622   const union MHD_ConnectionInfo * conn_info;
623
624   struct sockaddr_in  *addrin;
625   struct sockaddr_in6 *addrin6;
626
627   char address[INET6_ADDRSTRLEN+14];
628   struct GNUNET_PeerIdentity pi_in;
629   size_t id_num = 0;
630
631   struct IPv4HttpAddress ipv4addr;
632   struct IPv6HttpAddress ipv6addr;
633
634   struct HTTP_PeerContext *pc;
635   struct Session *ps = NULL;
636   struct Session *ps_tmp = NULL;
637
638   int res = GNUNET_NO;
639   int send_error_to_client;
640   void * addr;
641   size_t addr_len;
642
643   GNUNET_assert(cls !=NULL);
644   send_error_to_client = GNUNET_NO;
645
646   if (NULL == *httpSessionCache)
647   {
648     /* check url for peer identity , if invalid send HTTP 404*/
649     size_t len = strlen(&url[1]);
650     char * peer = GNUNET_malloc(104+1);
651
652     if ((len>104) && (url[104]==';'))
653     {
654         char * id = GNUNET_malloc((len-104)+1);
655         strcpy(id,&url[105]);
656         memcpy(peer,&url[1],103);
657         peer[103] = '\0';
658         id_num = strtoul ( id, NULL , 10);
659         GNUNET_free(id);
660     }
661     res = GNUNET_CRYPTO_hash_from_string (peer, &(pi_in.hashPubKey));
662     GNUNET_free(peer);
663     if ( GNUNET_SYSERR == res )
664     {
665       response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
666       res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
667       MHD_destroy_response (response);
668       if (res == MHD_YES)
669         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, sent HTTP 1.1/404\n");
670       else
671         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, could not send error\n");
672       return res;
673     }
674   }
675   else
676   {
677     ps = *httpSessionCache;
678     pc = ps->peercontext;
679   }
680
681   if (NULL == *httpSessionCache)
682   {
683     /* get peer context */
684     pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &pi_in.hashPubKey);
685     /* Peer unknown */
686     if (pc==NULL)
687     {
688       pc = GNUNET_malloc(sizeof (struct HTTP_PeerContext));
689       pc->plugin = plugin;
690       pc->session_id_counter=1;
691       memcpy(&pc->identity, &pi_in, sizeof(struct GNUNET_PeerIdentity));
692       GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
693     }
694
695     conn_info = MHD_get_connection_info(mhd_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS );
696     /* Incoming IPv4 connection */
697     if ( AF_INET == conn_info->client_addr->sin_family)
698     {
699       addrin = conn_info->client_addr;
700       inet_ntop(addrin->sin_family, &(addrin->sin_addr),address,INET_ADDRSTRLEN);
701       memcpy(&ipv4addr.ipv4_addr,&(addrin->sin_addr),sizeof(struct in_addr));
702       ipv4addr.u_port = addrin->sin_port;
703       addr = &ipv4addr;
704       addr_len = sizeof(struct IPv4HttpAddress);
705     }
706     /* Incoming IPv6 connection */
707     if ( AF_INET6 == conn_info->client_addr->sin_family)
708     {
709       addrin6 = (struct sockaddr_in6 *) conn_info->client_addr;
710       inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr),address,INET6_ADDRSTRLEN);
711       memcpy(&ipv6addr.ipv6_addr,&(addrin6->sin6_addr),sizeof(struct in6_addr));
712       ipv6addr.u6_port = addrin6->sin6_port;
713       addr = &ipv6addr;
714       addr_len = sizeof(struct IPv6HttpAddress);
715     }
716
717
718     //ps = get_Session(plugin, pc, addr, addr_len);
719     ps = NULL;
720     /* only inbound sessions here */
721
722     ps_tmp = pc->head;
723     while (ps_tmp!=NULL)
724     {
725       if ((ps_tmp->direction==INBOUND) && (ps_tmp->session_id == id_num) && (id_num!=0))
726       {
727         if ((ps_tmp->recv_force_disconnect!=GNUNET_YES) && (ps_tmp->send_force_disconnect!=GNUNET_YES))
728         ps=ps_tmp;
729         break;
730       }
731       ps_tmp=ps_tmp->next;
732     }
733
734     if (ps==NULL)
735     {
736       ps = GNUNET_malloc(sizeof (struct Session));
737       ps->addr = GNUNET_malloc(addr_len);
738       memcpy(ps->addr,addr,addr_len);
739       ps->addrlen = addr_len;
740       ps->direction=INBOUND;
741       ps->pending_msgs_head = NULL;
742       ps->pending_msgs_tail = NULL;
743       ps->send_connected=GNUNET_NO;
744       ps->send_active=GNUNET_NO;
745       ps->recv_connected=GNUNET_NO;
746       ps->recv_active=GNUNET_NO;
747       ps->peercontext=pc;
748       ps->session_id =id_num;
749       ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id);
750       GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps);
751     }
752
753     *httpSessionCache = ps;
754     if (ps->msgtok==NULL)
755       ps->msgtok = GNUNET_SERVER_mst_create (&mhd_write_mst_cb, ps);
756
757     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: HTTP Daemon has new an incoming `%s' request from peer `%s' (`%s')\n",
758                 ps,
759                 method,
760                 GNUNET_i2s(&pc->identity),
761                 http_plugin_address_to_string(NULL, ps->addr, ps->addrlen));
762   }
763
764   /* Is it a PUT or a GET request */
765   if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
766   {
767     if (ps->recv_force_disconnect == GNUNET_YES)
768     {
769 #if DEBUG_CONNECTIONS
770       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connection was forced to disconnect\n",ps);
771 #endif
772       ps->recv_active = GNUNET_NO;
773       return MHD_NO;
774     }
775     if ((*upload_data_size == 0) && (ps->recv_active==GNUNET_NO))
776     {
777       ps->recv_endpoint = mhd_connection;
778       ps->recv_connected = GNUNET_YES;
779       ps->recv_active = GNUNET_YES;
780       ps->recv_force_disconnect = GNUNET_NO;
781 #if DEBUG_CONNECTIONS
782       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound PUT connection connected\n",ps);
783 #endif
784       return MHD_YES;
785     }
786
787     /* Transmission of all data complete */
788     if ((*upload_data_size == 0) && (ps->recv_active == GNUNET_YES))
789     {
790       response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
791       res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
792 #if DEBUG_CONNECTIONS
793       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Sent HTTP/1.1: 200 OK as PUT Response\n",ps);
794 #endif
795       MHD_destroy_response (response);
796       ps->recv_active=GNUNET_NO;
797       return MHD_YES;
798     }
799
800     /* Recieving data */
801     if ((*upload_data_size > 0) && (ps->recv_active == GNUNET_YES))
802     {
803       res = GNUNET_SERVER_mst_receive(ps->msgtok, ps, upload_data,*upload_data_size, GNUNET_NO, GNUNET_NO);
804       (*upload_data_size) = 0;
805       return MHD_YES;
806     }
807     else
808       return MHD_NO;
809   }
810   if ( 0 == strcmp (MHD_HTTP_METHOD_GET, method) )
811   {
812     if (ps->send_force_disconnect == GNUNET_YES)
813     {
814 #if DEBUG_CONNECTIONS
815       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound connection was  forced to disconnect\n",ps);
816 #endif
817       ps->send_active = GNUNET_NO;
818       return MHD_NO;
819     }
820           ps->send_connected = GNUNET_YES;
821           ps->send_active = GNUNET_YES;
822           ps->send_endpoint = mhd_connection;
823           ps->send_force_disconnect = GNUNET_NO;
824 #if DEBUG_CONNECTIONS
825           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound GET connection connected\n",ps);
826 #endif
827           response = MHD_create_response_from_callback(-1,32 * 1024, &mhd_send_callback, ps, NULL);
828           res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
829           MHD_destroy_response (response);
830           return MHD_YES;
831   }
832   return MHD_NO;
833 }
834
835
836 /**
837  * Call MHD to process pending ipv4 requests and then go back
838  * and schedule the next run.
839  */
840 static void http_server_daemon_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
841 /**
842  * Call MHD to process pending ipv6 requests and then go back
843  * and schedule the next run.
844  */
845 static void http_server_daemon_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
846
847 /**
848  * Function that queries MHD's select sets and
849  * starts the task waiting for them.
850  */
851 static GNUNET_SCHEDULER_TaskIdentifier
852 http_server_daemon_prepare (void * cls, struct MHD_Daemon *daemon_handle)
853 {
854   struct Plugin *plugin = cls;
855   GNUNET_SCHEDULER_TaskIdentifier ret;
856   fd_set rs;
857   fd_set ws;
858   fd_set es;
859   struct GNUNET_NETWORK_FDSet *wrs;
860   struct GNUNET_NETWORK_FDSet *wws;
861   struct GNUNET_NETWORK_FDSet *wes;
862   int max;
863   unsigned long long timeout;
864   int haveto;
865   struct GNUNET_TIME_Relative tv;
866
867   GNUNET_assert(cls !=NULL);
868   ret = GNUNET_SCHEDULER_NO_TASK;
869   FD_ZERO(&rs);
870   FD_ZERO(&ws);
871   FD_ZERO(&es);
872   wrs = GNUNET_NETWORK_fdset_create ();
873   wes = GNUNET_NETWORK_fdset_create ();
874   wws = GNUNET_NETWORK_fdset_create ();
875   max = -1;
876   GNUNET_assert (MHD_YES ==
877                  MHD_get_fdset (daemon_handle,
878                                 &rs,
879                                 &ws,
880                                 &es,
881                                 &max));
882   haveto = MHD_get_timeout (daemon_handle, &timeout);
883   if (haveto == MHD_YES)
884     tv.value = (uint64_t) timeout;
885   else
886     tv = GNUNET_TIME_UNIT_FOREVER_REL;
887   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max);
888   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max);
889   GNUNET_NETWORK_fdset_copy_native (wes, &es, max);
890   if (daemon_handle == plugin->http_server_daemon_v4)
891   {
892         if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
893         {
894                 GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4);
895                 plugin->http_server_daemon_v4 = GNUNET_SCHEDULER_NO_TASK;
896         }
897
898     ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
899                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
900                                        GNUNET_SCHEDULER_NO_TASK,
901                                        tv,
902                                        wrs,
903                                        wws,
904                                        &http_server_daemon_v4_run,
905                                        plugin);
906   }
907   if (daemon_handle == plugin->http_server_daemon_v6)
908   {
909         if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
910         {
911                 GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v6);
912                 plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
913         }
914
915     ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
916                                        GNUNET_SCHEDULER_PRIORITY_DEFAULT,
917                                        GNUNET_SCHEDULER_NO_TASK,
918                                        tv,
919                                        wrs,
920                                        wws,
921                                        &http_server_daemon_v6_run,
922                                        plugin);
923   }
924   GNUNET_NETWORK_fdset_destroy (wrs);
925   GNUNET_NETWORK_fdset_destroy (wws);
926   GNUNET_NETWORK_fdset_destroy (wes);
927   return ret;
928 }
929
930 /**
931  * Call MHD to process pending requests and then go back
932  * and schedule the next run.
933  */
934 static void http_server_daemon_v4_run (void *cls,
935                              const struct GNUNET_SCHEDULER_TaskContext *tc)
936 {
937   struct Plugin *plugin = cls;
938
939   GNUNET_assert(cls !=NULL);
940   plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
941
942   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
943     return;
944
945   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v4));
946   plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
947   return;
948 }
949
950
951 /**
952  * Call MHD to process pending requests and then go back
953  * and schedule the next run.
954  */
955 static void http_server_daemon_v6_run (void *cls,
956                              const struct GNUNET_SCHEDULER_TaskContext *tc)
957 {
958   struct Plugin *plugin = cls;
959
960   GNUNET_assert(cls !=NULL);
961   plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
962
963   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
964     return;
965
966   GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v6));
967   plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
968   return;
969 }
970
971 /**
972  * Function setting up curl handle and selecting message to send
973  * @param cls plugin
974  * @param ses session to send data to
975  * @param con connection
976  * @return bytes sent to peer
977  */
978 static ssize_t send_check_connections (void *cls, struct Session *ps);
979
980 static size_t curl_get_header_function( void *ptr, size_t size, size_t nmemb, void *stream)
981 {
982   struct Session * ps = stream;
983
984   char * tmp;
985   size_t len = size * nmemb;
986   long http_result = 0;
987   int res;
988   /* Getting last http result code */
989   if (ps->recv_connected==GNUNET_NO)
990   {
991     GNUNET_assert(NULL!=ps);
992     res = curl_easy_getinfo(ps->recv_endpoint, CURLINFO_RESPONSE_CODE, &http_result);
993     if (CURLE_OK == res)
994     {
995       if (http_result == 200)
996       {
997         ps->recv_connected = GNUNET_YES;
998         ps->recv_active = GNUNET_YES;
999 #if DEBUG_CONNECTIONS
1000         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: connected to recieve data\n",ps);
1001 #endif
1002         // Calling send_check_connections again since receive is established
1003         send_check_connections (ps->peercontext->plugin, ps);
1004       }
1005     }
1006   }
1007
1008   tmp = NULL;
1009   if ((size * nmemb) < SIZE_MAX)
1010     tmp = GNUNET_malloc (len+1);
1011
1012   if ((tmp != NULL) && (len > 0))
1013   {
1014     memcpy(tmp,ptr,len);
1015     if (len>=2)
1016     {
1017       if (tmp[len-2] == 13)
1018         tmp[len-2]= '\0';
1019     }
1020 #if DEBUG_HTTP
1021     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Header: `%s' %u \n",tmp, http_result);
1022 #endif
1023   }
1024   if (NULL != tmp)
1025     GNUNET_free (tmp);
1026
1027   return size * nmemb;
1028 }
1029
1030 static size_t curl_put_header_function( void *ptr, size_t size, size_t nmemb, void *stream)
1031 {
1032   struct Session * ps = stream;
1033
1034   char * tmp;
1035   size_t len = size * nmemb;
1036   long http_result = 0;
1037   int res;
1038
1039   /* Getting last http result code */
1040   GNUNET_assert(NULL!=ps);
1041   res = curl_easy_getinfo(ps->send_endpoint, CURLINFO_RESPONSE_CODE, &http_result);
1042   if (CURLE_OK == res)
1043   {
1044     if ((http_result == 100) && (ps->send_connected==GNUNET_NO))
1045     {
1046       ps->send_connected = GNUNET_YES;
1047       ps->send_active = GNUNET_YES;
1048 #if DEBUG_CONNECTIONS
1049       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: connected to send data\n",ps);
1050 #endif
1051     }
1052     if ((http_result == 200) && (ps->send_connected==GNUNET_YES))
1053     {
1054       ps->send_connected = GNUNET_NO;
1055       ps->send_active = GNUNET_NO;
1056 #if DEBUG_CONNECTIONS
1057       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: sending disconnected\n",ps);
1058 #endif
1059     }
1060   }
1061
1062   tmp = NULL;
1063   if ((size * nmemb) < SIZE_MAX)
1064     tmp = GNUNET_malloc (len+1);
1065
1066   if ((tmp != NULL) && (len > 0))
1067   {
1068     memcpy(tmp,ptr,len);
1069     if (len>=2)
1070     {
1071       if (tmp[len-2] == 13)
1072         tmp[len-2]= '\0';
1073     }
1074 #if DEBUG_HTTP
1075     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Header: `%s' %u \n",tmp, http_result);
1076 #endif
1077   }
1078   if (NULL != tmp)
1079     GNUNET_free (tmp);
1080
1081   return size * nmemb;
1082 }
1083
1084 /**
1085  * Callback method used with libcurl
1086  * Method is called when libcurl needs to read data during sending
1087  * @param stream pointer where to write data
1088  * @param size size of an individual element
1089  * @param nmemb count of elements that can be written to the buffer
1090  * @param ptr source pointer, passed to the libcurl handle
1091  * @return bytes written to stream
1092  */
1093 static size_t curl_send_cb(void *stream, size_t size, size_t nmemb, void *ptr)
1094 {
1095   struct Session * ps = ptr;
1096   struct HTTP_Message * msg = ps->pending_msgs_tail;
1097   size_t bytes_sent;
1098   size_t len;
1099
1100   if (ps->send_active == GNUNET_NO)
1101         return CURL_READFUNC_PAUSE;
1102
1103
1104   if ((ps->pending_msgs_tail == NULL) && (ps->send_active == GNUNET_YES))
1105   {
1106 #if DEBUG_CONNECTIONS
1107     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: No Message to send, pausing connection\n",ps);
1108 #endif
1109     ps->send_active = GNUNET_NO;
1110     return CURL_READFUNC_PAUSE;
1111   }
1112
1113   msg = ps->pending_msgs_tail;
1114   /* data to send */
1115   if (msg->pos < msg->size)
1116   {
1117     /* data fit in buffer */
1118     if ((msg->size - msg->pos) <= (size * nmemb))
1119     {
1120       len = (msg->size - msg->pos);
1121       memcpy(stream, &msg->buf[msg->pos], len);
1122       msg->pos += len;
1123       bytes_sent = len;
1124     }
1125     else
1126     {
1127       len = size*nmemb;
1128       memcpy(stream, &msg->buf[msg->pos], len);
1129       msg->pos += len;
1130       bytes_sent = len;
1131     }
1132   }
1133   /* no data to send */
1134   else
1135   {
1136     bytes_sent = 0;
1137   }
1138
1139   if ( msg->pos == msg->size)
1140   {
1141 #if DEBUG_CONNECTIONS
1142     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Message with %u bytes sent, removing message from queue \n",ps, msg->pos);
1143 #endif
1144     /* Calling transmit continuation  */
1145     if (( NULL != ps->pending_msgs_tail) && (NULL != ps->pending_msgs_tail->transmit_cont))
1146       msg->transmit_cont (ps->pending_msgs_tail->transmit_cont_cls,&(ps->peercontext)->identity,GNUNET_OK);
1147     remove_http_message(ps, msg);
1148   }
1149   return bytes_sent;
1150 }
1151
1152 /**
1153 * Callback method used with libcurl
1154 * Method is called when libcurl needs to write data during sending
1155 * @param stream pointer where to write data
1156 * @param size size of an individual element
1157 * @param nmemb count of elements that can be written to the buffer
1158 * @param ptr destination pointer, passed to the libcurl handle
1159 * @return bytes read from stream
1160 */
1161 static size_t curl_receive_cb( void *stream, size_t size, size_t nmemb, void *ptr)
1162 {
1163   struct Session * ps = ptr;
1164 #if DEBUG_CONNECTIONS
1165   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: %u bytes received\n",ps, size*nmemb);
1166 #endif
1167   GNUNET_SERVER_mst_receive(ps->msgtok, ps, stream, size*nmemb, GNUNET_NO, GNUNET_NO);
1168   return (size * nmemb);
1169
1170 }
1171
1172 /**
1173  * Function setting up file descriptors and scheduling task to run
1174  * @param cls closure
1175  * @param ses session to send data to
1176  * @param
1177  */
1178 static int curl_schedule(void *cls );
1179
1180
1181
1182 /**
1183  * Function setting up curl handle and selecting message to send
1184  * @param cls plugin
1185  * @param ses session to send data to
1186  * @param con connection
1187  * @return GNUNET_SYSERR on failure, GNUNET_NO if connecting, GNUNET_YES if ok
1188  */
1189 static ssize_t send_check_connections (void *cls, struct Session *ps)
1190 {
1191   struct Plugin *plugin = cls;
1192   CURLMcode mret;
1193   struct HTTP_Message * msg;
1194
1195   struct GNUNET_TIME_Relative timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
1196
1197   GNUNET_assert(cls !=NULL);
1198
1199   if (ps->direction == OUTBOUND)
1200   {
1201     /* RECV DIRECTION */
1202     /* Check if session is connected to receive data, otherwise connect to peer */
1203     if (ps->recv_connected == GNUNET_NO)
1204     {
1205         if (ps->recv_endpoint == NULL)
1206         {
1207           ps->recv_endpoint = curl_easy_init();
1208 #if DEBUG_CURL
1209         curl_easy_setopt(ps->recv_endpoint, CURLOPT_VERBOSE, 1L);
1210 #endif
1211         curl_easy_setopt(ps->recv_endpoint, CURLOPT_URL, ps->url);
1212         curl_easy_setopt(ps->recv_endpoint, CURLOPT_HEADERFUNCTION, &curl_get_header_function);
1213         curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEHEADER, ps);
1214         curl_easy_setopt(ps->recv_endpoint, CURLOPT_READFUNCTION, curl_send_cb);
1215         curl_easy_setopt(ps->recv_endpoint, CURLOPT_READDATA, ps);
1216         curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEFUNCTION, curl_receive_cb);
1217         curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEDATA, ps);
1218         curl_easy_setopt(ps->recv_endpoint, CURLOPT_TIMEOUT, (long) timeout.value);
1219         curl_easy_setopt(ps->recv_endpoint, CURLOPT_PRIVATE, ps);
1220         curl_easy_setopt(ps->recv_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
1221         curl_easy_setopt(ps->recv_endpoint, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE);
1222
1223         mret = curl_multi_add_handle(plugin->multi_handle, ps->recv_endpoint);
1224         if (mret != CURLM_OK)
1225         {
1226           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1227                                           _("Connection: %X: %s failed at %s:%d: `%s'\n"),
1228                                           ps,
1229                       "curl_multi_add_handle", __FILE__, __LINE__,
1230                       curl_multi_strerror (mret));
1231           return GNUNET_SYSERR;
1232         }
1233         if (curl_schedule (plugin) == GNUNET_SYSERR)
1234                 return GNUNET_SYSERR;
1235 #if DEBUG_CONNECTIONS
1236         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound not connected, initiating connection\n",ps);
1237 #endif
1238       }
1239     }
1240
1241     /* waiting for receive direction */
1242     if (ps->recv_connected==GNUNET_NO)
1243       return GNUNET_NO;
1244
1245     /* SEND DIRECTION */
1246     /* Check if session is connected to send data, otherwise connect to peer */
1247     if ((ps->send_connected == GNUNET_YES) && (ps->send_endpoint!= NULL))
1248     {
1249       if (ps->send_active == GNUNET_YES)
1250       {
1251 #if DEBUG_CONNECTIONS
1252         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound active, enqueueing message\n",ps);
1253 #endif
1254         return GNUNET_YES;
1255       }
1256       if (ps->send_active == GNUNET_NO)
1257       {
1258 #if DEBUG_CONNECTIONS
1259         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound paused, unpausing existing connection and enqueueing message\n",ps);
1260 #endif
1261         if (CURLE_OK == curl_easy_pause(ps->send_endpoint,CURLPAUSE_CONT))
1262         {
1263                         ps->send_active=GNUNET_YES;
1264                         return GNUNET_YES;
1265         }
1266         else
1267                 return GNUNET_SYSERR;
1268       }
1269     }
1270     /* not connected, initiate connection */
1271     if ((ps->send_connected==GNUNET_NO) && (NULL == ps->send_endpoint))
1272     {
1273         ps->send_endpoint = curl_easy_init();
1274                 GNUNET_assert (ps->send_endpoint != NULL);
1275                 GNUNET_assert (NULL != ps->pending_msgs_tail);
1276 #if DEBUG_CONNECTIONS
1277                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound not connected, initiating connection\n",ps);
1278 #endif
1279                 ps->send_active = GNUNET_NO;
1280                 msg = ps->pending_msgs_tail;
1281
1282 #if DEBUG_CURL
1283                 curl_easy_setopt(ps->send_endpoint, CURLOPT_VERBOSE, 1L);
1284 #endif
1285                 curl_easy_setopt(ps->send_endpoint, CURLOPT_URL, ps->url);
1286                 curl_easy_setopt(ps->send_endpoint, CURLOPT_PUT, 1L);
1287                 curl_easy_setopt(ps->send_endpoint, CURLOPT_HEADERFUNCTION, &curl_put_header_function);
1288                 curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEHEADER, ps);
1289                 curl_easy_setopt(ps->send_endpoint, CURLOPT_READFUNCTION, curl_send_cb);
1290                 curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps);
1291                 curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEFUNCTION, curl_receive_cb);
1292                 curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps);
1293                 curl_easy_setopt(ps->send_endpoint, CURLOPT_TIMEOUT, (long) timeout.value);
1294                 curl_easy_setopt(ps->send_endpoint, CURLOPT_PRIVATE, ps);
1295                 curl_easy_setopt(ps->send_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
1296                 curl_easy_setopt(ps->send_endpoint, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE);
1297
1298                 mret = curl_multi_add_handle(plugin->multi_handle, ps->send_endpoint);
1299                 if (mret != CURLM_OK)
1300                 {
1301                   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1302                                           _("Connection: %X: %s failed at %s:%d: `%s'\n"),
1303                                           ps,
1304                                           "curl_multi_add_handle", __FILE__, __LINE__,
1305                                           curl_multi_strerror (mret));
1306                   return GNUNET_SYSERR;
1307                 }
1308     }
1309     if (curl_schedule (plugin) == GNUNET_SYSERR)
1310         return GNUNET_SYSERR;
1311     return GNUNET_YES;
1312   }
1313   if (ps->direction == INBOUND)
1314   {
1315     GNUNET_assert (NULL != ps->pending_msgs_tail);
1316     if ((ps->recv_connected==GNUNET_YES) && (ps->send_connected==GNUNET_YES) &&
1317         (ps->recv_force_disconnect==GNUNET_NO) && (ps->recv_force_disconnect==GNUNET_NO))
1318         return GNUNET_YES;
1319   }
1320   return GNUNET_SYSERR;
1321 }
1322
1323 static void curl_perform (void *cls,
1324              const struct GNUNET_SCHEDULER_TaskContext *tc)
1325 {
1326   struct Plugin *plugin = cls;
1327   static unsigned int handles_last_run;
1328   int running;
1329   struct CURLMsg *msg;
1330   CURLMcode mret;
1331   struct Session *ps = NULL;
1332   struct HTTP_PeerContext *pc = NULL;
1333   struct HTTP_Message * cur_msg = NULL;
1334   long http_result;
1335
1336   GNUNET_assert(cls !=NULL);
1337
1338   plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
1339   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1340     return;
1341
1342   do
1343     {
1344       running = 0;
1345       mret = curl_multi_perform (plugin->multi_handle, &running);
1346       if ((running < handles_last_run) && (running>0))
1347         {
1348           do
1349             {
1350
1351               msg = curl_multi_info_read (plugin->multi_handle, &running);
1352               if (running == 0)
1353                   break;
1354               /* get session for affected curl handle */
1355               GNUNET_assert ( msg->easy_handle != NULL );
1356               curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char *) &ps);
1357               GNUNET_assert ( ps != NULL );
1358               pc = ps->peercontext;
1359               GNUNET_assert ( pc != NULL );
1360               switch (msg->msg)
1361                 {
1362
1363                 case CURLMSG_DONE:
1364                   if ( (msg->data.result != CURLE_OK) &&
1365                        (msg->data.result != CURLE_GOT_NOTHING) )
1366                   {
1367                     /* sending msg failed*/
1368                     if (msg->easy_handle == ps->send_endpoint)
1369                     {
1370 #if DEBUG_CONNECTIONS
1371                       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1372                                  _("Connection %X: HTTP PUT to peer `%s' (`%s') failed: `%s' `%s'\n"),
1373                                  ps,
1374                                  GNUNET_i2s(&pc->identity),
1375                                  http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1376                                  "curl_multi_perform",
1377                                  curl_easy_strerror (msg->data.result));
1378 #endif
1379                       ps->send_connected = GNUNET_NO;
1380                       ps->send_active = GNUNET_NO;
1381                       curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint);
1382                       curl_easy_cleanup(ps->send_endpoint);
1383                       ps->send_endpoint=NULL;
1384                       cur_msg = ps->pending_msgs_tail;
1385                       if (( NULL != cur_msg) && ( NULL != cur_msg->transmit_cont))
1386                         cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR);
1387                     }
1388                     /* GET connection failed */
1389                     if (msg->easy_handle == ps->recv_endpoint)
1390                     {
1391 #if DEBUG_CONNECTIONS
1392                       GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1393                            _("Connection %X: HTTP GET to peer `%s' (`%s') failed: `%s' `%s'\n"),
1394                            ps,
1395                            GNUNET_i2s(&pc->identity),
1396                            http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1397                            "curl_multi_perform",
1398                            curl_easy_strerror (msg->data.result));
1399 #endif
1400                       ps->recv_connected = GNUNET_NO;
1401                       ps->recv_active = GNUNET_NO;
1402                       curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint);
1403                       curl_easy_cleanup(ps->recv_endpoint);
1404                       ps->recv_endpoint=NULL;
1405                     }
1406                   }
1407                   else
1408                   {
1409                     if (msg->easy_handle == ps->send_endpoint)
1410                     {
1411                       GNUNET_assert (CURLE_OK == curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &http_result));
1412 #if DEBUG_CONNECTIONS
1413                       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1414                                   "Connection %X: HTTP PUT connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1415                                    ps,
1416                                    GNUNET_i2s(&pc->identity),
1417                                    http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1418                                    http_result);
1419 #endif
1420                       /* Calling transmit continuation  */
1421                       cur_msg = ps->pending_msgs_tail;
1422                       if (( NULL != cur_msg) && (NULL != cur_msg->transmit_cont))
1423                       {
1424                         /* HTTP 1xx : Last message before here was informational */
1425                         if ((http_result >=100) && (http_result < 200))
1426                           cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK);
1427                         /* HTTP 2xx: successful operations */
1428                         if ((http_result >=200) && (http_result < 300))
1429                           cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK);
1430                         /* HTTP 3xx..5xx: error */
1431                         if ((http_result >=300) && (http_result < 600))
1432                           cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR);
1433                       }
1434                       ps->send_connected = GNUNET_NO;
1435                       ps->send_active = GNUNET_NO;
1436                       curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint);
1437                       curl_easy_cleanup(ps->send_endpoint);
1438                       ps->send_endpoint =NULL;
1439                     }
1440                     if (msg->easy_handle == ps->recv_endpoint)
1441                     {
1442 #if DEBUG_CONNECTIONS
1443                       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1444                                   "Connection %X: HTTP GET connection to peer `%s' (`%s') was closed with HTTP code %u\n",
1445                                    ps,
1446                                    GNUNET_i2s(&pc->identity),
1447                                    http_plugin_address_to_string(NULL, ps->addr, ps->addrlen),
1448                                    http_result);
1449 #endif
1450                       ps->recv_connected = GNUNET_NO;
1451                       ps->recv_active = GNUNET_NO;
1452                       curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint);
1453                       curl_easy_cleanup(ps->recv_endpoint);
1454                       ps->recv_endpoint=NULL;
1455                     }
1456                   }
1457                   if ((ps->recv_connected == GNUNET_NO) && (ps->send_connected == GNUNET_NO))
1458                     remove_session (pc, ps, GNUNET_YES, GNUNET_SYSERR);
1459                   break;
1460                 default:
1461                   break;
1462                 }
1463
1464             }
1465           while ( (running > 0) );
1466         }
1467       handles_last_run = running;
1468     }
1469   while (mret == CURLM_CALL_MULTI_PERFORM);
1470   curl_schedule(plugin);
1471 }
1472
1473
1474 /**
1475  * Function setting up file descriptors and scheduling task to run
1476  * @param ses session to send data to
1477  * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
1478  */
1479 static int curl_schedule(void *cls )
1480 {
1481   struct Plugin *plugin = cls;
1482   fd_set rs;
1483   fd_set ws;
1484   fd_set es;
1485   int max;
1486   struct GNUNET_NETWORK_FDSet *grs;
1487   struct GNUNET_NETWORK_FDSet *gws;
1488   long to;
1489   CURLMcode mret;
1490
1491   GNUNET_assert(cls !=NULL);
1492
1493   /* Cancel previous scheduled task */
1494   if (plugin->http_curl_task !=  GNUNET_SCHEDULER_NO_TASK)
1495   {
1496           GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task);
1497           plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
1498   }
1499
1500   max = -1;
1501   FD_ZERO (&rs);
1502   FD_ZERO (&ws);
1503   FD_ZERO (&es);
1504   mret = curl_multi_fdset (plugin->multi_handle, &rs, &ws, &es, &max);
1505   if (mret != CURLM_OK)
1506     {
1507       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1508                   _("%s failed at %s:%d: `%s'\n"),
1509                   "curl_multi_fdset", __FILE__, __LINE__,
1510                   curl_multi_strerror (mret));
1511       return GNUNET_SYSERR;
1512     }
1513   mret = curl_multi_timeout (plugin->multi_handle, &to);
1514   if (mret != CURLM_OK)
1515     {
1516       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1517                   _("%s failed at %s:%d: `%s'\n"),
1518                   "curl_multi_timeout", __FILE__, __LINE__,
1519                   curl_multi_strerror (mret));
1520       return GNUNET_SYSERR;
1521     }
1522
1523   grs = GNUNET_NETWORK_fdset_create ();
1524   gws = GNUNET_NETWORK_fdset_create ();
1525   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1526   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1527   plugin->http_curl_task = GNUNET_SCHEDULER_add_select (plugin->env->sched,
1528                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1529                                    GNUNET_SCHEDULER_NO_TASK,
1530                                    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 0),
1531                                    grs,
1532                                    gws,
1533                                    &curl_perform,
1534                                    plugin);
1535   GNUNET_NETWORK_fdset_destroy (gws);
1536   GNUNET_NETWORK_fdset_destroy (grs);
1537   return GNUNET_OK;
1538 }
1539
1540
1541 /**
1542  * Function that can be used by the transport service to transmit
1543  * a message using the plugin.   Note that in the case of a
1544  * peer disconnecting, the continuation MUST be called
1545  * prior to the disconnect notification itself.  This function
1546  * will be called with this peer's HELLO message to initiate
1547  * a fresh connection to another peer.
1548  *
1549  * @param cls closure
1550  * @param target who should receive this message
1551  * @param msgbuf the message to transmit
1552  * @param msgbuf_size number of bytes in 'msgbuf'
1553  * @param priority how important is the message (most plugins will
1554  *                 ignore message priority and just FIFO)
1555  * @param timeout how long to wait at most for the transmission (does not
1556  *                require plugins to discard the message after the timeout,
1557  *                just advisory for the desired delay; most plugins will ignore
1558  *                this as well)
1559  * @param session which session must be used (or NULL for "any")
1560  * @param addr the address to use (can be NULL if the plugin
1561  *                is "on its own" (i.e. re-use existing TCP connection))
1562  * @param addrlen length of the address in bytes
1563  * @param force_address GNUNET_YES if the plugin MUST use the given address,
1564  *                GNUNET_NO means the plugin may use any other address and
1565  *                GNUNET_SYSERR means that only reliable existing
1566  *                bi-directional connections should be used (regardless
1567  *                of address)
1568  * @param cont continuation to call once the message has
1569  *        been transmitted (or if the transport is ready
1570  *        for the next transmission call; or if the
1571  *        peer disconnected...); can be NULL
1572  * @param cont_cls closure for cont
1573  * @return number of bytes used (on the physical network, with overheads);
1574  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
1575  *         and does NOT mean that the message was not transmitted (DV)
1576  */
1577 static ssize_t
1578 http_plugin_send (void *cls,
1579                   const struct GNUNET_PeerIdentity *target,
1580                   const char *msgbuf,
1581                   size_t msgbuf_size,
1582                   unsigned int priority,
1583                   struct GNUNET_TIME_Relative to,
1584                   struct Session *session,
1585                   const void *addr,
1586                   size_t addrlen,
1587                   int force_address,
1588                   GNUNET_TRANSPORT_TransmitContinuation cont,
1589                   void *cont_cls)
1590 {
1591   struct Plugin *plugin = cls;
1592   struct HTTP_Message *msg;
1593
1594   struct HTTP_PeerContext * pc;
1595   struct Session * ps = NULL;
1596   struct Session * ps_tmp = NULL;
1597
1598   GNUNET_assert(cls !=NULL);
1599
1600   char * force = GNUNET_malloc(40);
1601   if (force_address == GNUNET_YES)
1602     strcpy(force,"forced addr.");
1603   if (force_address == GNUNET_NO)
1604     strcpy(force,"any addr.");
1605   if (force_address == GNUNET_SYSERR)
1606     strcpy(force,"reliable bi-direc. address addr.");
1607   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Transport tells me to send %u bytes to `%s' using %s (%s) and session: %X\n",
1608                                       msgbuf_size,
1609                                       GNUNET_i2s(target),
1610                                       force,
1611                                       http_plugin_address_to_string(NULL, addr, addrlen),
1612                                       session);
1613   GNUNET_free(force);
1614
1615   pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey);
1616   /* Peer unknown */
1617   if (pc==NULL)
1618   {
1619     pc = GNUNET_malloc(sizeof (struct HTTP_PeerContext));
1620     pc->plugin = plugin;
1621     pc->session_id_counter=1;
1622     memcpy(&pc->identity, target, sizeof(struct GNUNET_PeerIdentity));
1623     GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
1624   }
1625
1626   /* Search for existing session using the passed address */
1627   if  ((addr!=NULL) && (addrlen != 0))
1628   {
1629     ps = get_Session(plugin, pc, addr, addrlen);
1630   }
1631   if (ps != NULL)
1632     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Found existing connection to peer %s with given address, using %X\n", GNUNET_i2s(target), ps);
1633
1634   /* Search for existing session using the passed session */
1635   if ((ps==NULL) && (force_address != GNUNET_YES))
1636   {
1637     ps_tmp = pc->head;
1638     while (ps_tmp!=NULL)
1639     {
1640       if ((ps_tmp==session) && (ps_tmp->recv_force_disconnect==GNUNET_NO) && (ps_tmp->send_force_disconnect==GNUNET_NO) &&
1641           (ps_tmp->recv_connected==GNUNET_YES) && (ps_tmp->send_connected==GNUNET_YES))
1642       {
1643         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Found existing connection to peer %s with given session, using inbound session %X\n", GNUNET_i2s(target), ps_tmp);
1644         ps = ps_tmp;
1645         break;
1646       }
1647       ps_tmp=ps_tmp->next;
1648     }
1649   }
1650
1651   /* session not existing, address not forced -> looking for other session */
1652   if ((ps==NULL) && (force_address != GNUNET_YES))
1653   {
1654     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection, but free to choose existing, searching for existing connection to peer %s\n", GNUNET_i2s(target));
1655     /* Choosing different session to peer when possible */
1656     struct Session * tmp = pc->head;
1657     while (tmp!=NULL)
1658     {
1659       if ((tmp->recv_connected) && (tmp->send_connected) && (tmp->recv_force_disconnect==GNUNET_NO) && (tmp->send_force_disconnect==GNUNET_NO))
1660       {
1661         ps = tmp;
1662       }
1663       tmp = tmp->next;
1664     }
1665     if (ps != NULL)
1666      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection to peer %s, selected connection %X\n", GNUNET_i2s(target),ps);
1667     else
1668       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection to peer %s, no connection found\n", GNUNET_i2s(target));
1669   }
1670
1671   /* session not existing, but address forced -> creating new session */
1672   if ((ps==NULL) || ((ps==NULL) && (force_address == GNUNET_YES)))
1673   {
1674     if ((addr!=NULL) && (addrlen!=0))
1675     {
1676       if (force_address == GNUNET_YES)
1677         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection & forced address: creating new connection to peer %s\n", GNUNET_i2s(target));
1678       if (force_address != GNUNET_YES)
1679         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection: creating new connection to peer %s\n", GNUNET_i2s(target));
1680
1681       ps = GNUNET_malloc(sizeof (struct Session));
1682       if ((addrlen!=0) && (addr!=NULL))
1683       {
1684       ps->addr = GNUNET_malloc(addrlen);
1685       memcpy(ps->addr,addr,addrlen);
1686       ps->addrlen = addrlen;
1687       }
1688       else
1689       {
1690         ps->addr = NULL;
1691         ps->addrlen = 0;
1692       }
1693       ps->direction=OUTBOUND;
1694       ps->recv_connected = GNUNET_NO;
1695       ps->recv_force_disconnect = GNUNET_NO;
1696       ps->send_connected = GNUNET_NO;
1697       ps->send_force_disconnect = GNUNET_NO;
1698       ps->pending_msgs_head = NULL;
1699       ps->pending_msgs_tail = NULL;
1700       ps->peercontext=pc;
1701       ps->session_id = pc->session_id_counter;
1702       pc->session_id_counter++;
1703       ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id);
1704       if (ps->msgtok == NULL)
1705         ps->msgtok = GNUNET_SERVER_mst_create (&curl_receive_mst_cb, ps);
1706       GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps);
1707     }
1708     else
1709     {
1710       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing session & and no address given: no way to send this message to peer `%s'!\n", GNUNET_i2s(target));
1711       return GNUNET_SYSERR;
1712     }
1713   }
1714
1715   /* create msg */
1716   msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
1717   msg->next = NULL;
1718   msg->size = msgbuf_size;
1719   msg->pos = 0;
1720   msg->buf = (char *) &msg[1];
1721   msg->transmit_cont = cont;
1722   msg->transmit_cont_cls = cont_cls;
1723   memcpy (msg->buf,msgbuf, msgbuf_size);
1724   GNUNET_CONTAINER_DLL_insert(ps->pending_msgs_head,ps->pending_msgs_tail,msg);
1725
1726   if (send_check_connections (plugin, ps) != GNUNET_SYSERR)
1727           return msg->size;
1728   else
1729           return GNUNET_SYSERR;
1730 }
1731
1732
1733
1734 /**
1735  * Function that can be used to force the plugin to disconnect
1736  * from the given peer and cancel all previous transmissions
1737  * (and their continuationc).
1738  *
1739  * @param cls closure
1740  * @param target peer from which to disconnect
1741  */
1742 static void
1743 http_plugin_disconnect (void *cls,
1744                             const struct GNUNET_PeerIdentity *target)
1745 {
1746
1747
1748   struct Plugin *plugin = cls;
1749   struct HTTP_PeerContext *pc = NULL;
1750   struct Session *ps = NULL;
1751   //struct Session *tmp = NULL;
1752
1753   pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey);
1754   if (pc==NULL)
1755     return;
1756   ps = pc->head;
1757
1758   while (ps!=NULL)
1759   {
1760     /* Telling transport that session is getting disconnected */
1761     plugin->env->session_end(plugin, target, ps);
1762     if (ps->direction==OUTBOUND)
1763     {
1764       if (ps->send_endpoint!=NULL)
1765       {
1766         //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint));
1767         //curl_easy_cleanup(ps->send_endpoint);
1768         //ps->send_endpoint=NULL;
1769         ps->send_force_disconnect = GNUNET_YES;
1770       }
1771       if (ps->recv_endpoint!=NULL)
1772       {
1773        //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint));
1774        //curl_easy_cleanup(ps->recv_endpoint);
1775        //ps->recv_endpoint=NULL;
1776        ps->recv_force_disconnect = GNUNET_YES;
1777       }
1778     }
1779
1780     if (ps->direction==INBOUND)
1781     {
1782       ps->recv_force_disconnect = GNUNET_YES;
1783       ps->send_force_disconnect = GNUNET_YES;
1784     }
1785
1786     while (ps->pending_msgs_head!=NULL)
1787     {
1788       remove_http_message(ps, ps->pending_msgs_head);
1789     }
1790     ps->recv_active = GNUNET_NO;
1791     ps->send_active = GNUNET_NO;
1792     ps=ps->next;
1793   }
1794 }
1795
1796
1797 /**
1798  * Convert the transports address to a nice, human-readable
1799  * format.
1800  *
1801  * @param cls closure
1802  * @param type name of the transport that generated the address
1803  * @param addr one of the addresses of the host, NULL for the last address
1804  *        the specific address format depends on the transport
1805  * @param addrlen length of the address
1806  * @param numeric should (IP) addresses be displayed in numeric form?
1807  * @param timeout after how long should we give up?
1808  * @param asc function to call on each string
1809  * @param asc_cls closure for asc
1810  */
1811 static void
1812 http_plugin_address_pretty_printer (void *cls,
1813                                         const char *type,
1814                                         const void *addr,
1815                                         size_t addrlen,
1816                                         int numeric,
1817                                         struct GNUNET_TIME_Relative timeout,
1818                                         GNUNET_TRANSPORT_AddressStringCallback
1819                                         asc, void *asc_cls)
1820 {
1821   const struct IPv4HttpAddress *t4;
1822   const struct IPv6HttpAddress *t6;
1823   struct sockaddr_in a4;
1824   struct sockaddr_in6 a6;
1825   char * address;
1826   char * ret;
1827   unsigned int port;
1828   unsigned int res;
1829
1830   GNUNET_assert(cls !=NULL);
1831   if (addrlen == sizeof (struct IPv6HttpAddress))
1832   {
1833     address = GNUNET_malloc (INET6_ADDRSTRLEN);
1834     t6 = addr;
1835     a6.sin6_addr = t6->ipv6_addr;
1836     inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
1837     port = ntohs(t6->u6_port);
1838   }
1839   else if (addrlen == sizeof (struct IPv4HttpAddress))
1840   {
1841     address = GNUNET_malloc (INET_ADDRSTRLEN);
1842     t4 = addr;
1843     a4.sin_addr.s_addr =  t4->ipv4_addr;
1844     inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
1845     port = ntohs(t4->u_port);
1846   }
1847   else
1848   {
1849     /* invalid address */
1850     GNUNET_break_op (0);
1851     asc (asc_cls, NULL);
1852     return;
1853   }
1854   res = GNUNET_asprintf(&ret,"http://%s:%u/",address,port);
1855   GNUNET_free (address);
1856   GNUNET_assert(res != 0);
1857
1858   asc (asc_cls, ret);
1859 }
1860
1861
1862
1863 /**
1864  * Another peer has suggested an address for this
1865  * peer and transport plugin.  Check that this could be a valid
1866  * address.  If so, consider adding it to the list
1867  * of addresses.
1868  *
1869  * @param cls closure
1870  * @param addr pointer to the address
1871  * @param addrlen length of addr
1872  * @return GNUNET_OK if this is a plausible address for this peer
1873  *         and transport
1874  */
1875 static int
1876 http_plugin_address_suggested (void *cls,
1877                                const void *addr, size_t addrlen)
1878 {
1879   struct Plugin *plugin = cls;
1880   struct IPv4HttpAddress *v4;
1881   struct IPv6HttpAddress *v6;
1882   unsigned int port;
1883
1884   GNUNET_assert(cls !=NULL);
1885   if ((addrlen != sizeof (struct IPv4HttpAddress)) &&
1886       (addrlen != sizeof (struct IPv6HttpAddress)))
1887     {
1888       return GNUNET_SYSERR;
1889     }
1890   if (addrlen == sizeof (struct IPv4HttpAddress))
1891     {
1892       v4 = (struct IPv4HttpAddress *) addr;
1893       /* Not skipping loopback
1894       if (INADDR_LOOPBACK == ntohl(v4->ipv4_addr))
1895       {
1896         return GNUNET_SYSERR;
1897       } */
1898       port = ntohs (v4->u_port);
1899       if (port != plugin->port_inbound)
1900       {
1901         return GNUNET_SYSERR;
1902       }
1903     }
1904   if (addrlen == sizeof (struct IPv6HttpAddress))
1905     {
1906       v6 = (struct IPv6HttpAddress *) addr;
1907       if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
1908         {
1909           return GNUNET_SYSERR;
1910         }
1911       port = ntohs (v6->u6_port);
1912       if (port != plugin->port_inbound)
1913       {
1914         return GNUNET_SYSERR;
1915       }
1916     }
1917
1918   return GNUNET_OK;
1919 }
1920
1921
1922 /**
1923  * Function called for a quick conversion of the binary address to
1924  * a numeric address.  Note that the caller must not free the
1925  * address and that the next call to this function is allowed
1926  * to override the address again.
1927  *
1928  * @param cls closure
1929  * @param addr binary address
1930  * @param addrlen length of the address
1931  * @return string representing the same address
1932  */
1933 static const char*
1934 http_plugin_address_to_string (void *cls,
1935                                    const void *addr,
1936                                    size_t addrlen)
1937 {
1938   const struct IPv4HttpAddress *t4;
1939   const struct IPv6HttpAddress *t6;
1940   struct sockaddr_in a4;
1941   struct sockaddr_in6 a6;
1942   char * address;
1943   char * ret;
1944   uint16_t port;
1945   unsigned int res;
1946
1947   if (addrlen == sizeof (struct IPv6HttpAddress))
1948     {
1949       address = GNUNET_malloc (INET6_ADDRSTRLEN);
1950       t6 = addr;
1951       a6.sin6_addr = t6->ipv6_addr;
1952       inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
1953       port = ntohs(t6->u6_port);
1954     }
1955   else if (addrlen == sizeof (struct IPv4HttpAddress))
1956     {
1957       address = GNUNET_malloc (INET_ADDRSTRLEN);
1958       t4 = addr;
1959       a4.sin_addr.s_addr =  t4->ipv4_addr;
1960       inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
1961       port = ntohs(t4->u_port);
1962     }
1963   else
1964     {
1965       /* invalid address */
1966       return NULL;
1967     }
1968   res = GNUNET_asprintf(&ret,"%s:%u",address,port);
1969   GNUNET_free (address);
1970   GNUNET_assert(res != 0);
1971   return ret;
1972 }
1973
1974 /**
1975  * Add the IP of our network interface to the list of
1976  * our external IP addresses.
1977  *
1978  * @param cls the 'struct Plugin*'
1979  * @param name name of the interface
1980  * @param isDefault do we think this may be our default interface
1981  * @param addr address of the interface
1982  * @param addrlen number of bytes in addr
1983  * @return GNUNET_OK to continue iterating
1984  */
1985 static int
1986 process_interfaces (void *cls,
1987                     const char *name,
1988                     int isDefault,
1989                     const struct sockaddr *addr, socklen_t addrlen)
1990 {
1991   struct Plugin *plugin = cls;
1992   struct IPv4HttpAddress * t4;
1993   struct IPv6HttpAddress * t6;
1994   int af;
1995
1996   GNUNET_assert(cls !=NULL);
1997   af = addr->sa_family;
1998   if (af == AF_INET)
1999     {
2000       t4 = GNUNET_malloc(sizeof(struct IPv4HttpAddress));
2001       /* Not skipping loopback addresses
2002       if (INADDR_LOOPBACK == ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr))
2003       {
2004
2005         return GNUNET_OK;
2006       }
2007       */
2008       t4->ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
2009       t4->u_port = htons (plugin->port_inbound);
2010       plugin->env->notify_address(plugin->env->cls,"http",t4, sizeof (struct IPv4HttpAddress), GNUNET_TIME_UNIT_FOREVER_REL);
2011
2012     }
2013   else if (af == AF_INET6)
2014     {
2015       t6 = GNUNET_malloc(sizeof(struct IPv6HttpAddress));
2016       if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
2017         {
2018           /* skip link local addresses */
2019           return GNUNET_OK;
2020         }
2021       /* Not skipping loopback addresses
2022       if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr))
2023         {
2024
2025           return GNUNET_OK;
2026         }
2027       */
2028       memcpy (&t6->ipv6_addr,
2029               &((struct sockaddr_in6 *) addr)->sin6_addr,
2030               sizeof (struct in6_addr));
2031       t6->u6_port = htons (plugin->port_inbound);
2032       plugin->env->notify_address(plugin->env->cls,"http",t6,sizeof (struct IPv6HttpAddress) , GNUNET_TIME_UNIT_FOREVER_REL);
2033     }
2034   return GNUNET_NO;
2035   return GNUNET_OK;
2036 }
2037
2038 int remove_peer_context_Iterator (void *cls, const GNUNET_HashCode *key, void *value)
2039 {
2040   struct Plugin *plugin = cls;
2041   struct HTTP_PeerContext * pc = value;
2042   struct Session * ps = pc->head;
2043   struct Session * tmp = NULL;
2044   struct HTTP_Message * msg = NULL;
2045   struct HTTP_Message * msg_tmp = NULL;
2046
2047   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing context for peer `%s'\n",GNUNET_i2s(&pc->identity));
2048
2049   while (ps!=NULL)
2050   {
2051         plugin->env->session_end(plugin, &pc->identity, ps);
2052         tmp = ps->next;
2053
2054     GNUNET_free_non_null (ps->addr);
2055     GNUNET_free(ps->url);
2056     if (ps->msgtok != NULL)
2057       GNUNET_SERVER_mst_destroy (ps->msgtok);
2058
2059     msg = ps->pending_msgs_head;
2060     while (msg!=NULL)
2061     {
2062       msg_tmp = msg->next;
2063       GNUNET_free(msg);
2064       msg = msg_tmp;
2065     }
2066     if (ps->direction==OUTBOUND)
2067     {
2068       if (ps->send_endpoint!=NULL)
2069         curl_easy_cleanup(ps->send_endpoint);
2070       if (ps->recv_endpoint!=NULL)
2071         curl_easy_cleanup(ps->recv_endpoint);
2072     }
2073
2074     GNUNET_free(ps);
2075     ps=tmp;
2076   }
2077   GNUNET_free(pc);
2078   return GNUNET_YES;
2079 }
2080
2081
2082 /**
2083  * Exit point from the plugin.
2084  */
2085 void *
2086 libgnunet_plugin_transport_http_done (void *cls)
2087 {
2088   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
2089   struct Plugin *plugin = api->cls;
2090   CURLMcode mret;
2091   GNUNET_assert(cls !=NULL);
2092
2093   if (plugin->http_server_daemon_v4 != NULL)
2094   {
2095     MHD_stop_daemon (plugin->http_server_daemon_v4);
2096     plugin->http_server_daemon_v4 = NULL;
2097   }
2098   if (plugin->http_server_daemon_v6 != NULL)
2099   {
2100     MHD_stop_daemon (plugin->http_server_daemon_v6);
2101     plugin->http_server_daemon_v6 = NULL;
2102   }
2103
2104   if ( plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
2105   {
2106     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4);
2107     plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK;
2108   }
2109
2110   if ( plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
2111   {
2112     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v6);
2113     plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK;
2114   }
2115
2116
2117   /* free all peer information */
2118   if (plugin->peers!=NULL)
2119   {
2120           GNUNET_CONTAINER_multihashmap_iterate (plugin->peers,
2121                                                                                          &remove_peer_context_Iterator,
2122                                                                                          plugin);
2123           GNUNET_CONTAINER_multihashmap_destroy (plugin->peers);
2124   }
2125   if (plugin->multi_handle!=NULL)
2126   {
2127           mret = curl_multi_cleanup(plugin->multi_handle);
2128           if ( CURLM_OK != mret)
2129                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"curl multihandle clean up failed\n");
2130           plugin->multi_handle = NULL;
2131   }
2132   curl_global_cleanup();
2133
2134   if ( plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK)
2135   {
2136     GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task);
2137     plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK;
2138   }
2139
2140   GNUNET_free (plugin);
2141   GNUNET_free (api);
2142   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Unload http plugin complete...\n");
2143   return NULL;
2144 }
2145
2146
2147 /**
2148  * Entry point for the plugin.
2149  */
2150 void *
2151 libgnunet_plugin_transport_http_init (void *cls)
2152 {
2153   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
2154   struct Plugin *plugin;
2155   struct GNUNET_TRANSPORT_PluginFunctions *api;
2156   struct GNUNET_TIME_Relative gn_timeout;
2157   long long unsigned int port;
2158
2159   GNUNET_assert(cls !=NULL);
2160   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting http plugin...\n");
2161
2162   plugin = GNUNET_malloc (sizeof (struct Plugin));
2163   plugin->stats = env->stats;
2164   plugin->env = env;
2165   plugin->peers = NULL;
2166
2167   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
2168   api->cls = plugin;
2169   api->send = &http_plugin_send;
2170   api->disconnect = &http_plugin_disconnect;
2171   api->address_pretty_printer = &http_plugin_address_pretty_printer;
2172   api->check_address = &http_plugin_address_suggested;
2173   api->address_to_string = &http_plugin_address_to_string;
2174
2175   /* Hashing our identity to use it in URLs */
2176   GNUNET_CRYPTO_hash_to_enc ( &(plugin->env->my_identity->hashPubKey), &plugin->my_ascii_hash_ident);
2177
2178   /* Reading port number from config file */
2179   if ((GNUNET_OK !=
2180        GNUNET_CONFIGURATION_get_value_number (env->cfg,
2181                                               "transport-http",
2182                                               "PORT",
2183                                               &port)) ||
2184       (port > 65535) )
2185     {
2186       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2187                        "http",
2188                        _
2189                        ("Require valid port number for transport plugin `%s' in configuration!\n"),
2190                        "transport-http");
2191       libgnunet_plugin_transport_http_done (api);
2192       return NULL;
2193     }
2194   GNUNET_assert ((port > 0) && (port <= 65535));
2195   plugin->port_inbound = port;
2196   gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
2197   unsigned int timeout = (gn_timeout.value) / 1000;
2198   if ((plugin->http_server_daemon_v4 == NULL) && (plugin->http_server_daemon_v6 == NULL) && (port != 0))
2199     {
2200     plugin->http_server_daemon_v6 = MHD_start_daemon (
2201 #if DEBUG_HTTP
2202                                                                    MHD_USE_DEBUG |
2203 #endif
2204                                                                    MHD_USE_IPv6,
2205                                        port,
2206                                        &mhd_accept_cb,
2207                                        plugin , &mdh_access_cb, plugin,
2208                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 32,
2209                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 6,
2210                                        MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) timeout,
2211                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
2212                                        MHD_OPTION_NOTIFY_COMPLETED, &mhd_termination_cb, NULL,
2213                                        MHD_OPTION_END);
2214     plugin->http_server_daemon_v4 = MHD_start_daemon (
2215 #if DEBUG_HTTP
2216                                                                    MHD_USE_DEBUG |
2217 #endif
2218                                                                    MHD_NO_FLAG,
2219                                        port,
2220                                        &mhd_accept_cb,
2221                                        plugin , &mdh_access_cb, plugin,
2222                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 32,
2223                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 6,
2224                                        MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) timeout,
2225                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
2226                                        MHD_OPTION_NOTIFY_COMPLETED, &mhd_termination_cb, NULL,
2227                                        MHD_OPTION_END);
2228     }
2229   if (plugin->http_server_daemon_v4 != NULL)
2230     plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4);
2231   if (plugin->http_server_daemon_v6 != NULL)
2232     plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6);
2233
2234   if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)
2235     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 on port %u\n",port);
2236   else if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK)
2237     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 and IPv6 on port %u\n",port);
2238   else
2239   {
2240     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No MHD was started, transport plugin not functional!\n");
2241     libgnunet_plugin_transport_http_done (api);
2242     return NULL;
2243   }
2244
2245   /* Initializing cURL */
2246   curl_global_init(CURL_GLOBAL_ALL);
2247   plugin->multi_handle = curl_multi_init();
2248
2249   if ( NULL == plugin->multi_handle )
2250   {
2251     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
2252                      "http",
2253                      _("Could not initialize curl multi handle, failed to start http plugin!\n"),
2254                      "transport-http");
2255     libgnunet_plugin_transport_http_done (api);
2256     return NULL;
2257   }
2258
2259   plugin->peers = GNUNET_CONTAINER_multihashmap_create (10);
2260   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
2261
2262   return api;
2263 }
2264
2265 /* end of plugin_transport_http.c */