fixing: assertion failed at transport_api_new.c:1277
[oweals/gnunet.git] / src / transport / plugin_transport_smtp.c
1 /*
2      This file is part of GNUnet
3      (C) 2003, 2004, 2005, 2006, 2007 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_smtp.c
23  * @brief Implementation of the SMTP transport service
24  * @author Christian Grothoff
25  * @author Renaldo Ferreira
26  */
27
28 #include "platform.h"
29 #include "gnunet_util.h"
30 #include "gnunet_directories.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_transport.h"
33 #include "gnunet_stats_service.h"
34 #include <libesmtp.h>
35 #include <signal.h>
36
37
38 /**
39  * The default maximum size of each outbound SMTP message.
40  */
41 #define SMTP_MESSAGE_SIZE 65528
42
43 #define DEBUG_SMTP GNUNET_NO
44
45 #define FILTER_STRING_SIZE 64
46
47 /* how long can a line in base64 encoded
48    mime text be? (in characters, excluding "\n") */
49 #define MAX_CHAR_PER_LINE 76
50
51 #define EBUF_LEN 128
52
53 /**
54  * Host-Address in a SMTP network.
55  */
56 typedef struct
57 {
58
59   /**
60    * Filter line that every sender must include in the E-mails such
61    * that the receiver can effectively filter out the GNUnet traffic
62    * from the E-mail.
63    */
64   char filter[FILTER_STRING_SIZE];
65
66   /**
67    * Claimed E-mail address of the sender.
68    * Format is "foo@bar.com" with null termination, padded to be
69    * of a multiple of 8 bytes long.
70    */
71   char senderAddress[0];
72
73 } EmailAddress;
74
75 /**
76  * Encapsulation of a GNUnet message in the SMTP mail body (before
77  * base64 encoding).
78  */
79 typedef struct
80 {
81   GNUNET_MessageHeader header;
82
83   /**
84    * What is the identity of the sender (GNUNET_hash of public key)
85    */
86   GNUNET_PeerIdentity sender;
87
88 } SMTPMessage;
89
90 /* *********** globals ************* */
91
92 /**
93  * apis (our advertised API and the core api )
94  */
95 static GNUNET_CoreAPIForTransport *coreAPI;
96
97 static struct GNUNET_GE_Context *ectx;
98
99 /**
100  * Thread that listens for inbound messages
101  */
102 static struct GNUNET_ThreadHandle *dispatchThread;
103
104 /**
105  * Flag to indicate that server has been shut down.
106  */
107 static int smtp_shutdown = GNUNET_YES;
108
109 /**
110  * Set to the SMTP server hostname (and port) for outgoing messages.
111  */
112 static char *smtp_server_name;
113
114 static char *pipename;
115
116 /**
117  * Lock for uses of libesmtp (not thread-safe).
118  */
119 static struct GNUNET_Mutex *lock;
120
121 /**
122  * Old handler for SIGPIPE (kept to be able to restore).
123  */
124 static struct sigaction old_handler;
125
126 static char *email;
127
128 static GNUNET_TransportAPI smtpAPI;
129
130 static GNUNET_Stats_ServiceAPI *stats;
131
132 static int stat_bytesReceived;
133
134 static int stat_bytesSent;
135
136 static int stat_bytesDropped;
137
138 /**
139  * How many e-mails are we allowed to send per hour?
140  */
141 static unsigned long long rate_limit;
142
143 static GNUNET_CronTime last_transmission;
144
145 /** ******************** Base64 encoding ***********/
146
147 #define FILLCHAR '='
148 static char *cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
149   "abcdefghijklmnopqrstuvwxyz" "0123456789+/";
150
151 /**
152  * Encode into Base64.
153  *
154  * @param data the data to encode
155  * @param len the length of the input
156  * @param output where to write the output (*output should be NULL,
157  *   is allocated)
158  * @return the size of the output
159  */
160 static unsigned int
161 base64_encode (const char *data, unsigned int len, char **output)
162 {
163   unsigned int i;
164   char c;
165   unsigned int ret;
166   char *opt;
167
168 /*    (*output)[ret++] = '\r'; \*/
169 #define CHECKLINE \
170   if ( (ret % MAX_CHAR_PER_LINE) == 0) { \
171     (*output)[ret++] = '\n'; \
172   }
173   ret = 0;
174   opt = GNUNET_malloc (2 + (((len * 4 / 3) + 8) * (MAX_CHAR_PER_LINE + 2)) /
175                        MAX_CHAR_PER_LINE);
176   /* message must start with \r\n for libesmtp */
177   *output = opt;
178   opt[0] = '\r';
179   opt[1] = '\n';
180   ret += 2;
181   for (i = 0; i < len; ++i)
182     {
183       c = (data[i] >> 2) & 0x3f;
184       opt[ret++] = cvt[(int) c];
185       CHECKLINE;
186       c = (data[i] << 4) & 0x3f;
187       if (++i < len)
188         c |= (data[i] >> 4) & 0x0f;
189       opt[ret++] = cvt[(int) c];
190       CHECKLINE;
191       if (i < len)
192         {
193           c = (data[i] << 2) & 0x3f;
194           if (++i < len)
195             c |= (data[i] >> 6) & 0x03;
196           opt[ret++] = cvt[(int) c];
197           CHECKLINE;
198         }
199       else
200         {
201           ++i;
202           opt[ret++] = FILLCHAR;
203           CHECKLINE;
204         }
205       if (i < len)
206         {
207           c = data[i] & 0x3f;
208           opt[ret++] = cvt[(int) c];
209           CHECKLINE;
210         }
211       else
212         {
213           opt[ret++] = FILLCHAR;
214           CHECKLINE;
215         }
216     }
217   opt[ret++] = FILLCHAR;
218   return ret;
219 }
220
221 #define cvtfind(a)( (((a) >= 'A')&&((a) <= 'Z'))? (a)-'A'\
222                    :(((a)>='a')&&((a)<='z')) ? (a)-'a'+26\
223                    :(((a)>='0')&&((a)<='9')) ? (a)-'0'+52\
224            :((a) == '+') ? 62\
225            :((a) == '/') ? 63 : -1)
226 /**
227  * Decode from Base64.
228  *
229  * @param data the data to encode
230  * @param len the length of the input
231  * @param output where to write the output (*output should be NULL,
232  *   is allocated)
233  * @return the size of the output
234  */
235 static unsigned int
236 base64_decode (const char *data, unsigned int len, char **output)
237 {
238   unsigned int i;
239   char c;
240   char c1;
241   unsigned int ret = 0;
242
243 #define CHECK_CRLF  while (data[i] == '\r' || data[i] == '\n') {\
244                         GNUNET_GE_LOG(ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, "ignoring CR/LF\n"); \
245                         i++; \
246                         if (i >= len) goto END;  \
247                 }
248
249   *output = GNUNET_malloc ((len * 3 / 4) + 8);
250 #if DEBUG_SMTP
251   GNUNET_GE_LOG (ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
252                  "base64_decode decoding len=%d\n", len);
253 #endif
254   for (i = 0; i < len; ++i)
255     {
256       CHECK_CRLF;
257       if (data[i] == FILLCHAR)
258         break;
259       c = (char) cvtfind (data[i]);
260       ++i;
261       CHECK_CRLF;
262       c1 = (char) cvtfind (data[i]);
263       c = (c << 2) | ((c1 >> 4) & 0x3);
264       (*output)[ret++] = c;
265       if (++i < len)
266         {
267           CHECK_CRLF;
268           c = data[i];
269           if (FILLCHAR == c)
270             break;
271           c = (char) cvtfind (c);
272           c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
273           (*output)[ret++] = c1;
274         }
275       if (++i < len)
276         {
277           CHECK_CRLF;
278           c1 = data[i];
279           if (FILLCHAR == c1)
280             break;
281
282           c1 = (char) cvtfind (c1);
283           c = ((c << 6) & 0xc0) | c1;
284           (*output)[ret++] = c;
285         }
286     }
287 END:
288   return ret;
289 }
290
291 /* ********************* the real stuff ******************* */
292
293 #define strAUTOncmp(a,b) strncmp(a,b,strlen(b))
294
295 /**
296  * Listen to the pipe, decode messages and send to core.
297  */
298 static void *
299 listenAndDistribute (void *unused)
300 {
301   char *line;
302   unsigned int linesize;
303   SMTPMessage *mp;
304   FILE *fdes;
305   char *retl;
306   char *out;
307   unsigned int size;
308   GNUNET_TransportPacket *coreMP;
309   int fd;
310   unsigned int pos;
311
312   linesize = ((GNUNET_MAX_BUFFER_SIZE * 4 / 3) + 8) * (MAX_CHAR_PER_LINE + 2) / MAX_CHAR_PER_LINE;      /* maximum size of a line supported */
313   line = GNUNET_malloc (linesize + 2);  /* 2 bytes for off-by-one errors, just to be safe... */
314
315 #define READLINE(l,limit) \
316   do { retl = fgets(l, (limit), fdes);                          \
317     if ( (retl == NULL) || (smtp_shutdown == GNUNET_YES)) {\
318       goto END; \
319     }\
320     if (coreAPI->load_monitor != NULL) \
321      GNUNET_network_monitor_notify_transmission(coreAPI->load_monitor, GNUNET_ND_DOWNLOAD, strlen(retl)); \
322   } while (0)
323
324
325   while (smtp_shutdown == GNUNET_NO)
326     {
327       fd = OPEN (pipename, O_RDONLY | O_ASYNC);
328       if (fd == -1)
329         {
330           if (smtp_shutdown == GNUNET_NO)
331             GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS);
332           continue;
333         }
334       fdes = fdopen (fd, "r");
335       while (smtp_shutdown == GNUNET_NO)
336         {
337           /* skip until end of header */
338           do
339             {
340               READLINE (line, linesize);
341             }
342           while ((line[0] != '\r') && (line[0] != '\n'));       /* expect newline */
343           READLINE (line, linesize);    /* read base64 encoded message; decode, process */
344           pos = 0;
345           while (1)
346             {
347               pos = strlen (line) - 1;  /* ignore new line */
348               READLINE (&line[pos], linesize - pos);    /* read base64 encoded message; decode, process */
349               if ((line[pos] == '\r') || (line[pos] == '\n'))
350                 break;          /* empty line => end of message! */
351             }
352           size = base64_decode (line, pos, &out);
353           if (size < sizeof (SMTPMessage))
354             {
355               GNUNET_GE_BREAK (ectx, 0);
356               GNUNET_free (out);
357               goto END;
358             }
359
360           mp = (SMTPMessage *) & out[size - sizeof (SMTPMessage)];
361           if (ntohs (mp->header.size) != size)
362             {
363               GNUNET_GE_LOG (ectx,
364                              GNUNET_GE_WARNING | GNUNET_GE_BULK |
365                              GNUNET_GE_USER,
366                              _
367                              ("Received malformed message via %s. Ignored.\n"),
368                              "SMTP");
369 #if DEBUG_SMTP
370               GNUNET_GE_LOG (ectx,
371                              GNUNET_GE_DEBUG | GNUNET_GE_REQUEST |
372                              GNUNET_GE_USER,
373                              "Size returned by base64=%d, in the msg=%d.\n",
374                              size, ntohl (mp->size));
375 #endif
376               GNUNET_free (out);
377               goto END;
378             }
379           if (stats != NULL)
380             stats->change (stat_bytesReceived, size);
381           coreMP = GNUNET_malloc (sizeof (GNUNET_TransportPacket));
382           coreMP->msg = out;
383           coreMP->size = size - sizeof (SMTPMessage);
384           coreMP->tsession = NULL;
385           coreMP->sender = mp->sender;
386 #if DEBUG_SMTP
387           GNUNET_GE_LOG (ectx,
388                          GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
389                          "SMTP message passed to the core.\n");
390 #endif
391
392           coreAPI->receive (coreMP);
393         }
394     END:
395 #if DEBUG_SMTP
396       GNUNET_GE_LOG (ectx,
397                      GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
398                      "SMTP message processed.\n");
399 #endif
400       if (fdes != NULL)
401         fclose (fdes);
402     }
403   GNUNET_free (line);
404   return NULL;
405 }
406
407 /* *************** API implementation *************** */
408
409 /**
410  * Verify that a hello-Message is correct (a node is reachable at that
411  * address). Since the reply will be asynchronous, a method must be
412  * called on success.
413  *
414  * @param hello the hello message to verify
415  *        (the signature/crc have been verified before)
416  * @return GNUNET_OK on success, GNUNET_SYSERR on error
417  */
418 static int
419 api_verify_hello (const GNUNET_MessageHello * hello)
420 {
421   const EmailAddress *maddr;
422
423   maddr = (const EmailAddress *) &hello[1];
424   if ((ntohs (hello->header.size) !=
425        sizeof (GNUNET_MessageHello) + ntohs (hello->senderAddressSize)) ||
426       (maddr->senderAddress[ntohs (hello->senderAddressSize) - 1 -
427                             FILTER_STRING_SIZE] != '\0'))
428     {
429       GNUNET_GE_BREAK (ectx, 0);
430       return GNUNET_SYSERR;     /* obviously invalid */
431     }
432   if (NULL == strstr (maddr->filter, ": "))
433     return GNUNET_SYSERR;
434   return GNUNET_OK;
435 }
436
437 /**
438  * Create a hello-Message for the current node. The hello is created
439  * without signature and without a timestamp. The GNUnet core will
440  * GNUNET_RSA_sign the message and add an expiration time.
441  *
442  * @return hello on success, NULL on error
443  */
444 static GNUNET_MessageHello *
445 api_create_hello ()
446 {
447   GNUNET_MessageHello *msg;
448   char *filter;
449   EmailAddress *haddr;
450   int i;
451
452   GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
453                                             "SMTP", "FILTER",
454                                             "X-mailer: GNUnet", &filter);
455   if (NULL == strstr (filter, ": "))
456     {
457       GNUNET_GE_LOG (ectx,
458                      GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
459                      _("SMTP filter string to invalid, lacks ': '\n"));
460       GNUNET_free (filter);
461       return NULL;
462     }
463
464   if (strlen (filter) > FILTER_STRING_SIZE)
465     {
466       filter[FILTER_STRING_SIZE] = '\0';
467       GNUNET_GE_LOG (ectx,
468                      GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
469                      _("SMTP filter string to long, capped to `%s'\n"),
470                      filter);
471     }
472   i = (strlen (email) + 8) & (~7);      /* make multiple of 8 */
473   msg =
474     GNUNET_malloc (sizeof (GNUNET_MessageHello) + sizeof (EmailAddress) + i);
475   memset (msg, 0, sizeof (GNUNET_MessageHello) + sizeof (EmailAddress) + i);
476   haddr = (EmailAddress *) & msg[1];
477   memset (&haddr->filter[0], 0, FILTER_STRING_SIZE);
478   strcpy (&haddr->filter[0], filter);
479   memcpy (&haddr->senderAddress[0], email, strlen (email) + 1);
480   msg->senderAddressSize = htons (strlen (email) + 1 + sizeof (EmailAddress));
481   msg->protocol = htons (GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP);
482   msg->MTU = htonl (smtpAPI.mtu);
483   msg->header.size = htons (GNUNET_sizeof_hello (msg));
484   if (api_verify_hello (msg) == GNUNET_SYSERR)
485     GNUNET_GE_ASSERT (ectx, 0);
486   GNUNET_free (filter);
487   return msg;
488 }
489
490 struct GetMessageClosure
491 {
492   unsigned int esize;
493   unsigned int pos;
494   char *ebody;
495 };
496
497 static const char *
498 get_message (void **buf, int *len, void *cls)
499 {
500   struct GetMessageClosure *gmc = cls;
501
502   *buf = NULL;
503   if (len == NULL)
504     {
505       gmc->pos = 0;
506       return NULL;
507     }
508   if (gmc->pos == gmc->esize)
509     return NULL;                /* done */
510   *len = gmc->esize;
511   gmc->pos = gmc->esize;
512   return gmc->ebody;
513 }
514
515 /**
516  * Send a message to the specified remote node.
517  *
518  * @param tsession the GNUNET_MessageHello identifying the remote node
519  * @param msg what to send
520  * @param size the size of the message
521  * @param important is this message important enough to override typical limits?
522  * @return GNUNET_SYSERR on error, GNUNET_OK on success
523  */
524 static int
525 api_send (GNUNET_TSession * tsession,
526           const void *msg, const unsigned int size, int important)
527 {
528   const GNUNET_MessageHello *hello;
529   const EmailAddress *haddr;
530   char *m;
531   char *filter;
532   char *fvalue;
533   SMTPMessage *mp;
534   struct GetMessageClosure gm_cls;
535   smtp_session_t session;
536   smtp_message_t message;
537   smtp_recipient_t recipient;
538 #define EBUF_LEN 128
539   char ebuf[EBUF_LEN];
540   GNUNET_CronTime now;
541
542   if (smtp_shutdown == GNUNET_YES)
543     return GNUNET_SYSERR;
544   if ((size == 0) || (size > smtpAPI.mtu))
545     {
546       GNUNET_GE_BREAK (ectx, 0);
547       return GNUNET_SYSERR;
548     }
549   now = GNUNET_get_time ();
550   if ((important != GNUNET_YES) &&
551       ((now - last_transmission) * rate_limit) < GNUNET_CRON_HOURS)
552     return GNUNET_NO;           /* rate too high */
553   last_transmission = now;
554
555   hello = (const GNUNET_MessageHello *) tsession->internal;
556   if (hello == NULL)
557     return GNUNET_SYSERR;
558   GNUNET_mutex_lock (lock);
559   session = smtp_create_session ();
560   if (session == NULL)
561     {
562       GNUNET_GE_LOG (ectx,
563                      GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
564                      GNUNET_GE_IMMEDIATE,
565                      _("SMTP: `%s' failed: %s.\n"),
566                      "smtp_create_session",
567                      smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
568       GNUNET_mutex_unlock (lock);
569       return GNUNET_SYSERR;
570     }
571   if (0 == smtp_set_server (session, smtp_server_name))
572     {
573       GNUNET_GE_LOG (ectx,
574                      GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
575                      GNUNET_GE_IMMEDIATE,
576                      _("SMTP: `%s' failed: %s.\n"),
577                      "smtp_set_server",
578                      smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
579       smtp_destroy_session (session);
580       GNUNET_mutex_unlock (lock);
581       return GNUNET_SYSERR;
582     }
583   haddr = (const EmailAddress *) &hello[1];
584   message = smtp_add_message (session);
585   if (message == NULL)
586     {
587       GNUNET_GE_LOG (ectx,
588                      GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
589                      GNUNET_GE_BULK,
590                      _("SMTP: `%s' failed: %s.\n"),
591                      "smtp_add_message",
592                      smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
593       smtp_destroy_session (session);
594       GNUNET_mutex_unlock (lock);
595       return GNUNET_SYSERR;
596     }
597   smtp_set_header (message, "To", NULL, haddr->senderAddress);
598   smtp_set_header (message, "From", NULL, email);
599
600   filter = GNUNET_strdup (haddr->filter);
601   fvalue = strstr (filter, ": ");
602   GNUNET_GE_ASSERT (NULL, NULL != fvalue);
603   fvalue[0] = '\0';
604   fvalue += 2;
605   if (0 == smtp_set_header (message, filter, fvalue))
606     {
607       GNUNET_GE_LOG (ectx,
608                      GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
609                      GNUNET_GE_BULK,
610                      _("SMTP: `%s' failed: %s.\n"),
611                      "smtp_set_header",
612                      smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
613       smtp_destroy_session (session);
614       GNUNET_mutex_unlock (lock);
615       GNUNET_free (filter);
616       return GNUNET_SYSERR;
617     }
618   GNUNET_free (filter);
619   m = GNUNET_malloc (size + sizeof (SMTPMessage));
620   memcpy (m, msg, size);
621   mp = (SMTPMessage *) & m[size];
622   mp->header.size = htons (size + sizeof (SMTPMessage));
623   mp->header.type = htons (0);
624   mp->sender = *coreAPI->my_identity;
625   gm_cls.ebody = NULL;
626   gm_cls.pos = 0;
627   gm_cls.esize =
628     base64_encode (m, size + sizeof (SMTPMessage), &gm_cls.ebody);
629   GNUNET_free (m);
630   if (0 == smtp_size_set_estimate (message, gm_cls.esize))
631     {
632       GNUNET_GE_LOG (ectx,
633                      GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
634                      GNUNET_GE_BULK,
635                      _("SMTP: `%s' failed: %s.\n"),
636                      "smtp_size_set_estimate",
637                      smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
638     }
639   if (0 == smtp_set_messagecb (message, &get_message, &gm_cls))
640     {
641       GNUNET_GE_LOG (ectx,
642                      GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
643                      GNUNET_GE_BULK,
644                      _("SMTP: `%s' failed: %s.\n"),
645                      "smtp_set_messagecb",
646                      smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
647       smtp_destroy_session (session);
648       GNUNET_mutex_unlock (lock);
649       GNUNET_free (gm_cls.ebody);
650       return GNUNET_SYSERR;
651     }
652   recipient = smtp_add_recipient (message, haddr->senderAddress);
653   if (recipient == NULL)
654     {
655       GNUNET_GE_LOG (ectx,
656                      GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
657                      GNUNET_GE_BULK,
658                      _("SMTP: `%s' failed: %s.\n"),
659                      "smtp_add_recipient",
660                      smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
661       smtp_destroy_session (session);
662       GNUNET_mutex_unlock (lock);
663       return GNUNET_SYSERR;
664     }
665   if (0 == smtp_start_session (session))
666     {
667       GNUNET_GE_LOG (ectx,
668                      GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
669                      GNUNET_GE_BULK,
670                      _("SMTP: `%s' failed: %s.\n"),
671                      "smtp_start_session",
672                      smtp_strerror (smtp_errno (), ebuf, EBUF_LEN));
673       smtp_destroy_session (session);
674       GNUNET_mutex_unlock (lock);
675       GNUNET_free (gm_cls.ebody);
676       return GNUNET_SYSERR;
677     }
678   if (stats != NULL)
679     stats->change (stat_bytesSent, size);
680   if (coreAPI->load_monitor != NULL)
681     GNUNET_network_monitor_notify_transmission (coreAPI->load_monitor,
682                                                 GNUNET_ND_UPLOAD,
683                                                 gm_cls.esize);
684   smtp_message_reset_status (message);  /* this is needed to plug a 28-byte/message memory leak in libesmtp */
685   smtp_destroy_session (session);
686   GNUNET_mutex_unlock (lock);
687   GNUNET_free (gm_cls.ebody);
688   return GNUNET_OK;
689 }
690
691 /**
692  * Establish a connection to a remote node.
693  * @param hello the hello-Message for the target node
694  * @param tsessionPtr the session handle that is to be set
695  * @param may_reuse can we re-use an existing connection?
696  * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
697  */
698 static int
699 api_connect (const GNUNET_MessageHello * hello,
700              GNUNET_TSession ** tsessionPtr, int may_reuse)
701 {
702   GNUNET_TSession *tsession;
703
704   tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
705   tsession->internal = GNUNET_malloc (GNUNET_sizeof_hello (hello));
706   tsession->peer = hello->senderIdentity;
707   memcpy (tsession->internal, hello, GNUNET_sizeof_hello (hello));
708   tsession->ttype = smtpAPI.protocol_number;
709   (*tsessionPtr) = tsession;
710   return GNUNET_OK;
711 }
712
713 /**
714  * Disconnect from a remote node.
715  *
716  * @param tsession the session that is closed
717  * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
718  */
719 static int
720 api_disconnect (GNUNET_TSession * tsession)
721 {
722   if (tsession != NULL)
723     {
724       if (tsession->internal != NULL)
725         GNUNET_free (tsession->internal);
726       GNUNET_free (tsession);
727     }
728   return GNUNET_OK;
729 }
730
731 /**
732  * Start the server process to receive inbound traffic.
733  * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
734  */
735 static int
736 api_start_transport_server ()
737 {
738   smtp_shutdown = GNUNET_NO;
739   /* initialize SMTP network */
740   dispatchThread =
741     GNUNET_thread_create (&listenAndDistribute, NULL, 1024 * 4);
742   if (dispatchThread == NULL)
743     {
744       GNUNET_GE_DIE_STRERROR (ectx,
745                               GNUNET_GE_ADMIN | GNUNET_GE_BULK |
746                               GNUNET_GE_FATAL, "pthread_create");
747       return GNUNET_SYSERR;
748     }
749   return GNUNET_OK;
750 }
751
752 /**
753  * Shutdown the server process (stop receiving inbound traffic). Maybe
754  * restarted later!
755  */
756 static int
757 api_stop_transport_server ()
758 {
759   void *unused;
760
761   smtp_shutdown = GNUNET_YES;
762   GNUNET_thread_stop_sleep (dispatchThread);
763   GNUNET_thread_join (dispatchThread, &unused);
764   return GNUNET_OK;
765 }
766
767 /**
768  * Convert SMTP hello to an IP address (always fails).
769  */
770 static int
771 api_hello_to_address (const GNUNET_MessageHello * hello,
772                       void **sa, unsigned int *sa_len)
773 {
774   return GNUNET_SYSERR;
775 }
776
777 /**
778  * Always fails.
779  */
780 static int
781 api_associate (GNUNET_TSession * tsession)
782 {
783   return GNUNET_SYSERR;         /* SMTP connections can never be associated */
784 }
785
786 /**
787  * Always succeeds (for now; we should look at adding
788  * frequency limits to SMTP in the future!).
789  */
790 static int
791 api_test_would_try (GNUNET_TSession * tsession, const unsigned int size,
792                     int important)
793 {
794   return GNUNET_OK;             /* we always try... */
795 }
796
797 /**
798  * The exported method. Makes the core api available via a global and
799  * returns the smtp transport API.
800  */
801 GNUNET_TransportAPI *
802 inittransport_smtp (GNUNET_CoreAPIForTransport * core)
803 {
804
805
806   unsigned long long mtu;
807   struct sigaction sa;
808
809   coreAPI = core;
810   ectx = core->ectx;
811   if (!GNUNET_GC_have_configuration_value (coreAPI->cfg, "SMTP", "EMAIL"))
812     {
813       GNUNET_GE_LOG (ectx,
814                      GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
815                      _
816                      ("No email-address specified, can not start SMTP transport.\n"));
817       return NULL;
818     }
819   GNUNET_GC_get_configuration_value_number (coreAPI->cfg,
820                                             "SMTP",
821                                             "MTU",
822                                             1200,
823                                             SMTP_MESSAGE_SIZE,
824                                             SMTP_MESSAGE_SIZE, &mtu);
825   GNUNET_GC_get_configuration_value_number (coreAPI->cfg,
826                                             "SMTP",
827                                             "RATELIMIT",
828                                             0, 0, 1024 * 1024, &rate_limit);
829   stats = coreAPI->service_request ("stats");
830   if (stats != NULL)
831     {
832       stat_bytesReceived
833         = stats->create (gettext_noop ("# bytes received via SMTP"));
834       stat_bytesSent = stats->create (gettext_noop ("# bytes sent via SMTP"));
835       stat_bytesDropped
836         = stats->create (gettext_noop ("# bytes dropped by SMTP (outgoing)"));
837     }
838   GNUNET_GC_get_configuration_value_filename (coreAPI->cfg,
839                                               "SMTP",
840                                               "PIPE",
841                                               GNUNET_DEFAULT_DAEMON_VAR_DIRECTORY
842                                               "/smtp-pipe", &pipename);
843   UNLINK (pipename);
844   if (0 != mkfifo (pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH))
845     {
846       GNUNET_GE_LOG_STRERROR (ectx,
847                               GNUNET_GE_ADMIN | GNUNET_GE_BULK |
848                               GNUNET_GE_FATAL, "mkfifo");
849       GNUNET_free (pipename);
850       coreAPI->service_release (stats);
851       stats = NULL;
852       return NULL;
853     }
854   /* we need to allow the mailer program to send us messages;
855      easiest done by giving it write permissions (see Mantis #1142) */
856   if (0 != chmod (pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH))
857     GNUNET_GE_LOG_STRERROR (ectx,
858                             GNUNET_GE_ADMIN | GNUNET_GE_BULK |
859                             GNUNET_GE_WARNING, "chmod");
860   GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
861                                             "SMTP", "EMAIL", NULL, &email);
862   lock = GNUNET_mutex_create (GNUNET_NO);
863   GNUNET_GC_get_configuration_value_string (coreAPI->cfg,
864                                             "SMTP",
865                                             "SERVER",
866                                             "localhost:25",
867                                             &smtp_server_name);
868   sa.sa_handler = SIG_IGN;
869   sigemptyset (&sa.sa_mask);
870   sa.sa_flags = 0;
871   sigaction (SIGPIPE, &sa, &old_handler);
872
873   smtpAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP;
874   smtpAPI.mtu = mtu - sizeof (SMTPMessage);
875   smtpAPI.cost = 50;
876   smtpAPI.hello_verify = &api_verify_hello;
877   smtpAPI.hello_create = &api_create_hello;
878   smtpAPI.connect = &api_connect;
879   smtpAPI.send = &api_send;
880   smtpAPI.associate = &api_associate;
881   smtpAPI.disconnect = &api_disconnect;
882   smtpAPI.server_start = &api_start_transport_server;
883   smtpAPI.server_stop = &api_stop_transport_server;
884   smtpAPI.hello_to_address = &api_hello_to_address;
885   smtpAPI.send_now_test = &api_test_would_try;
886   return &smtpAPI;
887 }
888
889 void
890 donetransport_smtp ()
891 {
892   sigaction (SIGPIPE, &old_handler, NULL);
893   GNUNET_free (smtp_server_name);
894   if (stats != NULL)
895     {
896       coreAPI->service_release (stats);
897       stats = NULL;
898     }
899   GNUNET_mutex_destroy (lock);
900   lock = NULL;
901   UNLINK (pipename);
902   GNUNET_free (pipename);
903   pipename = NULL;
904   GNUNET_free (email);
905   email = NULL;
906 }
907
908 /* end of smtp.c */