2 This file is part of GNUnet
3 (C) 2003, 2004, 2005, 2006, 2007 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file transport/plugin_transport_smtp.c
23 * @brief Implementation of the SMTP transport service
24 * @author Christian Grothoff
25 * @author Renaldo Ferreira
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"
39 * The default maximum size of each outbound SMTP message.
41 #define SMTP_MESSAGE_SIZE 65528
43 #define DEBUG_SMTP GNUNET_EXTRA_LOGGING
45 #define FILTER_STRING_SIZE 64
47 /* how long can a line in base64 encoded
48 mime text be? (in characters, excluding "\n") */
49 #define MAX_CHAR_PER_LINE 76
54 * Host-Address in a SMTP network.
60 * Filter line that every sender must include in the E-mails such
61 * that the receiver can effectively filter out the GNUnet traffic
64 char filter[FILTER_STRING_SIZE];
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.
71 char senderAddress[0];
75 GNUNET_NETWORK_STRUCT_BEGIN
78 * Encapsulation of a GNUnet message in the SMTP mail body (before
83 GNUNET_MessageHeader header;
86 * What is the identity of the sender (GNUNET_hash of public key)
88 GNUNET_PeerIdentity sender;
91 GNUNET_NETWORK_STRUCT_END
93 /* *********** globals ************* */
96 * apis (our advertised API and the core api )
98 static GNUNET_CoreAPIForTransport *coreAPI;
100 static struct GNUNET_GE_Context *ectx;
103 * Thread that listens for inbound messages
105 static struct GNUNET_ThreadHandle *dispatchThread;
108 * Flag to indicate that server has been shut down.
110 static int smtp_shutdown = GNUNET_YES;
113 * Set to the SMTP server hostname (and port) for outgoing messages.
115 static char *smtp_server_name;
117 static char *pipename;
120 * Lock for uses of libesmtp (not thread-safe).
122 static struct GNUNET_Mutex *lock;
125 * Old handler for SIGPIPE (kept to be able to restore).
127 static struct sigaction old_handler;
131 static GNUNET_TransportAPI smtpAPI;
133 static GNUNET_Stats_ServiceAPI *stats;
135 static int stat_bytesReceived;
137 static int stat_bytesSent;
139 static int stat_bytesDropped;
142 * How many e-mails are we allowed to send per hour?
144 static unsigned long long rate_limit;
146 static GNUNET_CronTime last_transmission;
148 /** ******************** Base64 encoding ***********/
152 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/";
155 * Encode into Base64.
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,
161 * @return the size of the output
164 base64_encode (const char *data, unsigned int len, char **output)
171 /* (*output)[ret++] = '\r'; \*/
173 if ( (ret % MAX_CHAR_PER_LINE) == 0) { \
174 (*output)[ret++] = '\n'; \
179 (((len * 4 / 3) + 8) * (MAX_CHAR_PER_LINE +
180 2)) / MAX_CHAR_PER_LINE);
181 /* message must start with \r\n for libesmtp */
186 for (i = 0; i < len; ++i)
188 c = (data[i] >> 2) & 0x3f;
189 opt[ret++] = cvt[(int) c];
191 c = (data[i] << 4) & 0x3f;
193 c |= (data[i] >> 4) & 0x0f;
194 opt[ret++] = cvt[(int) c];
198 c = (data[i] << 2) & 0x3f;
200 c |= (data[i] >> 6) & 0x03;
201 opt[ret++] = cvt[(int) c];
207 opt[ret++] = FILLCHAR;
213 opt[ret++] = cvt[(int) c];
218 opt[ret++] = FILLCHAR;
222 opt[ret++] = FILLCHAR;
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\
230 :((a) == '/') ? 63 : -1)
232 * Decode from Base64.
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,
238 * @return the size of the output
241 base64_decode (const char *data, unsigned int len, char **output)
246 unsigned int ret = 0;
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"); \
251 if (i >= len) goto END; \
254 *output = GNUNET_malloc ((len * 3 / 4) + 8);
256 GNUNET_GE_LOG (ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
257 "base64_decode decoding len=%d\n", len);
259 for (i = 0; i < len; ++i)
262 if (data[i] == FILLCHAR)
264 c = (char) cvtfind (data[i]);
267 c1 = (char) cvtfind (data[i]);
268 c = (c << 2) | ((c1 >> 4) & 0x3);
269 (*output)[ret++] = c;
276 c = (char) cvtfind (c);
277 c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
278 (*output)[ret++] = c1;
287 c1 = (char) cvtfind (c1);
288 c = ((c << 6) & 0xc0) | c1;
289 (*output)[ret++] = c;
296 /* ********************* the real stuff ******************* */
298 #define strAUTOncmp(a,b) strncmp(a,b,strlen(b))
301 * Listen to the pipe, decode messages and send to core.
304 listenAndDistribute (void *unused)
307 unsigned int linesize;
313 GNUNET_TransportPacket *coreMP;
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... */
320 #define READLINE(l,limit) \
321 do { retl = fgets(l, (limit), fdes); \
322 if ( (retl == NULL) || (smtp_shutdown == GNUNET_YES)) {\
325 if (coreAPI->load_monitor != NULL) \
326 GNUNET_network_monitor_notify_transmission(coreAPI->load_monitor, GNUNET_ND_DOWNLOAD, strlen(retl)); \
330 while (smtp_shutdown == GNUNET_NO)
332 fd = OPEN (pipename, O_RDONLY | O_ASYNC);
335 if (smtp_shutdown == GNUNET_NO)
336 GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS);
339 fdes = fdopen (fd, "r");
340 while (smtp_shutdown == GNUNET_NO)
342 /* skip until end of header */
345 READLINE (line, linesize);
347 while ((line[0] != '\r') && (line[0] != '\n')); /* expect newline */
348 READLINE (line, linesize); /* read base64 encoded message; decode, process */
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! */
357 size = base64_decode (line, pos, &out);
358 if (size < sizeof (SMTPMessage))
360 GNUNET_GE_BREAK (ectx, 0);
365 mp = (SMTPMessage *) &out[size - sizeof (SMTPMessage)];
366 if (ntohs (mp->header.size) != size)
369 GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
370 _("Received malformed message via %s. Ignored.\n"),
374 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
375 "Size returned by base64=%d, in the msg=%d.\n", size,
382 stats->change (stat_bytesReceived, size);
383 coreMP = GNUNET_malloc (sizeof (GNUNET_TransportPacket));
385 coreMP->size = size - sizeof (SMTPMessage);
386 coreMP->tsession = NULL;
387 coreMP->sender = mp->sender;
389 GNUNET_GE_LOG (ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
390 "SMTP message passed to the core.\n");
393 coreAPI->receive (coreMP);
397 GNUNET_GE_LOG (ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
398 "SMTP message processed.\n");
407 /* *************** API implementation *************** */
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
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
419 api_verify_hello (const GNUNET_MessageHello * hello)
421 const EmailAddress *maddr;
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'))
429 GNUNET_GE_BREAK (ectx, 0);
430 return GNUNET_SYSERR; /* obviously invalid */
432 if (NULL == strstr (maddr->filter, ": "))
433 return GNUNET_SYSERR;
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.
442 * @return hello on success, NULL on error
444 static GNUNET_MessageHello *
447 GNUNET_MessageHello *msg;
452 GNUNET_GC_get_configuration_value_string (coreAPI->cfg, "SMTP", "FILTER",
453 "X-mailer: GNUnet", &filter);
454 if (NULL == strstr (filter, ": "))
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);
462 if (strlen (filter) > FILTER_STRING_SIZE)
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);
468 i = (strlen (email) + 8) & (~7); /* make multiple of 8 */
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);
486 struct GetMessageClosure
494 get_message (void **buf, int *len, void *cls)
496 struct GetMessageClosure *gmc = cls;
504 if (gmc->pos == gmc->esize)
505 return NULL; /* done */
507 gmc->pos = gmc->esize;
512 * Send a message to the specified remote node.
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
521 api_send (GNUNET_TSession * tsession, const void *msg, const unsigned int size,
524 const GNUNET_MessageHello *hello;
525 const EmailAddress *haddr;
530 struct GetMessageClosure gm_cls;
531 smtp_session_t session;
532 smtp_message_t message;
533 smtp_recipient_t recipient;
539 if (smtp_shutdown == GNUNET_YES)
540 return GNUNET_SYSERR;
541 if ((size == 0) || (size > smtpAPI.mtu))
543 GNUNET_GE_BREAK (ectx, 0);
544 return GNUNET_SYSERR;
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;
552 hello = (const GNUNET_MessageHello *) tsession->internal;
554 return GNUNET_SYSERR;
555 GNUNET_mutex_lock (lock);
556 session = smtp_create_session ();
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,
564 GNUNET_mutex_unlock (lock);
565 return GNUNET_SYSERR;
567 if (0 == smtp_set_server (session, smtp_server_name))
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,
574 smtp_destroy_session (session);
575 GNUNET_mutex_unlock (lock);
576 return GNUNET_SYSERR;
578 haddr = (const EmailAddress *) &hello[1];
579 message = smtp_add_message (session);
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,
587 smtp_destroy_session (session);
588 GNUNET_mutex_unlock (lock);
589 return GNUNET_SYSERR;
591 smtp_set_header (message, "To", NULL, haddr->senderAddress);
592 smtp_set_header (message, "From", NULL, email);
594 filter = GNUNET_strdup (haddr->filter);
595 fvalue = strstr (filter, ": ");
596 GNUNET_GE_ASSERT (NULL, NULL != fvalue);
599 if (0 == smtp_set_header (message, filter, fvalue))
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,
606 smtp_destroy_session (session);
607 GNUNET_mutex_unlock (lock);
608 GNUNET_free (filter);
609 return GNUNET_SYSERR;
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;
620 gm_cls.esize = base64_encode (m, size + sizeof (SMTPMessage), &gm_cls.ebody);
622 if (0 == smtp_size_set_estimate (message, gm_cls.esize))
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,
630 if (0 == smtp_set_messagecb (message, &get_message, &gm_cls))
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,
637 smtp_destroy_session (session);
638 GNUNET_mutex_unlock (lock);
639 GNUNET_free (gm_cls.ebody);
640 return GNUNET_SYSERR;
642 recipient = smtp_add_recipient (message, haddr->senderAddress);
643 if (recipient == NULL)
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,
650 smtp_destroy_session (session);
651 GNUNET_mutex_unlock (lock);
652 return GNUNET_SYSERR;
654 if (0 == smtp_start_session (session))
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,
661 smtp_destroy_session (session);
662 GNUNET_mutex_unlock (lock);
663 GNUNET_free (gm_cls.ebody);
664 return GNUNET_SYSERR;
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);
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
686 api_connect (const GNUNET_MessageHello * hello, GNUNET_TSession ** tsessionPtr,
689 GNUNET_TSession *tsession;
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;
701 * Disconnect from a remote node.
703 * @param tsession the session that is closed
704 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
707 api_disconnect (GNUNET_TSession * tsession)
709 if (tsession != NULL)
711 if (tsession->internal != NULL)
712 GNUNET_free (tsession->internal);
713 GNUNET_free (tsession);
719 * Start the server process to receive inbound traffic.
720 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
723 api_start_transport_server ()
725 smtp_shutdown = GNUNET_NO;
726 /* initialize SMTP network */
727 dispatchThread = GNUNET_thread_create (&listenAndDistribute, NULL, 1024 * 4);
728 if (dispatchThread == NULL)
730 GNUNET_GE_DIE_STRERROR (ectx,
731 GNUNET_GE_ADMIN | GNUNET_GE_BULK | GNUNET_GE_FATAL,
733 return GNUNET_SYSERR;
739 * Shutdown the server process (stop receiving inbound traffic). Maybe
743 api_stop_transport_server ()
747 smtp_shutdown = GNUNET_YES;
748 GNUNET_thread_stop_sleep (dispatchThread);
749 GNUNET_thread_join (dispatchThread, &unused);
754 * Convert SMTP hello to an IP address (always fails).
757 api_hello_to_address (const GNUNET_MessageHello * hello, void **sa,
758 unsigned int *sa_len)
760 return GNUNET_SYSERR;
767 api_associate (GNUNET_TSession * tsession)
769 return GNUNET_SYSERR; /* SMTP connections can never be associated */
773 * Always succeeds (for now; we should look at adding
774 * frequency limits to SMTP in the future!).
777 api_test_would_try (GNUNET_TSession * tsession, unsigned int size,
780 return GNUNET_OK; /* we always try... */
784 * The exported method. Makes the core api available via a global and
785 * returns the smtp transport API.
787 GNUNET_TransportAPI *
788 inittransport_smtp (struct GNUNET_CoreAPIForTransport * core)
790 unsigned long long mtu;
795 if (!GNUNET_GC_have_configuration_value (coreAPI->cfg, "SMTP", "EMAIL"))
797 GNUNET_GE_LOG (ectx, GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
799 ("No email-address specified, can not start SMTP transport.\n"));
802 GNUNET_GC_get_configuration_value_number (coreAPI->cfg, "SMTP", "MTU", 1200,
804 SMTP_MESSAGE_SIZE, &mtu);
805 GNUNET_GC_get_configuration_value_number (coreAPI->cfg, "SMTP", "RATELIMIT",
806 0, 0, 1024 * 1024, &rate_limit);
807 stats = coreAPI->service_request ("stats");
811 stats->create (gettext_noop ("# bytes received via SMTP"));
812 stat_bytesSent = stats->create (gettext_noop ("# bytes sent via SMTP"));
814 stats->create (gettext_noop ("# bytes dropped by SMTP (outgoing)"));
816 GNUNET_GC_get_configuration_value_filename (coreAPI->cfg, "SMTP", "PIPE", &pipename);
818 if (0 != mkfifo (pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH))
820 GNUNET_GE_LOG_STRERROR (ectx,
821 GNUNET_GE_ADMIN | GNUNET_GE_BULK | GNUNET_GE_FATAL,
823 GNUNET_free (pipename);
824 coreAPI->service_release (stats);
828 /* we need to allow the mailer program to send us messages;
829 * easiest done by giving it write permissions (see Mantis #1142) */
830 if (0 != chmod (pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH))
831 GNUNET_GE_LOG_STRERROR (ectx,
832 GNUNET_GE_ADMIN | GNUNET_GE_BULK |
833 GNUNET_GE_WARNING, "chmod");
834 GNUNET_GC_get_configuration_value_string (coreAPI->cfg, "SMTP", "EMAIL", NULL,
836 lock = GNUNET_mutex_create (GNUNET_NO);
837 GNUNET_GC_get_configuration_value_string (coreAPI->cfg, "SMTP", "SERVER",
838 "localhost:25", &smtp_server_name);
839 sa.sa_handler = SIG_IGN;
840 sigemptyset (&sa.sa_mask);
842 sigaction (SIGPIPE, &sa, &old_handler);
844 smtpAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP;
845 smtpAPI.mtu = mtu - sizeof (SMTPMessage);
847 smtpAPI.hello_verify = &api_verify_hello;
848 smtpAPI.hello_create = &api_create_hello;
849 smtpAPI.connect = &api_connect;
850 smtpAPI.send = &api_send;
851 smtpAPI.associate = &api_associate;
852 smtpAPI.disconnect = &api_disconnect;
853 smtpAPI.server_start = &api_start_transport_server;
854 smtpAPI.server_stop = &api_stop_transport_server;
855 smtpAPI.hello_to_address = &api_hello_to_address;
856 smtpAPI.send_now_test = &api_test_would_try;
861 donetransport_smtp ()
863 sigaction (SIGPIPE, &old_handler, NULL);
864 GNUNET_free (smtp_server_name);
867 coreAPI->service_release (stats);
870 GNUNET_mutex_destroy (lock);
873 GNUNET_free (pipename);