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@ximian.com>
7 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of version 2 of the GNU Lesser General Public
11 * License as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
34 #include <sys/param.h>
35 #include <sys/types.h>
37 #include <glib/gi18n-lib.h>
39 #include "camel-smtp-settings.h"
40 #include "camel-smtp-transport.h"
50 extern gint camel_verbose_debug;
52 #define d(x) (camel_verbose_debug ? (x) : 0)
54 /* Specified in RFC 821 */
56 #define SMTPS_PORT 465
58 /* support prototypes */
59 static GHashTable * esmtp_get_authtypes (const guchar *buffer);
60 static gboolean smtp_helo (CamelSmtpTransport *transport,
61 GCancellable *cancellable,
63 static gboolean smtp_mail (CamelSmtpTransport *transport,
65 gboolean has_8bit_parts,
66 GCancellable *cancellable,
68 static gboolean smtp_rcpt (CamelSmtpTransport *transport,
69 const gchar *recipient,
70 GCancellable *cancellable,
72 static gboolean smtp_data (CamelSmtpTransport *transport,
73 CamelMimeMessage *message,
74 GCancellable *cancellable,
76 static gboolean smtp_rset (CamelSmtpTransport *transport,
77 GCancellable *cancellable,
79 static gboolean smtp_quit (CamelSmtpTransport *transport,
80 GCancellable *cancellable,
82 static void smtp_set_error (CamelSmtpTransport *transport,
84 GCancellable *cancellable,
87 /* Forward Declarations */
88 static void camel_network_service_init (CamelNetworkServiceInterface *interface);
90 G_DEFINE_TYPE_WITH_CODE (
94 G_IMPLEMENT_INTERFACE (
95 CAMEL_TYPE_NETWORK_SERVICE,
96 camel_network_service_init))
99 connect_to_server (CamelService *service,
100 GCancellable *cancellable,
103 CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
104 CamelNetworkSettings *network_settings;
105 CamelNetworkSecurityMethod method;
106 CamelSettings *settings;
107 CamelStream *tcp_stream;
108 gchar *respbuf = NULL;
109 gboolean success = TRUE;
112 if (!CAMEL_SERVICE_CLASS (camel_smtp_transport_parent_class)->
113 connect_sync (service, cancellable, error))
116 /* set some smtp transport defaults */
117 transport->flags = 0;
118 transport->authtypes = NULL;
120 settings = camel_service_ref_settings (service);
122 network_settings = CAMEL_NETWORK_SETTINGS (settings);
123 host = camel_network_settings_dup_host (network_settings);
124 method = camel_network_settings_get_security_method (network_settings);
126 g_object_unref (settings);
128 tcp_stream = camel_network_service_connect_sync (
129 CAMEL_NETWORK_SERVICE (service), cancellable, error);
131 if (tcp_stream == NULL) {
136 transport->connected = TRUE;
138 /* get the localaddr - needed later by smtp_helo */
139 transport->localaddr = camel_tcp_stream_get_local_address (CAMEL_TCP_STREAM (tcp_stream), &transport->localaddrlen);
141 transport->ostream = tcp_stream;
142 transport->istream = camel_stream_buffer_new (tcp_stream, CAMEL_STREAM_BUFFER_READ);
144 /* Read the greeting, note whether the server is ESMTP or not. */
146 /* Check for "220" */
148 respbuf = camel_stream_buffer_read_line (
149 CAMEL_STREAM_BUFFER (transport->istream),
151 if (respbuf == NULL) {
152 g_prefix_error (error, _("Welcome response error: "));
153 transport->connected = FALSE;
157 if (strncmp (respbuf, "220", 3)) {
159 transport, respbuf, cancellable, error);
160 g_prefix_error (error, _("Welcome response error: "));
165 } while (*(respbuf+3) == '-'); /* if we got "220-" then loop again */
168 /* Try sending EHLO */
169 transport->flags |= CAMEL_SMTP_TRANSPORT_IS_ESMTP;
170 if (!smtp_helo (transport, cancellable, error)) {
171 if (!transport->connected) {
176 /* Fall back to HELO */
177 g_clear_error (error);
178 transport->flags &= ~CAMEL_SMTP_TRANSPORT_IS_ESMTP;
180 if (!smtp_helo (transport, cancellable, error)) {
181 camel_service_disconnect_sync (
182 CAMEL_SERVICE (transport),
183 TRUE, cancellable, NULL);
189 /* clear any EHLO/HELO exception and assume that any SMTP errors encountered were non-fatal */
190 g_clear_error (error);
192 if (method != CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT)
193 goto exit; /* we're done */
195 if (!(transport->flags & CAMEL_SMTP_TRANSPORT_STARTTLS)) {
197 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
198 _("Failed to connect to SMTP server %s in secure mode: %s"),
199 host, _("STARTTLS not supported"));
201 goto exception_cleanup;
204 d(fprintf (stderr, "sending : STARTTLS\r\n"));
205 if (camel_stream_write (
206 tcp_stream, "STARTTLS\r\n", 10, cancellable, error) == -1) {
207 g_prefix_error (error, _("STARTTLS command failed: "));
208 goto exception_cleanup;
214 /* Check for "220 Ready for TLS" */
216 respbuf = camel_stream_buffer_read_line (
217 CAMEL_STREAM_BUFFER (transport->istream),
219 if (respbuf == NULL) {
220 g_prefix_error (error, _("STARTTLS command failed: "));
221 transport->connected = FALSE;
222 goto exception_cleanup;
224 if (strncmp (respbuf, "220", 3) != 0) {
226 transport, respbuf, cancellable, error);
227 g_prefix_error (error, _("STARTTLS command failed: "));
229 goto exception_cleanup;
231 } while (*(respbuf+3) == '-'); /* if we got "220-" then loop again */
233 /* Okay, now toggle SSL/TLS mode */
234 if (camel_tcp_stream_ssl_enable_ssl (
235 CAMEL_TCP_STREAM_SSL (tcp_stream), cancellable, error) == -1) {
238 _("Failed to connect to SMTP server %s in secure mode: "),
240 goto exception_cleanup;
243 /* We are supposed to re-EHLO after a successful STARTTLS to
244 * re-fetch any supported extensions. */
245 if (!smtp_helo (transport, cancellable, error)) {
246 camel_service_disconnect_sync (
247 CAMEL_SERVICE (transport),
248 TRUE, cancellable, NULL);
256 g_object_unref (transport->istream);
257 transport->istream = NULL;
259 g_object_unref (transport->ostream);
260 transport->ostream = NULL;
262 transport->connected = FALSE;
273 authtypes_free (gpointer key,
281 smtp_transport_get_name (CamelService *service,
284 CamelNetworkSettings *network_settings;
285 CamelSettings *settings;
289 settings = camel_service_ref_settings (service);
291 network_settings = CAMEL_NETWORK_SETTINGS (settings);
292 host = camel_network_settings_dup_host (network_settings);
294 g_object_unref (settings);
297 name = g_strdup_printf (
298 _("SMTP server %s"), host);
300 name = g_strdup_printf (
301 _("SMTP mail delivery via %s"), host);
309 smtp_transport_connect_sync (CamelService *service,
310 GCancellable *cancellable,
313 CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
314 CamelNetworkSettings *network_settings;
315 CamelSettings *settings;
318 gboolean auth_required;
319 gboolean success = TRUE;
321 settings = camel_service_ref_settings (service);
323 network_settings = CAMEL_NETWORK_SETTINGS (settings);
324 host = camel_network_settings_dup_host (network_settings);
325 mechanism = camel_network_settings_dup_auth_mechanism (network_settings);
327 g_object_unref (settings);
329 /* We (probably) need to check popb4smtp before we connect ... */
330 if (g_strcmp0 (mechanism, "POPB4SMTP") == 0) {
334 sasl = camel_sasl_new ("smtp", "POPB4SMTP", service);
335 chal = camel_sasl_challenge_sync (sasl, NULL, cancellable, error);
337 g_byte_array_free (chal, TRUE);
339 if (camel_sasl_get_authenticated (sasl))
340 success = connect_to_server (
341 service, cancellable, error);
345 g_object_unref (sasl);
350 success = connect_to_server (service, cancellable, error);
355 /* check to see if AUTH is required, if so...then AUTH ourselves */
357 (mechanism != NULL) &&
358 (transport->authtypes != NULL) &&
359 (g_hash_table_size (transport->authtypes) > 0) &&
360 (transport->flags & CAMEL_SMTP_TRANSPORT_IS_ESMTP);
363 CamelSession *session;
365 session = camel_service_get_session (service);
367 if (g_hash_table_lookup (transport->authtypes, mechanism)) {
368 success = camel_session_authenticate_sync (
369 session, service, mechanism,
373 error, CAMEL_SERVICE_ERROR,
374 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
375 _("SMTP server %s does not support %s "
376 "authentication"), host, mechanism);
381 camel_service_disconnect_sync (
382 service, TRUE, cancellable, NULL);
393 smtp_transport_disconnect_sync (CamelService *service,
395 GCancellable *cancellable,
398 CamelServiceClass *service_class;
399 CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
401 /*if (!service->connected)
405 if (transport->connected && clean) {
406 /* send the QUIT command to the SMTP server */
407 smtp_quit (transport, cancellable, NULL);
410 /* Chain up to parent's disconnect() method. */
411 service_class = CAMEL_SERVICE_CLASS (camel_smtp_transport_parent_class);
412 if (!service_class->disconnect_sync (service, clean, cancellable, error))
415 if (transport->authtypes) {
416 g_hash_table_foreach (transport->authtypes, authtypes_free, NULL);
417 g_hash_table_destroy (transport->authtypes);
418 transport->authtypes = NULL;
421 if (transport->istream) {
422 g_object_unref (transport->istream);
423 transport->istream = NULL;
426 if (transport->ostream) {
427 g_object_unref (transport->ostream);
428 transport->ostream = NULL;
431 g_free (transport->localaddr);
432 transport->localaddr = NULL;
434 transport->connected = FALSE;
439 static CamelAuthenticationResult
440 smtp_transport_authenticate_sync (CamelService *service,
441 const gchar *mechanism,
442 GCancellable *cancellable,
445 CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
446 CamelAuthenticationResult result;
448 gchar *cmdbuf, *respbuf = NULL, *challenge;
449 gboolean auth_challenge = FALSE;
451 if (mechanism == NULL) {
453 error, CAMEL_SERVICE_ERROR,
454 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
455 _("No SASL mechanism was specified"));
456 return CAMEL_AUTHENTICATION_ERROR;
459 sasl = camel_sasl_new ("smtp", mechanism, service);
462 error, CAMEL_SERVICE_ERROR,
463 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
464 _("No support for %s authentication"), mechanism);
465 return CAMEL_AUTHENTICATION_ERROR;
468 challenge = camel_sasl_challenge_base64_sync (
469 sasl, NULL, cancellable, error);
471 auth_challenge = TRUE;
472 cmdbuf = g_strdup_printf (
473 "AUTH %s %s\r\n", mechanism, challenge);
476 cmdbuf = g_strdup_printf (
477 "AUTH %s\r\n", mechanism);
480 d(fprintf (stderr, "sending : %s", cmdbuf));
481 if (camel_stream_write_string (
482 transport->ostream, cmdbuf,
483 cancellable, error) == -1) {
485 g_prefix_error (error, _("AUTH command failed: "));
490 respbuf = camel_stream_buffer_read_line (
491 CAMEL_STREAM_BUFFER (transport->istream),
494 while (!camel_sasl_get_authenticated (sasl)) {
496 g_prefix_error (error, _("AUTH command failed: "));
497 transport->connected = FALSE;
501 /* the server may have accepted our initial response */
502 if (strncmp (respbuf, "235", 3) == 0)
505 /* the server challenge/response should follow a 334 code */
506 if (strncmp (respbuf, "334", 3) != 0) {
508 transport, respbuf, cancellable, error);
509 g_prefix_error (error, _("AUTH command failed: "));
515 d(fprintf (stderr, "Your SMTP server's implementation of the %s SASL\n"
516 "authentication mechanism is broken. Please report this to the\n"
517 "appropriate vendor and suggest that they re-read rfc2554 again\n"
518 "for the first time (specifically Section 4).\n",
523 for (challenge = respbuf + 4; isspace (*challenge); challenge++);
525 challenge = camel_sasl_challenge_base64_sync (
526 sasl, challenge, cancellable, error);
527 if (challenge == NULL)
532 /* send our challenge */
533 cmdbuf = g_strdup_printf ("%s\r\n", challenge);
535 d(fprintf (stderr, "sending : %s", cmdbuf));
536 if (camel_stream_write_string (
537 transport->ostream, cmdbuf,
538 cancellable, error) == -1) {
544 /* get the server's response */
545 respbuf = camel_stream_buffer_read_line (
546 CAMEL_STREAM_BUFFER (transport->istream),
553 /* Work around broken SASL implementations. */
554 if (auth_challenge && strncmp (respbuf, "334", 3) == 0)
555 goto broken_smtp_server;
557 /* If our authentication data was rejected, destroy the
558 * password so that the user gets prompted to try again. */
559 if (strncmp (respbuf, "535", 3) == 0)
560 result = CAMEL_AUTHENTICATION_REJECTED;
562 result = CAMEL_AUTHENTICATION_ACCEPTED;
564 /* Catch any other errors. */
565 if (strncmp (respbuf, "235", 3) != 0) {
567 error, CAMEL_SERVICE_ERROR,
568 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
569 _("Bad authentication response from server."));
576 /* Get the server out of "waiting for continuation data" mode. */
577 d(fprintf (stderr, "sending : *\n"));
578 camel_stream_write (transport->ostream, "*\r\n", 3, cancellable, NULL);
579 respbuf = camel_stream_buffer_read_line (
580 CAMEL_STREAM_BUFFER (transport->istream), cancellable, NULL);
581 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
584 result = CAMEL_AUTHENTICATION_ERROR;
587 g_object_unref (sasl);
594 smtp_transport_query_auth_types_sync (CamelService *service,
595 GCancellable *cancellable,
598 CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
599 CamelServiceAuthType *authtype;
600 CamelProvider *provider;
601 GList *types, *t, *next;
603 if (!connect_to_server (service, cancellable, error))
606 if (!transport->authtypes) {
607 smtp_transport_disconnect_sync (
608 service, TRUE, cancellable, NULL);
612 provider = camel_service_get_provider (service);
613 types = g_list_copy (provider->authtypes);
615 for (t = types; t; t = next) {
619 if (!g_hash_table_lookup (transport->authtypes, authtype->authproto)) {
620 types = g_list_remove_link (types, t);
625 smtp_transport_disconnect_sync (service, TRUE, cancellable, NULL);
631 smtp_transport_send_to_sync (CamelTransport *transport,
632 CamelMimeMessage *message,
634 CamelAddress *recipients,
635 GCancellable *cancellable,
638 CamelSmtpTransport *smtp_transport = CAMEL_SMTP_TRANSPORT (transport);
639 CamelInternetAddress *cia;
640 gboolean has_8bit_parts;
644 if (!smtp_transport->connected) {
646 error, CAMEL_SERVICE_ERROR,
647 CAMEL_SERVICE_ERROR_NOT_CONNECTED,
648 _("Cannot send message: service not connected."));
652 if (!camel_internet_address_get (CAMEL_INTERNET_ADDRESS (from), 0, NULL, &addr)) {
654 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
655 _("Cannot send message: sender address not valid."));
659 camel_operation_push_message (cancellable, _("Sending message"));
661 /* find out if the message has 8bit mime parts */
662 has_8bit_parts = camel_mime_message_has_8bit_parts (message);
664 /* If the connection needs a ReSET, then do so */
665 if (smtp_transport->need_rset &&
666 !smtp_rset (smtp_transport, cancellable, error)) {
667 camel_operation_pop_message (cancellable);
670 smtp_transport->need_rset = FALSE;
672 /* rfc1652 (8BITMIME) requires that you notify the ESMTP daemon that
673 * you'll be sending an 8bit mime message at "MAIL FROM:" time. */
675 smtp_transport, addr, has_8bit_parts, cancellable, error)) {
676 camel_operation_pop_message (cancellable);
680 len = camel_address_length (recipients);
683 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
684 _("Cannot send message: no recipients defined."));
685 camel_operation_pop_message (cancellable);
686 smtp_transport->need_rset = TRUE;
690 cia = CAMEL_INTERNET_ADDRESS (recipients);
691 for (i = 0; i < len; i++) {
694 if (!camel_internet_address_get (cia, i, NULL, &addr)) {
696 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
697 _("Cannot send message: "
698 "one or more invalid recipients"));
699 camel_operation_pop_message (cancellable);
700 smtp_transport->need_rset = TRUE;
704 enc = camel_internet_address_encode_address (NULL, NULL, addr);
705 if (!smtp_rcpt (smtp_transport, enc, cancellable, error)) {
707 camel_operation_pop_message (cancellable);
708 smtp_transport->need_rset = TRUE;
714 if (!smtp_data (smtp_transport, message, cancellable, error)) {
715 camel_operation_pop_message (cancellable);
716 smtp_transport->need_rset = TRUE;
720 camel_operation_pop_message (cancellable);
726 smtp_transport_get_service_name (CamelNetworkService *service,
727 CamelNetworkSecurityMethod method)
729 const gchar *service_name;
732 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
733 service_name = "smtps";
737 service_name = "smtp";
745 smtp_transport_get_default_port (CamelNetworkService *service,
746 CamelNetworkSecurityMethod method)
748 guint16 default_port;
751 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
752 default_port = SMTPS_PORT;
756 default_port = SMTP_PORT;
764 camel_smtp_transport_class_init (CamelSmtpTransportClass *class)
766 CamelServiceClass *service_class;
767 CamelTransportClass *transport_class;
769 service_class = CAMEL_SERVICE_CLASS (class);
770 service_class->settings_type = CAMEL_TYPE_SMTP_SETTINGS;
771 service_class->get_name = smtp_transport_get_name;
772 service_class->connect_sync = smtp_transport_connect_sync;
773 service_class->disconnect_sync = smtp_transport_disconnect_sync;
774 service_class->authenticate_sync = smtp_transport_authenticate_sync;
775 service_class->query_auth_types_sync = smtp_transport_query_auth_types_sync;
777 transport_class = CAMEL_TRANSPORT_CLASS (class);
778 transport_class->send_to_sync = smtp_transport_send_to_sync;
782 camel_network_service_init (CamelNetworkServiceInterface *interface)
784 interface->get_service_name = smtp_transport_get_service_name;
785 interface->get_default_port = smtp_transport_get_default_port;
789 camel_smtp_transport_init (CamelSmtpTransport *smtp)
792 smtp->connected = FALSE;
796 smtp_error_string (gint error)
798 /* SMTP error codes grabbed from rfc821 */
801 return _("Syntax error, command unrecognized");
803 return _("Syntax error in parameters or arguments");
805 return _("Command not implemented");
807 return _("Command parameter not implemented");
809 return _("System status, or system help reply");
811 return _("Help message");
813 return _("Service ready");
815 return _("Service closing transmission channel");
817 return _("Service not available, closing transmission channel");
819 return _("Requested mail action okay, completed");
821 return _("User not local; will forward to <forward-path>");
823 return _("Requested mail action not taken: mailbox unavailable");
825 return _("Requested action not taken: mailbox unavailable");
827 return _("Requested action aborted: error in processing");
829 return _("User not local; please try <forward-path>");
831 return _("Requested action not taken: insufficient system storage");
833 return _("Requested mail action aborted: exceeded storage allocation");
835 return _("Requested action not taken: mailbox name not allowed");
837 return _("Start mail input; end with <CRLF>.<CRLF>");
839 return _("Transaction failed");
841 /* AUTH error codes: */
843 return _("A password transition is needed");
845 return _("Authentication mechanism is too weak");
847 return _("Encryption required for requested authentication mechanism");
849 return _("Temporary authentication failure");
851 return _("Authentication required");
859 esmtp_get_authtypes (const guchar *buffer)
861 const guchar *start, *end;
862 GHashTable *table = NULL;
866 /* make sure there is at least one delimiter
867 character in the AUTH response */
868 if (!isspace ((gint) *start) && *start != '=')
871 /* advance to the first token */
872 while (isspace ((gint) *start) || *start == '=')
878 table = g_hash_table_new (g_str_hash, g_str_equal);
883 /* advance to the end of the token */
885 while (*end && !isspace ((gint) *end))
888 type = g_strndup ((gchar *) start, end - start);
889 g_hash_table_insert (table, type, type);
891 /* advance to the next token */
893 while (isspace ((gint) *start))
901 smtp_next_token (const gchar *buf)
905 token = (const guchar *) buf;
906 while (*token && !isspace ((gint) *token))
909 while (*token && isspace ((gint) *token))
912 return (const gchar *) token;
915 #define HEXVAL(c) (isdigit (c) ? (c) - '0' : (c) - 'A' + 10)
919 * 5.1.1 Mailbox "nosuchuser" does not exist
921 * The human-readable status code is what we want. Since this text
922 * could possibly be encoded, we must decode it.
924 * "xtext" is formally defined as follows:
926 * xtext = *( xchar / hexchar / linear-white-space / comment )
928 * xchar = any ASCII CHAR between "!" (33) and "~" (126) inclusive,
929 * except for "+", "\" and "(".
931 * "hexchar"s are intended to encode octets that cannot be represented
932 * as plain text, either because they are reserved, or because they are
933 * non-printable. However, any octet value may be represented by a
936 * hexchar = ASCII "+" immediately followed by two upper case
940 smtp_decode_status_code (const gchar *in,
943 guchar *inptr, *outptr;
947 outbuf = (gchar *) g_malloc (len + 1);
948 outptr = (guchar *) outbuf;
950 inptr = (guchar *) in;
952 while (inptr < inend) {
954 if (isxdigit (inptr[1]) && isxdigit (inptr[2])) {
955 *outptr++ = HEXVAL (inptr[1]) * 16 + HEXVAL (inptr[2]);
958 *outptr++ = *inptr++;
960 *outptr++ = *inptr++;
968 /* converts string str to local encoding, thinking it's in utf8.
969 * If fails, then converts all character greater than 127 to hex values.
970 * Also those under 32, other than \n, \r, \t.
971 * Note that the c is signed character, so all characters above 127 have
975 convert_to_local (GString *str)
979 buf = g_locale_from_utf8 (str->str, str->len, NULL, NULL, NULL);
984 GString *s = g_string_new_len (str->str, str->len);
986 g_string_truncate (str, 0);
988 for (i = 0; i < s->len; i++) {
991 if (c < 32 && c != '\n' && c != '\r' && c != '\t')
992 g_string_append_printf (str, "<%X%X>", (c >> 4) & 0xF, c & 0xF);
994 g_string_append_c (str, c);
997 g_string_free (s, TRUE);
999 g_string_truncate (str, 0);
1000 g_string_append (str, buf);
1007 smtp_set_error (CamelSmtpTransport *transport,
1008 const gchar *respbuf,
1009 GCancellable *cancellable,
1012 const gchar *token, *rbuf = respbuf;
1013 gchar *buffer = NULL;
1016 g_return_if_fail (respbuf != NULL);
1018 string = g_string_new ("");
1020 if (transport->flags & CAMEL_SMTP_TRANSPORT_ENHANCEDSTATUSCODES)
1021 token = smtp_next_token (rbuf + 4);
1025 if (*token == '\0') {
1027 g_string_free (string, TRUE);
1028 goto fake_status_code;
1031 g_string_append (string, token);
1032 if (*(rbuf + 3) == '-') {
1034 buffer = camel_stream_buffer_read_line (
1035 CAMEL_STREAM_BUFFER (transport->istream),
1037 g_string_append_c (string, '\n');
1046 convert_to_local (string);
1047 if (!(transport->flags & CAMEL_SMTP_TRANSPORT_ENHANCEDSTATUSCODES) && string->len) {
1048 string->str = g_strstrip (string->str);
1049 string->len = strlen (string->str);
1052 g_string_free (string, TRUE);
1053 goto fake_status_code;
1058 CAMEL_ERROR_GENERIC,
1061 g_string_free (string, TRUE);
1063 buffer = smtp_decode_status_code (string->str, string->len);
1064 g_string_free (string, TRUE);
1066 goto fake_status_code;
1070 CAMEL_ERROR_GENERIC,
1080 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1081 "%s", smtp_error_string (atoi (respbuf)));
1085 smtp_helo (CamelSmtpTransport *transport,
1086 GCancellable *cancellable,
1089 gchar *name = NULL, *cmdbuf = NULL, *respbuf = NULL;
1090 const gchar *token, *numeric = NULL;
1091 struct sockaddr *addr;
1094 /* these are flags that we set, so unset them in case we
1095 * are being called a second time (ie, after a STARTTLS) */
1096 transport->flags &= ~(CAMEL_SMTP_TRANSPORT_8BITMIME |
1097 CAMEL_SMTP_TRANSPORT_ENHANCEDSTATUSCODES |
1098 CAMEL_SMTP_TRANSPORT_STARTTLS);
1100 if (transport->authtypes) {
1101 g_hash_table_foreach (transport->authtypes, authtypes_free, NULL);
1102 g_hash_table_destroy (transport->authtypes);
1103 transport->authtypes = NULL;
1106 camel_operation_push_message (cancellable, _("SMTP Greeting"));
1108 addr = transport->localaddr;
1109 addrlen = transport->localaddrlen;
1111 if (camel_getnameinfo (
1112 addr, addrlen, &name, NULL,
1113 NI_NUMERICHOST, cancellable, NULL) != 0) {
1114 name = g_strdup ("localhost.localdomain");
1116 if (addr->sa_family == AF_INET6)
1122 token = (transport->flags & CAMEL_SMTP_TRANSPORT_IS_ESMTP) ? "EHLO" : "HELO";
1124 cmdbuf = g_strdup_printf("%s [%s%s]\r\n", token, numeric, name);
1126 cmdbuf = g_strdup_printf("%s %s\r\n", token, name);
1129 d(fprintf (stderr, "sending : %s", cmdbuf));
1130 if (camel_stream_write_string (
1131 transport->ostream, cmdbuf, cancellable, error) == -1) {
1133 g_prefix_error (error, _("HELO command failed: "));
1134 camel_operation_pop_message (cancellable);
1136 camel_service_disconnect_sync (
1137 CAMEL_SERVICE (transport),
1138 FALSE, cancellable, NULL);
1145 /* Check for "250" */
1147 respbuf = camel_stream_buffer_read_line (
1148 CAMEL_STREAM_BUFFER (transport->istream),
1149 cancellable, error);
1150 if (respbuf == NULL) {
1151 g_prefix_error (error, _("HELO command failed: "));
1152 transport->connected = FALSE;
1153 camel_operation_pop_message (cancellable);
1156 if (strncmp (respbuf, "250", 3)) {
1158 transport, respbuf, cancellable, error);
1159 g_prefix_error (error, _("HELO command failed: "));
1160 camel_operation_pop_message (cancellable);
1165 token = respbuf + 4;
1167 if (transport->flags & CAMEL_SMTP_TRANSPORT_IS_ESMTP) {
1168 if (!g_ascii_strncasecmp (token, "8BITMIME", 8)) {
1169 transport->flags |= CAMEL_SMTP_TRANSPORT_8BITMIME;
1170 } else if (!g_ascii_strncasecmp (token, "ENHANCEDSTATUSCODES", 19)) {
1171 transport->flags |= CAMEL_SMTP_TRANSPORT_ENHANCEDSTATUSCODES;
1172 } else if (!g_ascii_strncasecmp (token, "STARTTLS", 8)) {
1173 transport->flags |= CAMEL_SMTP_TRANSPORT_STARTTLS;
1174 } else if (!g_ascii_strncasecmp (token, "AUTH", 4)) {
1175 if (!transport->authtypes || transport->flags & CAMEL_SMTP_TRANSPORT_AUTH_EQUAL) {
1176 /* Don't bother parsing any authtypes if we already have a list.
1177 * Some servers will list AUTH twice, once the standard way and
1178 * once the way Microsoft Outlook requires them to be:
1180 * 250-AUTH LOGIN PLAIN DIGEST-MD5 CRAM-MD5
1181 * 250-AUTH=LOGIN PLAIN DIGEST-MD5 CRAM-MD5
1183 * Since they can come in any order, parse each list that we get
1184 * until we parse an authtype list that does not use the AUTH=
1185 * format. We want to let the standard way have priority over the
1189 if (token[4] == '=')
1190 transport->flags |= CAMEL_SMTP_TRANSPORT_AUTH_EQUAL;
1192 transport->flags &= ~CAMEL_SMTP_TRANSPORT_AUTH_EQUAL;
1194 /* parse for supported AUTH types */
1197 if (transport->authtypes) {
1198 g_hash_table_foreach (transport->authtypes, authtypes_free, NULL);
1199 g_hash_table_destroy (transport->authtypes);
1202 transport->authtypes = esmtp_get_authtypes ((const guchar *) token);
1206 } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
1209 camel_operation_pop_message (cancellable);
1215 smtp_mail (CamelSmtpTransport *transport,
1216 const gchar *sender,
1217 gboolean has_8bit_parts,
1218 GCancellable *cancellable,
1221 /* we gotta tell the smtp server who we are. (our email addy) */
1222 gchar *cmdbuf, *respbuf = NULL;
1224 if (transport->flags & CAMEL_SMTP_TRANSPORT_8BITMIME && has_8bit_parts)
1225 cmdbuf = g_strdup_printf ("MAIL FROM:<%s> BODY=8BITMIME\r\n", sender);
1227 cmdbuf = g_strdup_printf ("MAIL FROM:<%s>\r\n", sender);
1229 d(fprintf (stderr, "sending : %s", cmdbuf));
1231 if (camel_stream_write_string (
1232 transport->ostream, cmdbuf, cancellable, error) == -1) {
1234 g_prefix_error (error, _("MAIL FROM command failed: "));
1235 camel_service_disconnect_sync (
1236 CAMEL_SERVICE (transport),
1237 FALSE, cancellable, NULL);
1243 /* Check for "250 Sender OK..." */
1245 respbuf = camel_stream_buffer_read_line (
1246 CAMEL_STREAM_BUFFER (transport->istream),
1247 cancellable, error);
1248 if (respbuf == NULL) {
1249 g_prefix_error (error, _("MAIL FROM command failed: "));
1250 camel_service_disconnect_sync (
1251 CAMEL_SERVICE (transport),
1252 FALSE, cancellable, NULL);
1255 if (strncmp (respbuf, "250", 3)) {
1257 transport, respbuf, cancellable, error);
1259 error, _("MAIL FROM command failed: "));
1263 } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
1270 smtp_rcpt (CamelSmtpTransport *transport,
1271 const gchar *recipient,
1272 GCancellable *cancellable,
1275 /* we gotta tell the smtp server who we are going to be sending
1277 gchar *cmdbuf, *respbuf = NULL;
1279 cmdbuf = g_strdup_printf ("RCPT TO:<%s>\r\n", recipient);
1281 d(fprintf (stderr, "sending : %s", cmdbuf));
1283 if (camel_stream_write_string (
1284 transport->ostream, cmdbuf, cancellable, error) == -1) {
1286 g_prefix_error (error, _("RCPT TO command failed: "));
1287 camel_service_disconnect_sync (
1288 CAMEL_SERVICE (transport),
1289 FALSE, cancellable, NULL);
1296 /* Check for "250 Recipient OK..." */
1298 respbuf = camel_stream_buffer_read_line (
1299 CAMEL_STREAM_BUFFER (transport->istream),
1300 cancellable, error);
1301 if (respbuf == NULL) {
1303 error, _("RCPT TO <%s> failed: "), recipient);
1304 camel_service_disconnect_sync (
1305 CAMEL_SERVICE (transport),
1306 FALSE, cancellable, NULL);
1309 if (strncmp (respbuf, "250", 3)) {
1311 transport, respbuf, cancellable, error);
1313 error, _("RCPT TO <%s> failed: "), recipient);
1318 } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
1325 smtp_data (CamelSmtpTransport *transport,
1326 CamelMimeMessage *message,
1327 GCancellable *cancellable,
1330 struct _camel_header_raw *header, *savedbcc, *n, *tail;
1331 CamelBestencEncoding enctype = CAMEL_BESTENC_8BIT;
1332 CamelStream *filtered_stream;
1333 gchar *cmdbuf, *respbuf = NULL;
1334 CamelMimeFilter *filter;
1335 CamelStreamNull *null;
1338 /* If the server doesn't support 8BITMIME, set our required encoding to be 7bit */
1339 if (!(transport->flags & CAMEL_SMTP_TRANSPORT_8BITMIME))
1340 enctype = CAMEL_BESTENC_7BIT;
1342 /* FIXME: should we get the best charset too?? */
1343 /* Changes the encoding of all mime parts to fit within our required
1344 * encoding type and also force any text parts with long lines (longer
1345 * than 998 octets) to wrap by QP or base64 encoding them. */
1346 camel_mime_message_set_best_encoding (message, CAMEL_BESTENC_GET_ENCODING, enctype);
1348 cmdbuf = g_strdup ("DATA\r\n");
1350 d(fprintf (stderr, "sending : %s", cmdbuf));
1352 if (camel_stream_write_string (
1353 transport->ostream, cmdbuf, cancellable, error) == -1) {
1355 g_prefix_error (error, _("DATA command failed: "));
1356 camel_service_disconnect_sync (
1357 CAMEL_SERVICE (transport),
1358 FALSE, cancellable, NULL);
1363 respbuf = camel_stream_buffer_read_line (
1364 CAMEL_STREAM_BUFFER (transport->istream), cancellable, error);
1365 if (respbuf == NULL) {
1366 g_prefix_error (error, _("DATA command failed: "));
1367 camel_service_disconnect_sync (
1368 CAMEL_SERVICE (transport),
1369 FALSE, cancellable, NULL);
1372 if (strncmp (respbuf, "354", 3) != 0) {
1373 /* We should have gotten instructions on how to use the DATA
1374 * command: 354 Enter mail, end with "." on a line by itself
1376 smtp_set_error (transport, respbuf, cancellable, error);
1377 g_prefix_error (error, _("DATA command failed: "));
1385 /* unlink the bcc headers */
1387 tail = (struct _camel_header_raw *) &savedbcc;
1389 header = (struct _camel_header_raw *) &CAMEL_MIME_PART (message)->headers;
1392 if (!g_ascii_strcasecmp (n->name, "Bcc")) {
1393 header->next = n->next;
1404 /* find out how large the message is... */
1405 null = CAMEL_STREAM_NULL (camel_stream_null_new ());
1406 camel_data_wrapper_write_to_stream_sync (
1407 CAMEL_DATA_WRAPPER (message),
1408 CAMEL_STREAM (null), NULL, NULL);
1410 filtered_stream = camel_stream_filter_new (transport->ostream);
1412 /* setup progress reporting for message sending... */
1413 filter = camel_mime_filter_progress_new (cancellable, null->written);
1414 camel_stream_filter_add (
1415 CAMEL_STREAM_FILTER (filtered_stream), filter);
1416 g_object_unref (filter);
1417 g_object_unref (null);
1419 /* setup LF->CRLF conversion */
1420 filter = camel_mime_filter_crlf_new (
1421 CAMEL_MIME_FILTER_CRLF_ENCODE,
1422 CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
1423 camel_stream_filter_add (
1424 CAMEL_STREAM_FILTER (filtered_stream), filter);
1425 g_object_unref (filter);
1427 /* write the message */
1428 ret = camel_data_wrapper_write_to_stream_sync (
1429 CAMEL_DATA_WRAPPER (message),
1430 filtered_stream, cancellable, error);
1432 /* restore the bcc headers */
1433 header->next = savedbcc;
1436 g_prefix_error (error, _("DATA command failed: "));
1438 g_object_unref (filtered_stream);
1440 camel_service_disconnect_sync (
1441 CAMEL_SERVICE (transport),
1442 FALSE, cancellable, NULL);
1446 camel_stream_flush (filtered_stream, cancellable, NULL);
1447 g_object_unref (filtered_stream);
1449 /* terminate the message body */
1451 d(fprintf (stderr, "sending : \\r\\n.\\r\\n\n"));
1453 if (camel_stream_write (
1454 transport->ostream, "\r\n.\r\n", 5,
1455 cancellable, error) == -1) {
1456 g_prefix_error (error, _("DATA command failed: "));
1457 camel_service_disconnect_sync (
1458 CAMEL_SERVICE (transport),
1459 FALSE, cancellable, NULL);
1464 /* Check for "250 Sender OK..." */
1466 respbuf = camel_stream_buffer_read_line (
1467 CAMEL_STREAM_BUFFER (transport->istream),
1468 cancellable, error);
1469 if (respbuf == NULL) {
1470 g_prefix_error (error, _("DATA command failed: "));
1471 camel_service_disconnect_sync (
1472 CAMEL_SERVICE (transport),
1473 FALSE, cancellable, NULL);
1476 if (strncmp (respbuf, "250", 3) != 0) {
1478 transport, respbuf, cancellable, error);
1479 g_prefix_error (error, _("DATA command failed: "));
1483 } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
1490 smtp_rset (CamelSmtpTransport *transport,
1491 GCancellable *cancellable,
1494 /* we are going to reset the smtp server (just to be nice) */
1495 gchar *cmdbuf, *respbuf = NULL;
1497 cmdbuf = g_strdup ("RSET\r\n");
1499 d(fprintf (stderr, "sending : %s", cmdbuf));
1501 if (camel_stream_write_string (
1502 transport->ostream, cmdbuf, cancellable, error) == -1) {
1504 g_prefix_error (error, _("RSET command failed: "));
1505 camel_service_disconnect_sync (
1506 CAMEL_SERVICE (transport),
1507 FALSE, cancellable, NULL);
1513 /* Check for "250" */
1515 respbuf = camel_stream_buffer_read_line (
1516 CAMEL_STREAM_BUFFER (transport->istream),
1517 cancellable, error);
1518 if (respbuf == NULL) {
1519 g_prefix_error (error, _("RSET command failed: "));
1520 camel_service_disconnect_sync (
1521 CAMEL_SERVICE (transport),
1522 FALSE, cancellable, NULL);
1525 if (strncmp (respbuf, "250", 3) != 0) {
1527 transport, respbuf, cancellable, error);
1528 g_prefix_error (error, _("RSET command failed: "));
1532 } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
1539 smtp_quit (CamelSmtpTransport *transport,
1540 GCancellable *cancellable,
1543 /* we are going to reset the smtp server (just to be nice) */
1544 gchar *cmdbuf, *respbuf = NULL;
1546 cmdbuf = g_strdup ("QUIT\r\n");
1548 d(fprintf (stderr, "sending : %s", cmdbuf));
1550 if (camel_stream_write_string (
1551 transport->ostream, cmdbuf, cancellable, error) == -1) {
1553 g_prefix_error (error, _("QUIT command failed: "));
1559 /* Check for "221" */
1561 respbuf = camel_stream_buffer_read_line (
1562 CAMEL_STREAM_BUFFER (transport->istream),
1563 cancellable, error);
1565 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
1566 if (respbuf == NULL) {
1567 g_prefix_error (error, _("QUIT command failed: "));
1568 transport->connected = FALSE;
1571 if (strncmp (respbuf, "221", 3) != 0) {
1573 transport, respbuf, cancellable, error);
1574 g_prefix_error (error, _("QUIT command failed: "));
1578 } while (*(respbuf+3) == '-'); /* if we got "221-" then loop again */