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