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