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