uncrustify as demanded.
[oweals/gnunet.git] / src / transport / plugin_transport_smtp.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2003-2013 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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_constants.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    * Filter line that every sender must include in the E-mails such
59    * that the receiver can effectively filter out the GNUnet traffic
60    * from the E-mail.
61    */
62   char filter[FILTER_STRING_SIZE];
63
64   /**
65    * Claimed E-mail address of the sender.
66    * Format is "foo@bar.com" with null termination, padded to be
67    * of a multiple of 8 bytes long.
68    */
69   char senderAddress[0];
70 } EmailAddress;
71
72 GNUNET_NETWORK_STRUCT_BEGIN
73
74 /**
75  * Encapsulation of a GNUnet message in the SMTP mail body (before
76  * base64 encoding).
77  */
78 typedef struct {
79   GNUNET_MessageHeader header;
80
81   /**
82    * What is the identity of the sender (GNUNET_hash of public key)
83    */
84   GNUNET_PeerIdentity sender;
85 } SMTPMessage;
86 GNUNET_NETWORK_STRUCT_END
87
88 /* *********** globals ************* */
89
90 /**
91  * apis (our advertised API and the core api )
92  */
93 static GNUNET_CoreAPIForTransport *core_api;
94
95 static struct GNUNET_GE_Context *ectx;
96
97 /**
98  * Thread that listens for inbound messages
99  */
100 static struct GNUNET_ThreadHandle *dispatchThread;
101
102 /**
103  * Flag to indicate that server has been shut down.
104  */
105 static int smtp_shutdown = GNUNET_YES;
106
107 /**
108  * Set to the SMTP server hostname (and port) for outgoing messages.
109  */
110 static char *smtp_server_name;
111
112 static char *pipename;
113
114 /**
115  * Lock for uses of libesmtp (not thread-safe).
116  */
117 static struct GNUNET_Mutex *lock;
118
119 /**
120  * Old handler for SIGPIPE (kept to be able to restore).
121  */
122 static struct sigaction old_handler;
123
124 static char *email;
125
126 static GNUNET_TransportAPI smtpAPI;
127
128 static GNUNET_Stats_ServiceAPI *stats;
129
130 static int stat_bytesReceived;
131
132 static int stat_bytesSent;
133
134 static int stat_bytesDropped;
135
136 /**
137  * How many e-mails are we allowed to send per hour?
138  */
139 static unsigned long long rate_limit;
140
141 static GNUNET_CronTime last_transmission;
142
143
144 /* ********************* the real stuff ******************* */
145
146 #define strAUTOncmp(a, b) strncmp(a, b, strlen(b))
147
148 /**
149  * Listen to the pipe, decode messages and send to core.
150  */
151 static void *
152 listenAndDistribute(void *unused)
153 {
154   char *line;
155   unsigned int linesize;
156   SMTPMessage *mp;
157   FILE *fdes;
158   char *retl;
159   char *out;
160   unsigned int size;
161   GNUNET_TransportPacket *coreMP;
162   int fd;
163   unsigned int pos;
164
165   linesize = ((GNUNET_MAX_BUFFER_SIZE * 4 / 3) + 8) * (MAX_CHAR_PER_LINE + 2) / MAX_CHAR_PER_LINE;      /* maximum size of a line supported */
166   line = GNUNET_malloc(linesize + 2);   /* 2 bytes for off-by-one errors, just to be safe... */
167
168 #define READLINE(l, limit) \
169   do { retl = fgets(l, (limit), fdes);                          \
170        if ((retl == NULL) || (smtp_shutdown == GNUNET_YES)) { \
171            goto END; \
172          } \
173        if (core_api->load_monitor != NULL) \
174        GNUNET_network_monitor_notify_transmission (core_api->load_monitor, GNUNET_ND_DOWNLOAD, strlen(retl)); \
175     } while (0)
176
177
178   while (smtp_shutdown == GNUNET_NO)
179     {
180       fd = OPEN(pipename, O_RDONLY | O_ASYNC);
181       if (fd == -1)
182         {
183           if (smtp_shutdown == GNUNET_NO)
184             GNUNET_thread_sleep(5 * GNUNET_CRON_SECONDS);
185           continue;
186         }
187       fdes = fdopen(fd, "r");
188       while (smtp_shutdown == GNUNET_NO)
189         {
190           /* skip until end of header */
191           do
192             {
193               READLINE(line, linesize);
194             }
195           while ((line[0] != '\r') && (line[0] != '\n')); /* expect newline */
196           READLINE(line, linesize);     /* read base64 encoded message; decode, process */
197           pos = 0;
198           while (1)
199             {
200               pos = strlen(line) - 1;   /* ignore new line */
201               READLINE(&line[pos], linesize - pos); /* read base64 encoded message; decode, process */
202               if ((line[pos] == '\r') || (line[pos] == '\n'))
203                 break;          /* empty line => end of message! */
204             }
205           size = GNUNET_STRINGS_base64_decode(line, pos, &out);
206           if (size < sizeof(SMTPMessage))
207             {
208               GNUNET_GE_BREAK(ectx, 0);
209               GNUNET_free(out);
210               goto END;
211             }
212
213           mp = (SMTPMessage *)&out[size - sizeof(SMTPMessage)];
214           if (ntohs(mp->header.size) != size)
215             {
216               GNUNET_GE_LOG(ectx,
217                             GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
218                             _("Received malformed message via %s. Ignored.\n"),
219                             "SMTP");
220 #if DEBUG_SMTP
221               GNUNET_GE_LOG(ectx,
222                             GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
223                             "Size returned by base64=%d, in the msg=%d.\n", size,
224                             ntohl(mp->size));
225 #endif
226               GNUNET_free(out);
227               goto END;
228             }
229           if (stats != NULL)
230             stats->change(stat_bytesReceived, size);
231           coreMP = GNUNET_new(GNUNET_TransportPacket);
232           coreMP->msg = out;
233           coreMP->size = size - sizeof(SMTPMessage);
234           coreMP->tsession = NULL;
235           coreMP->sender = mp->sender;
236 #if DEBUG_SMTP
237           GNUNET_GE_LOG(ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
238                         "SMTP message passed to the core.\n");
239 #endif
240
241           core_api->receive(coreMP);
242         }
243 END:
244 #if DEBUG_SMTP
245       GNUNET_GE_LOG(ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
246                     "SMTP message processed.\n");
247 #endif
248       if (fdes != NULL)
249         fclose(fdes);
250     }
251   GNUNET_free(line);
252   return NULL;
253 }
254
255 /* *************** API implementation *************** */
256
257 /**
258  * Verify that a hello-Message is correct (a node is reachable at that
259  * address). Since the reply will be asynchronous, a method must be
260  * called on success.
261  *
262  * @param hello the hello message to verify
263  *        (the signature/crc have been verified before)
264  * @return GNUNET_OK on success, GNUNET_SYSERR on error
265  */
266 static int
267 api_verify_hello(const GNUNET_MessageHello * hello)
268 {
269   const EmailAddress *maddr;
270
271   maddr = (const EmailAddress *)&hello[1];
272   if ((ntohs(hello->header.size) !=
273        sizeof(GNUNET_MessageHello) + ntohs(hello->senderAddressSize)) ||
274       (maddr->senderAddress
275        [ntohs(hello->senderAddressSize) - 1 - FILTER_STRING_SIZE] != '\0'))
276     {
277       GNUNET_GE_BREAK(ectx, 0);
278       return GNUNET_SYSERR;     /* obviously invalid */
279     }
280   if (NULL == strstr(maddr->filter, ": "))
281     return GNUNET_SYSERR;
282   return GNUNET_OK;
283 }
284
285 /**
286  * Create a hello-Message for the current node. The hello is created
287  * without signature and without a timestamp. The GNUnet core will
288  * GNUNET_RSA_sign the message and add an expiration time.
289  *
290  * @return hello on success, NULL on error
291  */
292 static GNUNET_MessageHello *
293 api_create_hello()
294 {
295   GNUNET_MessageHello *msg;
296   char *filter;
297   EmailAddress *haddr;
298   int i;
299
300   GNUNET_GC_get_configuration_value_string(core_api->cfg, "SMTP", "FILTER",
301                                            "X-mailer: GNUnet", &filter);
302   if (NULL == strstr(filter, ": "))
303     {
304       GNUNET_GE_LOG(ectx, GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
305                     _("SMTP filter string to invalid, lacks ': '\n"));
306       GNUNET_free(filter);
307       return NULL;
308     }
309
310   if (strlen(filter) > FILTER_STRING_SIZE)
311     {
312       filter[FILTER_STRING_SIZE] = '\0';
313       GNUNET_GE_LOG(ectx, GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
314                     _("SMTP filter string to long, capped to `%s'\n"), filter);
315     }
316   i = (strlen(email) + 8) & (~7);       /* make multiple of 8 */
317   msg =
318     GNUNET_malloc(sizeof(GNUNET_MessageHello) + sizeof(EmailAddress) + i);
319   memset(msg, 0, sizeof(GNUNET_MessageHello) + sizeof(EmailAddress) + i);
320   haddr = (EmailAddress *)&msg[1];
321   memset(&haddr->filter[0], 0, FILTER_STRING_SIZE);
322   strcpy(&haddr->filter[0], filter);
323   GNUNET_memcpy(&haddr->senderAddress[0], email, strlen(email) + 1);
324   msg->senderAddressSize = htons(strlen(email) + 1 + sizeof(EmailAddress));
325   msg->protocol = htons(GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP);
326   msg->MTU = htonl(smtpAPI.mtu);
327   msg->header.size = htons(GNUNET_sizeof_hello(msg));
328   if (api_verify_hello(msg) == GNUNET_SYSERR)
329     GNUNET_GE_ASSERT(ectx, 0);
330   GNUNET_free(filter);
331   return msg;
332 }
333
334 struct GetMessageClosure {
335   unsigned int esize;
336   unsigned int pos;
337   char *ebody;
338 };
339
340 static const char *
341 get_message(void **buf, int *len, void *cls)
342 {
343   struct GetMessageClosure *gmc = cls;
344
345   *buf = NULL;
346   if (len == NULL)
347     {
348       gmc->pos = 0;
349       return NULL;
350     }
351   if (gmc->pos == gmc->esize)
352     return NULL;                /* done */
353   *len = gmc->esize;
354   gmc->pos = gmc->esize;
355   return gmc->ebody;
356 }
357
358 /**
359  * Send a message to the specified remote node.
360  *
361  * @param tsession the GNUNET_MessageHello identifying the remote node
362  * @param msg what to send
363  * @param size the size of the message
364  * @param important is this message important enough to override typical limits?
365  * @return GNUNET_SYSERR on error, GNUNET_OK on success
366  */
367 static int
368 api_send(GNUNET_TSession * tsession, const void *msg, const unsigned int size,
369          int important)
370 {
371   const GNUNET_MessageHello *hello;
372   const EmailAddress *haddr;
373   char *m;
374   char *filter;
375   char *fvalue;
376   SMTPMessage *mp;
377   struct GetMessageClosure gm_cls;
378   smtp_session_t session;
379   smtp_message_t message;
380   smtp_recipient_t recipient;
381
382 #define EBUF_LEN 128
383   char ebuf[EBUF_LEN];
384   GNUNET_CronTime now;
385
386   if (smtp_shutdown == GNUNET_YES)
387     return GNUNET_SYSERR;
388   if ((size == 0) || (size > smtpAPI.mtu))
389     {
390       GNUNET_GE_BREAK(ectx, 0);
391       return GNUNET_SYSERR;
392     }
393   now = GNUNET_get_time();
394   if ((important != GNUNET_YES) &&
395       ((now - last_transmission) * rate_limit) < GNUNET_CRON_HOURS)
396     return GNUNET_NO;           /* rate too high */
397   last_transmission = now;
398
399   hello = (const GNUNET_MessageHello *)tsession->internal;
400   if (hello == NULL)
401     return GNUNET_SYSERR;
402   GNUNET_mutex_lock(lock);
403   session = smtp_create_session();
404   if (session == NULL)
405     {
406       GNUNET_GE_LOG(ectx,
407                     GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
408                     GNUNET_GE_IMMEDIATE, _("SMTP: `%s' failed: %s.\n"),
409                     "smtp_create_session", smtp_strerror(smtp_errno(), ebuf,
410                                                          EBUF_LEN));
411       GNUNET_mutex_unlock(lock);
412       return GNUNET_SYSERR;
413     }
414   if (0 == smtp_set_server(session, smtp_server_name))
415     {
416       GNUNET_GE_LOG(ectx,
417                     GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
418                     GNUNET_GE_IMMEDIATE, _("SMTP: `%s' failed: %s.\n"),
419                     "smtp_set_server", smtp_strerror(smtp_errno(), ebuf,
420                                                      EBUF_LEN));
421       smtp_destroy_session(session);
422       GNUNET_mutex_unlock(lock);
423       return GNUNET_SYSERR;
424     }
425   haddr = (const EmailAddress *)&hello[1];
426   message = smtp_add_message(session);
427   if (message == NULL)
428     {
429       GNUNET_GE_LOG(ectx,
430                     GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
431                     GNUNET_GE_BULK, _("SMTP: `%s' failed: %s.\n"),
432                     "smtp_add_message", smtp_strerror(smtp_errno(), ebuf,
433                                                       EBUF_LEN));
434       smtp_destroy_session(session);
435       GNUNET_mutex_unlock(lock);
436       return GNUNET_SYSERR;
437     }
438   smtp_set_header(message, "To", NULL, haddr->senderAddress);
439   smtp_set_header(message, "From", NULL, email);
440
441   filter = GNUNET_strdup(haddr->filter);
442   fvalue = strstr(filter, ": ");
443   GNUNET_GE_ASSERT(NULL, NULL != fvalue);
444   fvalue[0] = '\0';
445   fvalue += 2;
446   if (0 == smtp_set_header(message, filter, fvalue))
447     {
448       GNUNET_GE_LOG(ectx,
449                     GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
450                     GNUNET_GE_BULK, _("SMTP: `%s' failed: %s.\n"),
451                     "smtp_set_header", smtp_strerror(smtp_errno(), ebuf,
452                                                      EBUF_LEN));
453       smtp_destroy_session(session);
454       GNUNET_mutex_unlock(lock);
455       GNUNET_free(filter);
456       return GNUNET_SYSERR;
457     }
458   GNUNET_free(filter);
459   m = GNUNET_malloc(size + sizeof(SMTPMessage));
460   GNUNET_memcpy(m, msg, size);
461   mp = (SMTPMessage *)&m[size];
462   mp->header.size = htons(size + sizeof(SMTPMessage));
463   mp->header.type = htons(0);
464   mp->sender = *core_api->my_identity;
465   gm_cls.ebody = NULL;
466   gm_cls.pos = 0;
467   gm_cls.esize = GNUNET_STRINGS_base64_encode(m, size + sizeof(SMTPMessage), &gm_cls.ebody);
468   GNUNET_free(m);
469   if (0 == smtp_size_set_estimate(message, gm_cls.esize))
470     {
471       GNUNET_GE_LOG(ectx,
472                     GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
473                     GNUNET_GE_BULK, _("SMTP: `%s' failed: %s.\n"),
474                     "smtp_size_set_estimate", smtp_strerror(smtp_errno(), ebuf,
475                                                             EBUF_LEN));
476     }
477   if (0 == smtp_set_messagecb(message, &get_message, &gm_cls))
478     {
479       GNUNET_GE_LOG(ectx,
480                     GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
481                     GNUNET_GE_BULK, _("SMTP: `%s' failed: %s.\n"),
482                     "smtp_set_messagecb", smtp_strerror(smtp_errno(), ebuf,
483                                                         EBUF_LEN));
484       smtp_destroy_session(session);
485       GNUNET_mutex_unlock(lock);
486       GNUNET_free(gm_cls.ebody);
487       return GNUNET_SYSERR;
488     }
489   recipient = smtp_add_recipient(message, haddr->senderAddress);
490   if (recipient == NULL)
491     {
492       GNUNET_GE_LOG(ectx,
493                     GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
494                     GNUNET_GE_BULK, _("SMTP: `%s' failed: %s.\n"),
495                     "smtp_add_recipient", smtp_strerror(smtp_errno(), ebuf,
496                                                         EBUF_LEN));
497       smtp_destroy_session(session);
498       GNUNET_mutex_unlock(lock);
499       return GNUNET_SYSERR;
500     }
501   if (0 == smtp_start_session(session))
502     {
503       GNUNET_GE_LOG(ectx,
504                     GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
505                     GNUNET_GE_BULK, _("SMTP: `%s' failed: %s.\n"),
506                     "smtp_start_session", smtp_strerror(smtp_errno(), ebuf,
507                                                         EBUF_LEN));
508       smtp_destroy_session(session);
509       GNUNET_mutex_unlock(lock);
510       GNUNET_free(gm_cls.ebody);
511       return GNUNET_SYSERR;
512     }
513   if (stats != NULL)
514     stats->change(stat_bytesSent, size);
515   if (core_api->load_monitor != NULL)
516     GNUNET_network_monitor_notify_transmission(core_api->load_monitor,
517                                                GNUNET_ND_UPLOAD, gm_cls.esize);
518   smtp_message_reset_status(message);   /* this is needed to plug a 28-byte/message memory leak in libesmtp */
519   smtp_destroy_session(session);
520   GNUNET_mutex_unlock(lock);
521   GNUNET_free(gm_cls.ebody);
522   return GNUNET_OK;
523 }
524
525 /**
526  * Establish a connection to a remote node.
527  * @param hello the hello-Message for the target node
528  * @param tsessionPtr the session handle that is to be set
529  * @param may_reuse can we re-use an existing connection?
530  * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
531  */
532 static int
533 api_connect(const GNUNET_MessageHello * hello, GNUNET_TSession ** tsessionPtr,
534             int may_reuse)
535 {
536   GNUNET_TSession *tsession;
537
538   tsession = GNUNET_new(GNUNET_TSession);
539   tsession->internal = GNUNET_malloc(GNUNET_sizeof_hello(hello));
540   tsession->peer = hello->senderIdentity;
541   GNUNET_memcpy(tsession->internal, hello, GNUNET_sizeof_hello(hello));
542   tsession->ttype = smtpAPI.protocol_number;
543   (*tsessionPtr) = tsession;
544   return GNUNET_OK;
545 }
546
547 /**
548  * Disconnect from a remote node.
549  *
550  * @param tsession the session that is closed
551  * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
552  */
553 static int
554 api_disconnect(GNUNET_TSession * tsession)
555 {
556   if (tsession != NULL)
557     {
558       if (tsession->internal != NULL)
559         GNUNET_free(tsession->internal);
560       GNUNET_free(tsession);
561     }
562   return GNUNET_OK;
563 }
564
565 /**
566  * Start the server process to receive inbound traffic.
567  * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
568  */
569 static int
570 api_start_transport_server()
571 {
572   smtp_shutdown = GNUNET_NO;
573   /* initialize SMTP network */
574   dispatchThread = GNUNET_thread_create(&listenAndDistribute, NULL, 1024 * 4);
575   if (dispatchThread == NULL)
576     {
577       GNUNET_GE_DIE_STRERROR(ectx,
578                              GNUNET_GE_ADMIN | GNUNET_GE_BULK | GNUNET_GE_FATAL,
579                              "pthread_create");
580       return GNUNET_SYSERR;
581     }
582   return GNUNET_OK;
583 }
584
585 /**
586  * Shutdown the server process (stop receiving inbound traffic). Maybe
587  * restarted later!
588  */
589 static int
590 api_stop_transport_server()
591 {
592   void *unused;
593
594   smtp_shutdown = GNUNET_YES;
595   GNUNET_thread_stop_sleep(dispatchThread);
596   GNUNET_thread_join(dispatchThread, &unused);
597   return GNUNET_OK;
598 }
599
600 /**
601  * Convert SMTP hello to an IP address (always fails).
602  */
603 static int
604 api_hello_to_address(const GNUNET_MessageHello * hello, void **sa,
605                      unsigned int *sa_len)
606 {
607   return GNUNET_SYSERR;
608 }
609
610 /**
611  * Always fails.
612  */
613 static int
614 api_associate(GNUNET_TSession * tsession)
615 {
616   return GNUNET_SYSERR;         /* SMTP connections can never be associated */
617 }
618
619 /**
620  * Always succeeds (for now; we should look at adding
621  * frequency limits to SMTP in the future!).
622  */
623 static int
624 api_test_would_try(GNUNET_TSession * tsession, unsigned int size,
625                    int important)
626 {
627   return GNUNET_OK;             /* we always try... */
628 }
629
630 /**
631  * The exported method. Makes the core api available via a global and
632  * returns the smtp transport API.
633  */
634 GNUNET_TransportAPI *
635 inittransport_smtp(struct GNUNET_CoreAPIForTransport * core)
636 {
637   unsigned long long mtu;
638   struct sigaction sa;
639
640   core_api = core;
641   ectx = core->ectx;
642   if (!GNUNET_GC_have_configuration_value(core_api->cfg, "SMTP", "EMAIL"))
643     {
644       GNUNET_GE_LOG(ectx, GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
645                     _
646                       ("No email-address specified, can not start SMTP transport.\n"));
647       return NULL;
648     }
649   GNUNET_GC_get_configuration_value_number(core_api->cfg, "SMTP", "MTU", 1200,
650                                            SMTP_MESSAGE_SIZE,
651                                            SMTP_MESSAGE_SIZE, &mtu);
652   GNUNET_GC_get_configuration_value_number(core_api->cfg, "SMTP", "RATELIMIT",
653                                            0, 0, 1024 * 1024, &rate_limit);
654   stats = core_api->service_request("stats");
655   if (stats != NULL)
656     {
657       stat_bytesReceived =
658         stats->create(gettext_noop("# bytes received via SMTP"));
659       stat_bytesSent = stats->create(gettext_noop("# bytes sent via SMTP"));
660       stat_bytesDropped =
661         stats->create(gettext_noop("# bytes dropped by SMTP (outgoing)"));
662     }
663   GNUNET_GC_get_configuration_value_filename(core_api->cfg, "SMTP", "PIPE", &pipename);
664   unlink(pipename);
665   if (0 != mkfifo(pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH))
666     {
667       GNUNET_GE_LOG_STRERROR(ectx,
668                              GNUNET_GE_ADMIN | GNUNET_GE_BULK | GNUNET_GE_FATAL,
669                              "mkfifo");
670       GNUNET_free(pipename);
671       core_api->service_release(stats);
672       stats = NULL;
673       return NULL;
674     }
675   /* we need to allow the mailer program to send us messages;
676    * easiest done by giving it write permissions (see Mantis #1142) */
677   if (0 != chmod(pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH))
678     GNUNET_GE_LOG_STRERROR(ectx,
679                            GNUNET_GE_ADMIN | GNUNET_GE_BULK |
680                            GNUNET_GE_WARNING, "chmod");
681   GNUNET_GC_get_configuration_value_string(core_api->cfg, "SMTP", "EMAIL", NULL,
682                                            &email);
683   lock = GNUNET_mutex_create(GNUNET_NO);
684   GNUNET_GC_get_configuration_value_string(core_api->cfg, "SMTP", "SERVER",
685                                            "localhost:25", &smtp_server_name);
686   sa.sa_handler = SIG_IGN;
687   sigemptyset(&sa.sa_mask);
688   sa.sa_flags = 0;
689   sigaction(SIGPIPE, &sa, &old_handler);
690
691   smtpAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP;
692   smtpAPI.mtu = mtu - sizeof(SMTPMessage);
693   smtpAPI.cost = 50;
694   smtpAPI.hello_verify = &api_verify_hello;
695   smtpAPI.hello_create = &api_create_hello;
696   smtpAPI.connect = &api_connect;
697   smtpAPI.send = &api_send;
698   smtpAPI.associate = &api_associate;
699   smtpAPI.disconnect = &api_disconnect;
700   smtpAPI.server_start = &api_start_transport_server;
701   smtpAPI.server_stop = &api_stop_transport_server;
702   smtpAPI.hello_to_address = &api_hello_to_address;
703   smtpAPI.send_now_test = &api_test_would_try;
704   return &smtpAPI;
705 }
706
707 void
708 donetransport_smtp()
709 {
710   sigaction(SIGPIPE, &old_handler, NULL);
711   GNUNET_free(smtp_server_name);
712   if (stats != NULL)
713     {
714       core_api->service_release(stats);
715       stats = NULL;
716     }
717   GNUNET_mutex_destroy(lock);
718   lock = NULL;
719   unlink(pipename);
720   GNUNET_free(pipename);
721   pipename = NULL;
722   GNUNET_free(email);
723   email = NULL;
724 }
725
726 /* end of smtp.c */