1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-smtp-transport.c : class for a smtp transport */
5 * Authors: Jeffrey Stedfast <fejj@helixcode.com>
7 * Copyright (C) 2000 Helix Code, Inc. (www.helixcode.com)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
42 #include "camel-mime-filter-crlf.h"
43 #include "camel-mime-filter-linewrap.h"
44 #include "camel-stream-filter.h"
45 #include "camel-smtp-transport.h"
46 #include "camel-mime-message.h"
47 #include "camel-multipart.h"
48 #include "camel-mime-part.h"
49 #include "camel-stream-buffer.h"
50 #include "camel-tcp-stream.h"
51 #include "camel-tcp-stream-raw.h"
53 #include "camel-tcp-stream-ssl.h"
57 #include "camel-tcp-stream-openssl.h"
59 #include "camel-session.h"
60 #include "camel-exception.h"
61 #include "camel-sasl.h"
62 #include "string-utils.h"
66 /* Specified in RFC 821 */
69 /* camel smtp transport class prototypes */
70 static gboolean smtp_can_send (CamelTransport *transport, CamelMedium *message);
71 static gboolean smtp_send (CamelTransport *transport, CamelMedium *message, CamelException *ex);
72 static gboolean smtp_send_to (CamelTransport *transport, CamelMedium *message, GList *recipients, CamelException *ex);
74 /* support prototypes */
75 static void smtp_construct (CamelService *service, CamelSession *session,
76 CamelProvider *provider, CamelURL *url,
78 static gboolean smtp_connect (CamelService *service, CamelException *ex);
79 static gboolean smtp_disconnect (CamelService *service, gboolean clean, CamelException *ex);
80 static GHashTable *esmtp_get_authtypes (gchar *buffer);
81 static GList *query_auth_types (CamelService *service, CamelException *ex);
82 static char *get_name (CamelService *service, gboolean brief);
84 static gboolean smtp_helo (CamelSmtpTransport *transport, CamelException *ex);
85 static gboolean smtp_auth (CamelSmtpTransport *transport, const char *mech, CamelException *ex);
86 static gboolean smtp_mail (CamelSmtpTransport *transport, const char *sender,
87 gboolean has_8bit_parts, CamelException *ex);
88 static gboolean smtp_rcpt (CamelSmtpTransport *transport, const char *recipient, CamelException *ex);
89 static gboolean smtp_data (CamelSmtpTransport *transport, CamelMedium *message,
90 gboolean has_8bit_parts, CamelException *ex);
91 static gboolean smtp_rset (CamelSmtpTransport *transport, CamelException *ex);
92 static gboolean smtp_quit (CamelSmtpTransport *transport, CamelException *ex);
94 /* private data members */
95 static CamelTransportClass *parent_class = NULL;
98 camel_smtp_transport_class_init (CamelSmtpTransportClass *camel_smtp_transport_class)
100 CamelTransportClass *camel_transport_class =
101 CAMEL_TRANSPORT_CLASS (camel_smtp_transport_class);
102 CamelServiceClass *camel_service_class =
103 CAMEL_SERVICE_CLASS (camel_smtp_transport_class);
105 parent_class = CAMEL_TRANSPORT_CLASS (camel_type_get_global_classfuncs (camel_transport_get_type ()));
107 /* virtual method overload */
108 camel_service_class->construct = smtp_construct;
109 camel_service_class->connect = smtp_connect;
110 camel_service_class->disconnect = smtp_disconnect;
111 camel_service_class->query_auth_types = query_auth_types;
112 camel_service_class->get_name = get_name;
114 camel_transport_class->can_send = smtp_can_send;
115 camel_transport_class->send = smtp_send;
116 camel_transport_class->send_to = smtp_send_to;
120 camel_smtp_transport_init (gpointer object)
122 CamelTransport *transport = CAMEL_TRANSPORT (object);
124 transport->supports_8bit = FALSE;
128 camel_smtp_transport_get_type (void)
130 static CamelType camel_smtp_transport_type = CAMEL_INVALID_TYPE;
132 if (camel_smtp_transport_type == CAMEL_INVALID_TYPE) {
133 camel_smtp_transport_type =
134 camel_type_register (CAMEL_TRANSPORT_TYPE, "CamelSmtpTransport",
135 sizeof (CamelSmtpTransport),
136 sizeof (CamelSmtpTransportClass),
137 (CamelObjectClassInitFunc) camel_smtp_transport_class_init,
139 (CamelObjectInitFunc) camel_smtp_transport_init,
143 return camel_smtp_transport_type;
147 smtp_construct (CamelService *service, CamelSession *session,
148 CamelProvider *provider, CamelURL *url,
151 CamelSmtpTransport *smtp_transport = CAMEL_SMTP_TRANSPORT (service);
153 CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex);
155 if (camel_url_get_param (url, "use_ssl"))
156 smtp_transport->use_ssl = TRUE;
160 get_smtp_error_string (int error)
162 /* SMTP error codes grabbed from rfc821 */
165 /* looks like a read problem, check errno */
166 return g_strerror (errno);
168 return _("Syntax error, command unrecognized");
170 return _("Syntax error in parameters or arguments");
172 return _("Command not implemented");
174 return _("Command parameter not implemented");
176 return _("System status, or system help reply");
178 return _("Help message");
180 return _("Service ready");
182 return _("Service closing transmission channel");
184 return _("Service not available, closing transmission channel");
186 return _("Requested mail action okay, completed");
188 return _("User not local; will forward to <forward-path>");
190 return _("Requested mail action not taken: mailbox unavailable");
192 return _("Requested action not taken: mailbox unavailable");
194 return _("Requested action aborted: error in processing");
196 return _("User not local; please try <forward-path>");
198 return _("Requested action not taken: insufficient system storage");
200 return _("Requested mail action aborted: exceeded storage allocation");
202 return _("Requested action not taken: mailbox name not allowed");
204 return _("Start mail input; end with <CRLF>.<CRLF>");
206 return _("Transaction failed");
208 /* AUTH error codes: */
210 return _("A password transition is needed");
212 return _("Authentication mechanism is too weak");
214 return _("Encryption required for requested authentication mechanism");
216 return _("Temporary authentication failure");
218 return _("Authentication required");
226 connect_to_server (CamelService *service, CamelException *ex)
228 CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
229 CamelStream *tcp_stream;
230 gchar *respbuf = NULL;
235 if (!CAMEL_SERVICE_CLASS (parent_class)->connect (service, ex))
238 h = camel_service_gethost (service, ex);
242 /* set some smtp transport defaults */
243 transport->is_esmtp = FALSE;
244 transport->authtypes = NULL;
245 CAMEL_TRANSPORT (transport)->supports_8bit = FALSE;
247 port = service->url->port ? service->url->port : SMTP_PORT;
249 #if defined(HAVE_NSS) || defined(HAVE_OPENSSL)
250 if (transport->use_ssl) {
251 port = service->url->port ? service->url->port : 465;
253 /* use the preferred implementation - NSS */
254 tcp_stream = camel_tcp_stream_ssl_new (service, service->url->host);
256 tcp_stream = camel_tcp_stream_openssl_new (service, service->url->host);
257 #endif /* HAVE_NSS */
259 tcp_stream = camel_tcp_stream_raw_new ();
262 tcp_stream = camel_tcp_stream_raw_new ();
263 #endif /* HAVE_NSS || HAVE_OPENSSL */
265 ret = camel_tcp_stream_connect (CAMEL_TCP_STREAM (tcp_stream), h, port);
268 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
269 _("Could not connect to %s (port %d): %s"),
270 service->url->host, port,
276 /* get the localaddr - needed later by smtp_helo */
277 addrlen = sizeof (transport->localaddr);
279 if (transport->use_ssl) {
280 PRFileDesc *sockfd = camel_tcp_stream_get_socket (CAMEL_TCP_STREAM (tcp_stream));
284 PR_GetSockName (sockfd, &addr);
285 memset (hname, 0, sizeof (hname));
286 PR_NetAddrToString (&addr, hname, 1023);
288 inet_aton (hname, (struct in_addr *)&transport->localaddr.sin_addr);
290 int sockfd = GPOINTER_TO_INT (camel_tcp_stream_get_socket (CAMEL_TCP_STREAM (tcp_stream)));
292 getsockname (sockfd, (struct sockaddr *)&transport->localaddr, &addrlen);
295 getsockname (CAMEL_TCP_STREAM_RAW (tcp_stream)->sockfd,
296 (struct sockaddr *)&transport->localaddr, &addrlen);
297 #endif /* HAVE_NSS */
299 transport->ostream = tcp_stream;
300 transport->istream = camel_stream_buffer_new (tcp_stream, CAMEL_STREAM_BUFFER_READ);
302 /* Read the greeting, note whether the server is ESMTP or not. */
304 /* Check for "220" */
306 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
307 if (!respbuf || strncmp (respbuf, "220", 3)) {
310 error = respbuf ? atoi (respbuf) : 0;
312 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
313 _("Welcome response error: %s: possibly non-fatal"),
314 get_smtp_error_string (error));
317 if (strstr (respbuf, "ESMTP"))
318 transport->is_esmtp = TRUE;
319 } while (*(respbuf+3) == '-'); /* if we got "220-" then loop again */
322 /* send HELO (or EHLO, depending on the service type) */
323 if (!transport->is_esmtp) {
324 /* If we did not auto-detect ESMTP, we should still send EHLO */
325 transport->is_esmtp = TRUE;
326 if (!smtp_helo (transport, NULL)) {
327 /* Okay, apprently this server doesn't support ESMTP */
328 transport->is_esmtp = FALSE;
329 smtp_helo (transport, ex);
333 smtp_helo (transport, ex);
340 smtp_connect (CamelService *service, CamelException *ex)
342 CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
344 if (!connect_to_server (service, ex))
347 /* check to see if AUTH is required, if so...then AUTH ourselves */
348 if (service->url->authmech) {
349 CamelSession *session = camel_service_get_session (service);
350 CamelServiceAuthType *authtype;
351 gboolean authenticated = FALSE;
354 if (!transport->is_esmtp || !g_hash_table_lookup (transport->authtypes, service->url->authmech)) {
355 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
356 _("SMTP server %s does not support requested "
357 "authentication type %s"), service->url->host,
358 service->url->authmech);
359 camel_service_disconnect (service, TRUE, NULL);
363 authtype = camel_sasl_authtype (service->url->authmech);
365 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
366 _("No support for authentication type %s"),
367 service->url->authmech);
368 camel_service_disconnect (service, TRUE, NULL);
372 if (!authtype->need_password) {
373 /* authentication mechanism doesn't need a password,
374 so if it fails there's nothing we can do */
375 authenticated = smtp_auth (transport, authtype->authproto, ex);
376 if (!authenticated) {
377 camel_service_disconnect (service, TRUE, NULL);
382 /* keep trying to login until either we succeed or the user cancels */
383 while (!authenticated) {
385 /* We need to un-cache the password before prompting again */
386 camel_session_forget_password (
387 session, service, "password", ex);
388 g_free (service->url->passwd);
389 service->url->passwd = NULL;
392 if (!service->url->passwd) {
395 prompt = g_strdup_printf (_("%sPlease enter the SMTP password for %s@%s"),
396 errbuf ? errbuf : "", service->url->user,
399 service->url->passwd =
400 camel_session_get_password (
401 session, prompt, TRUE,
402 service, "password", ex);
408 if (!service->url->passwd) {
409 camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
410 _("You didn't enter a password."));
411 camel_service_disconnect (service, TRUE, NULL);
416 authenticated = smtp_auth (transport, authtype->authproto, ex);
417 if (!authenticated) {
418 errbuf = g_strdup_printf (_("Unable to authenticate "
419 "to SMTP server.\n%s\n\n"),
420 camel_exception_get_description (ex));
421 camel_exception_clear (ex);
425 /* The spec says we have to re-EHLO, but some servers
426 * we won't bother to name don't want you to... so ignore
429 smtp_helo (transport, NULL);
436 authtypes_free (gpointer key, gpointer value, gpointer data)
445 smtp_disconnect (CamelService *service, gboolean clean, CamelException *ex)
447 CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
449 /*if (!service->connected)
454 /* send the QUIT command to the SMTP server */
455 smtp_quit (transport, ex);
458 if (!CAMEL_SERVICE_CLASS (parent_class)->disconnect (service, clean, ex))
461 if (transport->authtypes) {
462 g_hash_table_foreach_remove (transport->authtypes, authtypes_free, NULL);
463 g_hash_table_destroy (transport->authtypes);
464 transport->authtypes = NULL;
467 camel_object_unref (CAMEL_OBJECT (transport->ostream));
468 camel_object_unref (CAMEL_OBJECT (transport->istream));
470 transport->ostream = NULL;
471 transport->istream = NULL;
477 esmtp_get_authtypes (char *buffer)
479 GHashTable *table = NULL;
482 /* advance to the first token */
483 for (start = buffer; isspace (*start) || *start == '='; start++);
485 if (!*start) return NULL;
487 table = g_hash_table_new (g_str_hash, g_str_equal);
492 /* advance to the end of the token */
493 for (end = start; *end && !isspace (*end); end++);
495 type = g_strndup (start, end - start);
496 g_hash_table_insert (table, g_strdup (type), type);
498 /* advance to the next token */
499 for (start = end; isspace (*start); start++);
506 query_auth_types (CamelService *service, CamelException *ex)
508 CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
509 CamelServiceAuthType *authtype;
510 GList *types, *t, *next;
512 if (!connect_to_server (service, ex))
515 types = g_list_copy (service->provider->authtypes);
516 for (t = types; t; t = next) {
520 if (!g_hash_table_lookup (transport->authtypes, authtype->authproto)) {
521 types = g_list_remove_link (types, t);
526 smtp_disconnect (service, TRUE, NULL);
531 get_name (CamelService *service, gboolean brief)
534 return g_strdup_printf (_("SMTP server %s"), service->url->host);
536 return g_strdup_printf (_("SMTP mail delivery via %s"),
542 smtp_can_send (CamelTransport *transport, CamelMedium *message)
544 return CAMEL_IS_MIME_MESSAGE (message);
548 smtp_send_to (CamelTransport *transport, CamelMedium *message,
549 GList *recipients, CamelException *ex)
551 CamelSmtpTransport *smtp_transport = CAMEL_SMTP_TRANSPORT (transport);
552 const CamelInternetAddress *cia;
555 gboolean has_8bit_parts;
558 cia = camel_mime_message_get_from(CAMEL_MIME_MESSAGE (message));
560 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
561 _("Cannot send message: "
562 "sender address not defined."));
566 if (!camel_internet_address_get (cia, 0, NULL, &addr)) {
567 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
568 _("Cannot send message: "
569 "sender address not valid."));
573 /* find out if the message has 8bit mime parts */
574 has_8bit_parts = camel_mime_message_has_8bit_parts (CAMEL_MIME_MESSAGE (message));
576 /* rfc1652 (8BITMIME) requires that you notify the ESMTP daemon that
577 you'll be sending an 8bit mime message at "MAIL FROM:" time. */
578 smtp_mail (smtp_transport, addr, has_8bit_parts, ex);
581 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
582 _("Cannot send message: "
583 "no recipients defined."));
587 for (r = recipients; r; r = r->next) {
588 recipient = (char *) r->data;
589 if (!smtp_rcpt (smtp_transport, recipient, ex)) {
596 /* passing in has_8bit_parts saves time as we don't have to
597 recurse through the message all over again if the user is
598 not sending 8bit mime parts */
599 if (!smtp_data (smtp_transport, message, has_8bit_parts, ex))
602 /* reset the service for our next transfer session */
603 smtp_rset (smtp_transport, ex);
609 smtp_send (CamelTransport *transport, CamelMedium *message, CamelException *ex)
611 const CamelInternetAddress *to, *cc, *bcc;
612 GList *recipients = NULL;
615 to = camel_mime_message_get_recipients (CAMEL_MIME_MESSAGE (message), CAMEL_RECIPIENT_TYPE_TO);
616 cc = camel_mime_message_get_recipients (CAMEL_MIME_MESSAGE (message), CAMEL_RECIPIENT_TYPE_CC);
617 bcc = camel_mime_message_get_recipients (CAMEL_MIME_MESSAGE (message), CAMEL_RECIPIENT_TYPE_BCC);
619 /* get all of the To addresses into our recipient list */
620 len = CAMEL_ADDRESS (to)->addresses->len;
621 for (index = 0; index < len; index++) {
624 if (camel_internet_address_get (to, index, NULL, &addr))
625 recipients = g_list_append (recipients, g_strdup (addr));
628 /* get all of the Cc addresses into our recipient list */
629 len = CAMEL_ADDRESS (cc)->addresses->len;
630 for (index = 0; index < len; index++) {
633 if (camel_internet_address_get (cc, index, NULL, &addr))
634 recipients = g_list_append (recipients, g_strdup (addr));
637 /* get all of the Bcc addresses into our recipient list */
638 len = CAMEL_ADDRESS (bcc)->addresses->len;
639 for (index = 0; index < len; index++) {
642 if (camel_internet_address_get (bcc, index, NULL, &addr))
643 recipients = g_list_append (recipients, g_strdup (addr));
646 return smtp_send_to (transport, message, recipients, ex);
650 smtp_helo (CamelSmtpTransport *transport, CamelException *ex)
652 /* say hello to the server */
653 gchar *cmdbuf, *respbuf = NULL;
654 struct hostent *host;
656 /* get the local host name */
657 host = gethostbyaddr ((gchar *)&transport->localaddr.sin_addr, sizeof (transport->localaddr.sin_addr), AF_INET);
659 /* hiya server! how are you today? */
660 if (transport->is_esmtp) {
661 if (host && host->h_name)
662 cmdbuf = g_strdup_printf ("EHLO %s\r\n", host->h_name);
664 cmdbuf = g_strdup_printf ("EHLO [%s]\r\n", inet_ntoa (transport->localaddr.sin_addr));
666 if (host && host->h_name)
667 cmdbuf = g_strdup_printf ("HELO %s\r\n", host->h_name);
669 cmdbuf = g_strdup_printf ("HELO [%s]\r\n", inet_ntoa (transport->localaddr.sin_addr));
672 d(fprintf (stderr, "sending : %s", cmdbuf));
673 if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
675 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
676 _("HELO request timed out: %s: non-fatal"),
683 /* Check for "250" */
685 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
687 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
689 if (!respbuf || strncmp (respbuf, "250", 3)) {
692 error = respbuf ? atoi (respbuf) : 0;
694 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
695 _("HELO response error: %s: non-fatal"),
696 get_smtp_error_string (error));
700 if (strstrcase (respbuf, "8BITMIME")) {
701 d(fprintf (stderr, "This server supports 8bit MIME\n"));
702 CAMEL_TRANSPORT (transport)->supports_8bit = TRUE;
705 /* Only parse authtypes if we don't already have them */
706 if (transport->is_esmtp && strstr (respbuf, "AUTH") && !transport->authtypes) {
707 /* parse for supported AUTH types */
708 char *auths = strstr (respbuf, "AUTH") + 4;
710 transport->authtypes = esmtp_get_authtypes (auths);
712 } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
719 smtp_auth (CamelSmtpTransport *transport, const char *mech, CamelException *ex)
721 gchar *cmdbuf, *respbuf = NULL, *challenge;
724 sasl = camel_sasl_new ("smtp", mech, CAMEL_SERVICE (transport));
727 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
728 _("Error creating SASL authentication object."));
732 challenge = camel_sasl_challenge_base64 (sasl, NULL, ex);
734 cmdbuf = g_strdup_printf ("AUTH %s %s\r\n", mech, challenge);
737 cmdbuf = g_strdup_printf ("AUTH %s\r\n", mech);
739 d(fprintf (stderr, "sending : %s", cmdbuf));
740 if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
742 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
743 _("AUTH request timed out: %s"),
749 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
750 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
752 while (!camel_sasl_authenticated (sasl)) {
754 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
755 _("AUTH request timed out: %s"),
760 /* the server challenge/response should follow a 334 code */
761 if (strcmp (respbuf, "334")) {
763 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
764 _("AUTH request failed."));
769 for (challenge = respbuf + 4; isspace (*challenge); challenge++);
771 challenge = camel_sasl_challenge_base64 (sasl, challenge, ex);
773 if (camel_exception_is_set (ex))
776 /* send our challenge */
777 cmdbuf = g_strdup_printf ("%s\r\n", challenge);
779 d(fprintf (stderr, "sending : %s", cmdbuf));
780 if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
786 /* get the server's response */
787 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
788 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
791 /* check that the server says we are authenticated */
792 if (!respbuf || strncmp (respbuf, "235", 3)) {
800 /* Get the server out of "waiting for continuation data" mode. */
801 d(fprintf (stderr, "sending : *\n"));
802 camel_stream_write (transport->ostream, "*\r\n", 3);
803 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
804 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
807 if (!camel_exception_is_set (ex)) {
808 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
809 _("Bad authentication response from server.\n"));
813 camel_object_unref (CAMEL_OBJECT (sasl));
819 smtp_mail (CamelSmtpTransport *transport, const char *sender, gboolean has_8bit_parts, CamelException *ex)
821 /* we gotta tell the smtp server who we are. (our email addy) */
822 gchar *cmdbuf, *respbuf = NULL;
824 /* enclose address in <>'s since some SMTP daemons *require* that */
825 if (CAMEL_TRANSPORT (transport)->supports_8bit && has_8bit_parts)
826 cmdbuf = g_strdup_printf ("MAIL FROM: <%s> BODY=8BITMIME\r\n", sender);
828 cmdbuf = g_strdup_printf ("MAIL FROM: <%s>\r\n", sender);
830 d(fprintf (stderr, "sending : %s", cmdbuf));
832 if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
834 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
835 _("MAIL FROM request timed out: %s: mail not sent"),
842 /* Check for "250 Sender OK..." */
844 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
846 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
848 if (!respbuf || strncmp (respbuf, "250", 3)) {
851 error = respbuf ? atoi (respbuf) : 0;
853 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
854 _("MAIL FROM response error: %s: mail not sent"),
855 get_smtp_error_string (error));
858 } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
865 smtp_rcpt (CamelSmtpTransport *transport, const char *recipient, CamelException *ex)
867 /* we gotta tell the smtp server who we are going to be sending
869 gchar *cmdbuf, *respbuf = NULL;
871 /* enclose address in <>'s since some SMTP daemons *require* that */
872 cmdbuf = g_strdup_printf ("RCPT TO: <%s>\r\n", recipient);
874 d(fprintf (stderr, "sending : %s", cmdbuf));
876 if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
878 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
879 _("RCPT TO request timed out: %s: mail not sent"),
886 /* Check for "250 Sender OK..." */
888 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
890 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
892 if (!respbuf || strncmp (respbuf, "250", 3)) {
895 error = respbuf ? atoi (respbuf) : 0;
897 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
898 _("RCPT TO response error: %s: mail not sent"),
899 get_smtp_error_string (error));
902 } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
909 smtp_data (CamelSmtpTransport *transport, CamelMedium *message, gboolean has_8bit_parts, CamelException *ex)
911 /* now we can actually send what's important :p */
912 gchar *cmdbuf, *respbuf = NULL;
913 CamelStreamFilter *filtered_stream;
914 CamelMimeFilter *crlffilter;
916 /* if the message contains 8bit mime parts and the server
917 doesn't support it, encode 8bit parts to the best
918 encoding. This will also enforce an encoding to keep the lines in limit */
919 if (has_8bit_parts && !CAMEL_TRANSPORT (transport)->supports_8bit)
920 camel_mime_message_encode_8bit_parts (CAMEL_MIME_MESSAGE (message));
922 cmdbuf = g_strdup ("DATA\r\n");
924 d(fprintf (stderr, "sending : %s", cmdbuf));
926 if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
928 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
929 _("DATA request timed out: %s: mail not sent"),
935 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
937 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
939 if (!respbuf || strncmp (respbuf, "354", 3)) {
940 /* we should have gotten instructions on how to use the DATA command:
941 * 354 Enter mail, end with "." on a line by itself
945 error = respbuf ? atoi (respbuf) : 0;
947 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
948 _("DATA response error: %s: mail not sent"),
949 get_smtp_error_string (error));
956 /* setup stream filtering */
957 crlffilter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
958 filtered_stream = camel_stream_filter_new_with_stream (transport->ostream);
959 camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (crlffilter));
961 if (camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), CAMEL_STREAM (filtered_stream)) == -1) {
962 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
963 _("DATA send timed out: message termination: "
964 "%s: mail not sent"),
967 camel_object_unref (CAMEL_OBJECT (filtered_stream));
972 camel_stream_flush (CAMEL_STREAM (filtered_stream));
973 camel_object_unref (CAMEL_OBJECT (filtered_stream));
975 /* terminate the message body */
977 d(fprintf (stderr, "sending : \\r\\n.\\r\\n\n"));
979 if (camel_stream_write (transport->ostream, "\r\n.\r\n", 5) == -1) {
980 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
981 _("DATA send timed out: message termination: "
982 "%s: mail not sent"),
988 /* Check for "250 Sender OK..." */
990 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
992 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
994 if (!respbuf || strncmp (respbuf, "250", 3)) {
997 error = respbuf ? atoi (respbuf) : 0;
999 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1000 _("DATA response error: message termination: "
1001 "%s: mail not sent"),
1002 get_smtp_error_string (error));
1005 } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
1012 smtp_rset (CamelSmtpTransport *transport, CamelException *ex)
1014 /* we are going to reset the smtp server (just to be nice) */
1015 gchar *cmdbuf, *respbuf = NULL;
1017 cmdbuf = g_strdup ("RSET\r\n");
1019 d(fprintf (stderr, "sending : %s", cmdbuf));
1021 if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
1023 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1024 _("RSET request timed out: %s"),
1025 g_strerror (errno));
1031 /* Check for "250" */
1033 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
1035 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
1037 if (!respbuf || strncmp (respbuf, "250", 3)) {
1040 error = respbuf ? atoi (respbuf) : 0;
1042 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1043 _("RSET response error: %s"),
1044 get_smtp_error_string (error));
1047 } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
1054 smtp_quit (CamelSmtpTransport *transport, CamelException *ex)
1056 /* we are going to reset the smtp server (just to be nice) */
1057 gchar *cmdbuf, *respbuf = NULL;
1059 cmdbuf = g_strdup ("QUIT\r\n");
1061 d(fprintf (stderr, "sending : %s", cmdbuf));
1063 if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
1065 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1066 _("QUIT request timed out: %s: non-fatal"),
1067 g_strerror (errno));
1073 /* Check for "221" */
1075 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
1077 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
1079 if (!respbuf || strncmp (respbuf, "221", 3)) {
1082 error = respbuf ? atoi (respbuf) : 0;
1084 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1085 _("QUIT response error: %s: non-fatal"),
1086 get_smtp_error_string (error));
1089 } while (*(respbuf+3) == '-'); /* if we got "221-" then loop again */