Updated with the extended AUTH return codes.
[platform/upstream/evolution-data-server.git] / camel / providers / smtp / camel-smtp-transport.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-smtp-transport.c : class for a smtp transport */
3
4 /* 
5  * Authors: Jeffrey Stedfast <fejj@helixcode.com>
6  *
7  * Copyright (C) 2000 Helix Code, Inc. (www.helixcode.com)
8  *
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.
13  *
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.
18  *
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
22  * USA
23  */
24
25 #include "config.h"
26
27 #include <sys/param.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <unistd.h>
38 #undef MIN
39 #undef MAX
40 #include "camel-mime-filter-crlf.h"
41 #include "camel-mime-filter-linewrap.h"
42 #include "camel-stream-filter.h"
43 #include "camel-smtp-transport.h"
44 #include "camel-mime-message.h"
45 #include "camel-multipart.h"
46 #include "camel-mime-part.h"
47 #include "camel-stream-buffer.h"
48 #include "camel-stream-fs.h"
49 #include "camel-session.h"
50 #include "camel-exception.h"
51 #include "camel-sasl.h"
52 #include <gal/util/e-util.h>
53
54 #define d(x) x
55
56 /* Specified in RFC 821 */
57 #define SMTP_PORT 25
58
59 /* camel smtp transport class prototypes */
60 static gboolean smtp_can_send (CamelTransport *transport, CamelMedium *message);
61 static gboolean smtp_send (CamelTransport *transport, CamelMedium *message, CamelException *ex);
62 static gboolean smtp_send_to (CamelTransport *transport, CamelMedium *message, GList *recipients, CamelException *ex);
63
64 /* support prototypes */
65 static gboolean smtp_connect (CamelService *service, CamelException *ex);
66 static gboolean smtp_disconnect (CamelService *service, gboolean clean, CamelException *ex);
67 static GHashTable *esmtp_get_authtypes (gchar *buffer);
68 static GList *query_auth_types (CamelService *service, gboolean connect, CamelException *ex);
69 static void free_auth_types (CamelService *service, GList *authtypes);
70 static char *get_name (CamelService *service, gboolean brief);
71
72 static gboolean smtp_helo (CamelSmtpTransport *transport, CamelException *ex);
73 static gboolean smtp_auth (CamelSmtpTransport *transport, const char *mech, CamelException *ex);
74 static gboolean smtp_mail (CamelSmtpTransport *transport, const char *sender,
75                            gboolean has_8bit_parts, CamelException *ex);
76 static gboolean smtp_rcpt (CamelSmtpTransport *transport, const char *recipient, CamelException *ex);
77 static gboolean smtp_data (CamelSmtpTransport *transport, CamelMedium *message,
78                            gboolean has_8bit_parts, CamelException *ex);
79 static gboolean smtp_rset (CamelSmtpTransport *transport, CamelException *ex);
80 static gboolean smtp_quit (CamelSmtpTransport *transport, CamelException *ex);
81
82 /* private data members */
83 static CamelServiceClass *service_class = NULL;
84
85 static void
86 camel_smtp_transport_class_init (CamelSmtpTransportClass *camel_smtp_transport_class)
87 {
88         CamelTransportClass *camel_transport_class =
89                 CAMEL_TRANSPORT_CLASS (camel_smtp_transport_class);
90         CamelServiceClass *camel_service_class =
91                 CAMEL_SERVICE_CLASS (camel_smtp_transport_class);
92         
93         service_class = CAMEL_SERVICE_CLASS (camel_type_get_global_classfuncs (camel_service_get_type ()));
94         
95         /* virtual method overload */
96         camel_service_class->connect = smtp_connect;
97         camel_service_class->disconnect = smtp_disconnect;
98         camel_service_class->query_auth_types = query_auth_types;
99         camel_service_class->free_auth_types = free_auth_types;
100         camel_service_class->get_name = get_name;
101
102         camel_transport_class->can_send = smtp_can_send;
103         camel_transport_class->send = smtp_send;
104         camel_transport_class->send_to = smtp_send_to;
105 }
106
107 static void
108 camel_smtp_transport_init (gpointer object)
109 {
110         CamelTransport *transport = CAMEL_TRANSPORT (object);
111         
112         transport->supports_8bit = FALSE;
113 }
114
115 CamelType
116 camel_smtp_transport_get_type (void)
117 {
118         static CamelType camel_smtp_transport_type = CAMEL_INVALID_TYPE;
119         
120         if (camel_smtp_transport_type == CAMEL_INVALID_TYPE) {
121                 camel_smtp_transport_type =
122                         camel_type_register (CAMEL_TRANSPORT_TYPE, "CamelSmtpTransport",
123                                              sizeof (CamelSmtpTransport),
124                                              sizeof (CamelSmtpTransportClass),
125                                              (CamelObjectClassInitFunc) camel_smtp_transport_class_init,
126                                              NULL,
127                                              (CamelObjectInitFunc) camel_smtp_transport_init,
128                                              NULL);
129         }
130         
131         return camel_smtp_transport_type;
132 }
133
134 static const char *
135 get_smtp_error_string (int error)
136 {
137         /* SMTP error codes grabbed from rfc821 */
138         switch (error) {
139         case 0:
140                 /* looks like a read problem, check errno */
141                 return g_strerror (errno);
142         case 500:
143                 return _("Syntax error, command unrecognized");
144         case 501:
145                 return _("Syntax error in parameters or arguments");
146         case 502:
147                 return _("Command not implemented");
148         case 504:
149                 return _("Command parameter not implemented");
150         case 211:
151                 return _("System status, or system help reply");
152         case 214:
153                 return _("Help message");
154         case 220:
155                 return _("Service ready");
156         case 221:
157                 return _("Service closing transmission channel");
158         case 421:
159                 return _("Service not available, closing transmission channel");
160         case 250:
161                 return _("Requested mail action okay, completed");
162         case 251:
163                 return _("User not local; will forward to <forward-path>");
164         case 450:
165                 return _("Requested mail action not taken: mailbox unavailable");
166         case 550:
167                 return _("Requested action not taken: mailbox unavailable");
168         case 451:
169                 return _("Requested action aborted: error in processing");
170         case 551:
171                 return _("User not local; please try <forward-path>");
172         case 452:
173                 return _("Requested action not taken: insufficient system storage");
174         case 552:
175                 return _("Requested mail action aborted: exceeded storage allocation");
176         case 553:
177                 return _("Requested action not taken: mailbox name not allowed");
178         case 354:
179                 return _("Start mail input; end with <CRLF>.<CRLF>");
180         case 554:
181                 return _("Transaction failed");
182                 
183         /* AUTH error codes: */
184         case 432:
185                 return _("A password transition is needed");
186         case 534:
187                 return _("Authentication mechanism is too weak");
188         case 538:
189                 return _("Encryption required for requested authentication mechanism");
190         case 454:
191                 return _("Temporary authentication failure");
192         case 530:
193                 return _("Authentication required");
194                 
195         default:
196                 return _("Unknown");
197         }
198 }
199
200 static gboolean
201 smtp_connect (CamelService *service, CamelException *ex)
202 {
203         CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
204         gchar *pass = NULL, *respbuf = NULL;
205         struct hostent *h;
206         struct sockaddr_in sin;
207         guint32 addrlen;
208         gint fd;
209         
210         if (!service_class->connect (service, ex))
211                 return FALSE;
212         
213         h = camel_service_gethost (service, ex);
214         if (!h)
215                 return FALSE;
216         
217         /* set some smtp transport defaults */
218         transport->is_esmtp = FALSE;
219         transport->authtypes = NULL;
220         CAMEL_TRANSPORT (transport)->supports_8bit = FALSE;
221         
222         sin.sin_family = h->h_addrtype;
223         sin.sin_port = htons (service->url->port ? service->url->port : SMTP_PORT);
224         memcpy (&sin.sin_addr, h->h_addr, sizeof (sin.sin_addr));
225         
226         fd = socket (h->h_addrtype, SOCK_STREAM, 0);
227         if (fd == -1 || connect (fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) {
228                 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
229                                       _("Could not connect to %s (port %d): %s"),
230                                       service->url->host,
231                                       service->url->port ? service->url->port : SMTP_PORT,
232                                       strerror (errno));
233                 if (fd > -1)
234                         close (fd);
235                 g_free (pass);
236                 return FALSE;
237         }
238         
239         /* get the localaddr - needed later by smtp_helo */
240         addrlen = sizeof (transport->localaddr);
241         getsockname (fd, (struct sockaddr*)&transport->localaddr, &addrlen);
242         
243         transport->ostream = camel_stream_fs_new_with_fd (fd);
244         transport->istream = camel_stream_buffer_new (transport->ostream, 
245                                                       CAMEL_STREAM_BUFFER_READ);
246         
247         /* Read the greeting, note whether the server is ESMTP or not. */
248         do {
249                 /* Check for "220" */
250                 g_free (respbuf);
251                 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
252                 if (!respbuf || strncmp (respbuf, "220", 3)) {
253                         int error;
254                         
255                         error = respbuf ? atoi (respbuf) : 0;
256                         g_free (respbuf);
257                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
258                                               _("Welcome response error: %s: possibly non-fatal"),
259                                               get_smtp_error_string (error));
260                         return FALSE;
261                 }
262                 if (strstr (respbuf, "ESMTP"))
263                         transport->is_esmtp = TRUE;
264         } while (*(respbuf+3) == '-'); /* if we got "220-" then loop again */
265         g_free (respbuf);
266         
267         /* send HELO (or EHLO, depending on the service type) */
268         if (!transport->is_esmtp) {
269                 /* If we did not auto-detect ESMTP, we should still send EHLO */
270                 transport->is_esmtp = TRUE;
271                 if (!smtp_helo (transport, NULL)) {
272                         /* Okay, apprently this server doesn't support ESMTP */
273                         transport->is_esmtp = FALSE;
274                         smtp_helo (transport, ex);
275                 }
276         } else {
277                 /* send EHLO */
278                 smtp_helo (transport, ex);
279         }
280         
281         /* check to see if AUTH is required, if so...then AUTH ourselves */
282         if (service->url->authmech) {
283                 CamelServiceAuthType *authtype;
284                 
285                 if (!transport->is_esmtp || !g_hash_table_lookup (transport->authtypes, service->url->authmech)) {
286                         camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
287                                               "SMTP server %s does not support requested "
288                                               "authentication type %s", service->url->host,
289                                               service->url->authmech);
290                         camel_service_disconnect (service, TRUE, NULL);
291                         return FALSE;
292                 }
293                 
294                 authtype = camel_sasl_authtype (service->url->authmech);
295                 if (!authtype) {
296                         camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
297                                               "No support for authentication type %s",
298                                               service->url->authmech);
299                         camel_service_disconnect (service, TRUE, NULL);
300                         return FALSE;
301                 }
302                 
303                 if (!smtp_auth (transport, authtype->authproto, ex)) {
304                         camel_service_disconnect (service, TRUE, NULL);
305                         return FALSE;
306                 }
307                 
308                 /* we have to re-EHLO */
309                 smtp_helo (transport, ex);
310         }
311         
312         return TRUE;
313 }
314
315 static gboolean
316 authtypes_free (gpointer key, gpointer value, gpointer data)
317 {
318         g_free (key);
319         g_free (value);
320         
321         return TRUE;
322 }
323
324 static gboolean
325 smtp_disconnect (CamelService *service, gboolean clean, CamelException *ex)
326 {
327         CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
328         
329         /*if (!service->connected)
330          *      return TRUE;
331          */
332         
333         if (clean) {
334                 /* send the QUIT command to the SMTP server */
335                 smtp_quit (transport, ex);
336         }
337         
338         if (!service_class->disconnect (service, clean, ex))
339                 return FALSE;
340         
341         if (transport->authtypes) {
342                 g_hash_table_foreach_remove (transport->authtypes, authtypes_free, NULL);
343                 g_hash_table_destroy (transport->authtypes);
344                 transport->authtypes = NULL;
345         }
346         
347         camel_object_unref (CAMEL_OBJECT (transport->ostream));
348         camel_object_unref (CAMEL_OBJECT (transport->istream));
349         
350         transport->ostream = NULL;
351         transport->istream = NULL;
352         
353         return TRUE;
354 }
355
356 static GHashTable *
357 esmtp_get_authtypes (char *buffer)
358 {
359         GHashTable *table = NULL;
360         gchar *start, *end;
361         
362         /* advance to the first token */
363         for (start = buffer; isspace (*start) || *start == '='; start++);
364         
365         if (!*start) return NULL;
366         
367         table = g_hash_table_new (g_str_hash, g_str_equal);
368         
369         for ( ; *start; ) {
370                 char *type;
371                 
372                 /* advance to the end of the token */
373                 for (end = start; *end && !isspace (*end); end++);
374                 
375                 type = g_strndup (start, end - start);
376                 g_hash_table_insert (table, g_strdup (type), type);
377                 
378                 /* advance to the next token */
379                 for (start = end; isspace (*start); start++);
380         }
381         
382         return table;
383 }
384
385 static CamelServiceAuthType no_authtype = {
386         N_("No authentication required"),
387         
388         N_("This option will connect to the SMTP server without using any "
389            "kind of authentication. This should be fine for connecting to "
390            "most SMTP servers."),
391         
392         "",
393         FALSE
394 };
395
396 static GList *
397 query_auth_types (CamelService *service, gboolean connect, CamelException *ex)
398 {
399         CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
400         CamelServiceAuthType *authtype;
401         GList *types, *t;
402         
403         if (connect && !smtp_connect (service, ex))
404                 return NULL;
405         
406         types = camel_sasl_authtype_list ();
407         if (connect) {
408                 for (t = types; t; t = t->next) {
409                         authtype = t->data;
410                         
411                         if (!g_hash_table_lookup (transport->authtypes, authtype->authproto)) {
412                                 g_list_remove_link (types, t);
413                                 g_list_free_1 (t);
414                         }
415                 }
416         }
417         
418         return g_list_prepend (types, &no_authtype);
419 }
420
421 static void
422 free_auth_types (CamelService *service, GList *authtypes)
423 {
424         g_list_free (authtypes);
425 }
426
427 static char *
428 get_name (CamelService *service, gboolean brief)
429 {
430         if (brief)
431                 return g_strdup_printf (_("SMTP server %s"), service->url->host);
432         else {
433                 return g_strdup_printf (_("SMTP mail delivery via %s"),
434                                         service->url->host);
435         }
436 }
437
438 static gboolean
439 smtp_can_send (CamelTransport *transport, CamelMedium *message)
440 {
441         return CAMEL_IS_MIME_MESSAGE (message);
442 }
443
444 static gboolean
445 smtp_send_to (CamelTransport *transport, CamelMedium *message,
446               GList *recipients, CamelException *ex)
447 {
448         CamelSmtpTransport *smtp_transport = CAMEL_SMTP_TRANSPORT (transport);
449         const CamelInternetAddress *cia;
450         char *recipient;
451         const char *addr;
452         gboolean has_8bit_parts;
453         GList *r;
454         
455         cia = camel_mime_message_get_from(CAMEL_MIME_MESSAGE (message));
456         if (!cia) {
457                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
458                                       _("Cannot send message: "
459                                         "sender address not defined."));
460                 return FALSE;
461         }
462         
463         if (!camel_internet_address_get (cia, 0, NULL, &addr)) {
464                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
465                                       _("Cannot send message: "
466                                         "sender address not valid."));
467                 return FALSE;
468         }
469         
470         /* find out if the message has 8bit mime parts */
471         has_8bit_parts = camel_mime_message_has_8bit_parts (CAMEL_MIME_MESSAGE (message));
472         
473         /* rfc1652 (8BITMIME) requires that you notify the ESMTP daemon that
474            you'll be sending an 8bit mime message at "MAIL FROM:" time. */
475         smtp_mail (smtp_transport, addr, has_8bit_parts, ex);
476         
477         if (!recipients) {
478                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
479                                       _("Cannot send message: "
480                                         "no recipients defined."));
481                 return FALSE;
482         }
483         
484         for (r = recipients; r; r = r->next) {
485                 recipient = (char *) r->data;
486                 if (!smtp_rcpt (smtp_transport, recipient, ex)) {
487                         g_free (recipient);
488                         return FALSE;
489                 }
490                 g_free (recipient);
491         }
492         
493         /* passing in has_8bit_parts saves time as we don't have to
494            recurse through the message all over again if the user is
495            not sending 8bit mime parts */
496         if (!smtp_data (smtp_transport, message, has_8bit_parts, ex))
497                 return FALSE;
498         
499         /* reset the service for our next transfer session */
500         smtp_rset (smtp_transport, ex);
501         
502         return TRUE;
503 }
504
505 static gboolean
506 smtp_send (CamelTransport *transport, CamelMedium *message, CamelException *ex)
507 {
508         const CamelInternetAddress *to, *cc, *bcc;
509         GList *recipients = NULL;
510         guint index, len;
511         
512         to = camel_mime_message_get_recipients (CAMEL_MIME_MESSAGE (message), CAMEL_RECIPIENT_TYPE_TO);
513         cc = camel_mime_message_get_recipients (CAMEL_MIME_MESSAGE (message), CAMEL_RECIPIENT_TYPE_CC);
514         bcc = camel_mime_message_get_recipients (CAMEL_MIME_MESSAGE (message), CAMEL_RECIPIENT_TYPE_BCC);
515         
516         /* get all of the To addresses into our recipient list */
517         len = CAMEL_ADDRESS (to)->addresses->len;
518         for (index = 0; index < len; index++) {
519                 const char *addr;
520                 
521                 if (camel_internet_address_get (to, index, NULL, &addr))
522                         recipients = g_list_append (recipients, g_strdup (addr));
523         }
524         
525         /* get all of the Cc addresses into our recipient list */
526         len = CAMEL_ADDRESS (cc)->addresses->len;
527         for (index = 0; index < len; index++) {
528                 const char *addr;
529                 
530                 if (camel_internet_address_get (cc, index, NULL, &addr))
531                         recipients = g_list_append (recipients, g_strdup (addr));
532         }
533         
534         /* get all of the Bcc addresses into our recipient list */
535         len = CAMEL_ADDRESS (bcc)->addresses->len;
536         for (index = 0; index < len; index++) {
537                 const char *addr;
538                 
539                 if (camel_internet_address_get (bcc, index, NULL, &addr))
540                         recipients = g_list_append (recipients, g_strdup (addr));
541         }
542         
543         return smtp_send_to (transport, message, recipients, ex);
544 }
545
546 static gboolean
547 smtp_helo (CamelSmtpTransport *transport, CamelException *ex)
548 {
549         /* say hello to the server */
550         gchar *cmdbuf, *respbuf = NULL;
551         struct hostent *host;
552         
553         /* get the local host name */
554         host = gethostbyaddr ((gchar *)&transport->localaddr.sin_addr, sizeof (transport->localaddr.sin_addr), AF_INET);
555         
556         /* hiya server! how are you today? */
557         if (transport->is_esmtp) {
558                 if (host && host->h_name)
559                         cmdbuf = g_strdup_printf ("EHLO %s\r\n", host->h_name);
560                 else
561                         cmdbuf = g_strdup_printf ("EHLO [%s]\r\n", inet_ntoa (transport->localaddr.sin_addr));
562         } else {
563                 if (host && host->h_name)
564                         cmdbuf = g_strdup_printf ("HELO %s\r\n", host->h_name);
565                 else
566                         cmdbuf = g_strdup_printf ("HELO [%s]\r\n", inet_ntoa (transport->localaddr.sin_addr));
567         }
568         
569         d(fprintf (stderr, "sending : %s", cmdbuf));
570         if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
571                 g_free (cmdbuf);
572                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
573                                       _("HELO request timed out: %s: non-fatal"),
574                                       g_strerror (errno));
575                 return FALSE;
576         }
577         g_free (cmdbuf);
578         
579         do {
580                 /* Check for "250" */
581                 g_free (respbuf);
582                 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
583                 
584                 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
585                 
586                 if (!respbuf || strncmp (respbuf, "250", 3)) {
587                         int error;
588
589                         error = respbuf ? atoi (respbuf) : 0;
590                         g_free (respbuf);
591                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
592                                               _("HELO response error: %s: non-fatal"),
593                                               get_smtp_error_string (error));
594                         return FALSE;
595                 }
596                 
597                 if (e_strstrcase (respbuf, "8BITMIME")) {
598                         d(fprintf (stderr, "This server supports 8bit MIME\n"));
599                         CAMEL_TRANSPORT (transport)->supports_8bit = TRUE;
600                 }
601                 
602                 /* Only parse authtypes if we don't already have them */
603                 if (transport->is_esmtp && strstr (respbuf, "AUTH") && !transport->authtypes) {
604                         /* parse for supported AUTH types */
605                         char *auths = strstr (respbuf, "AUTH") + 4;
606                         
607                         transport->authtypes = esmtp_get_authtypes (auths);
608                 }
609         } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
610         g_free (respbuf);
611         
612         return TRUE;
613 }
614
615 static gboolean
616 smtp_auth (CamelSmtpTransport *transport, const char *mech, CamelException *ex)
617 {
618         gchar *cmdbuf, *respbuf = NULL;
619         CamelSasl *sasl;
620         
621         /* tell the server we want to authenticate... */
622         cmdbuf = g_strdup_printf ("AUTH %s\r\n", mech);
623         d(fprintf (stderr, "sending : %s", cmdbuf));
624         if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
625                 g_free (cmdbuf);
626                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
627                                       _("AUTH request timed out: %s"),
628                                       g_strerror (errno));
629                 return FALSE;
630         }
631         g_free (cmdbuf);
632         
633         /* get the base64 encoded server challenge which should follow a 334 code */
634         respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
635         d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
636         if (!respbuf || strncmp (respbuf, "334", 3)) {
637                 g_free (respbuf);
638                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
639                                       _("AUTH request timed out: %s"),
640                                       g_strerror (errno));
641                 return FALSE;
642         }
643         
644         sasl = camel_sasl_new ("smtp", mech, CAMEL_SERVICE (transport));
645         if (!sasl) {
646                 g_free (respbuf);
647                 goto break_and_lose;
648         }
649         
650         while (!camel_sasl_authenticated (sasl)) {
651                 char *challenge;
652                 
653                 if (!respbuf)
654                         goto lose;
655                 
656                 /* eat whtspc */
657                 for (challenge = respbuf + 4; isspace (*challenge); challenge++);
658                 
659                 challenge = camel_sasl_challenge_base64 (sasl, challenge, ex);
660                 g_free (respbuf);
661                 if (camel_exception_is_set (ex))
662                         goto break_and_lose;
663                 
664                 /* send our challenge */
665                 cmdbuf = g_strdup_printf ("%s\r\n", challenge);
666                 g_free (challenge);
667                 d(fprintf (stderr, "sending : %s", cmdbuf));
668                 if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
669                         g_free (cmdbuf);
670                         goto lose;
671                 }
672                 g_free (cmdbuf);
673                 
674                 /* get the server's response */
675                 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
676                 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
677         }
678         
679         /* check that the server says we are authenticated */
680         if (!respbuf || strncmp (respbuf, "235", 3)) {
681                 g_free (respbuf);
682                 goto lose;
683         }
684         
685         return TRUE;
686         
687  break_and_lose:
688         /* Get the server out of "waiting for continuation data" mode. */
689         d(fprintf (stderr, "sending : *\n"));
690         camel_stream_write (transport->ostream, "*\r\n", 3);
691         respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
692         d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
693         
694  lose:
695         if (!camel_exception_is_set (ex)) {
696                 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
697                                      _("Bad authentication response from server.\n"));
698         }
699         
700         camel_object_unref (CAMEL_OBJECT (sasl));
701         
702         return FALSE;
703 }
704
705 static gboolean
706 smtp_mail (CamelSmtpTransport *transport, const char *sender, gboolean has_8bit_parts, CamelException *ex)
707 {
708         /* we gotta tell the smtp server who we are. (our email addy) */
709         gchar *cmdbuf, *respbuf = NULL;
710         
711         /* enclose address in <>'s since some SMTP daemons *require* that */
712         if (CAMEL_TRANSPORT (transport)->supports_8bit && has_8bit_parts)
713                 cmdbuf = g_strdup_printf ("MAIL FROM: <%s> BODY=8BITMIME\r\n", sender);
714         else
715                 cmdbuf = g_strdup_printf ("MAIL FROM: <%s>\r\n", sender);
716         
717         d(fprintf (stderr, "sending : %s", cmdbuf));
718         
719         if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
720                 g_free (cmdbuf);
721                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
722                                       _("MAIL FROM request timed out: %s: mail not sent"),
723                                       g_strerror (errno));
724                 return FALSE;
725         }
726         g_free (cmdbuf);
727         
728         do {
729                 /* Check for "250 Sender OK..." */
730                 g_free (respbuf);
731                 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
732                 
733                 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
734                 
735                 if (!respbuf || strncmp (respbuf, "250", 3)) {
736                         int error;
737                         
738                         error = respbuf ? atoi (respbuf) : 0;
739                         g_free (respbuf);
740                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
741                                               _("MAIL FROM response error: %s: mail not sent"),
742                                               get_smtp_error_string (error));
743                         return FALSE;
744                 }
745         } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
746         g_free (respbuf);
747         
748         return TRUE;
749 }
750
751 static gboolean
752 smtp_rcpt (CamelSmtpTransport *transport, const char *recipient, CamelException *ex)
753 {
754         /* we gotta tell the smtp server who we are going to be sending
755          * our email to */
756         gchar *cmdbuf, *respbuf = NULL;
757         
758         /* enclose address in <>'s since some SMTP daemons *require* that */
759         cmdbuf = g_strdup_printf ("RCPT TO: <%s>\r\n", recipient);
760         
761         d(fprintf (stderr, "sending : %s", cmdbuf));
762         
763         if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
764                 g_free (cmdbuf);
765                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
766                                       _("RCPT TO request timed out: %s: mail not sent"),
767                                       g_strerror (errno));
768                 return FALSE;
769         }
770         g_free (cmdbuf);
771         
772         do {
773                 /* Check for "250 Sender OK..." */
774                 g_free (respbuf);
775                 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
776                 
777                 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
778                 
779                 if (!respbuf || strncmp (respbuf, "250", 3)) {
780                         int error;
781                         
782                         error = respbuf ? atoi (respbuf) : 0;
783                         g_free (respbuf);
784                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
785                                               _("RCPT TO response error: %s: mail not sent"),
786                                               get_smtp_error_string (error));
787                         return FALSE;
788                 }
789         } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
790         g_free (respbuf);
791         
792         return TRUE;
793 }
794
795 static gboolean
796 smtp_data (CamelSmtpTransport *transport, CamelMedium *message, gboolean has_8bit_parts, CamelException *ex)
797 {
798         /* now we can actually send what's important :p */
799         gchar *cmdbuf, *respbuf = NULL;
800         CamelStreamFilter *filtered_stream;
801         CamelMimeFilter *crlffilter;
802         
803         /* if the message contains 8bit mime parts and the server
804            doesn't support it, encode 8bit parts to the best
805            encoding.  This will also enforce an encoding to keep the lines in limit */
806         if (has_8bit_parts && !CAMEL_TRANSPORT (transport)->supports_8bit)
807                 camel_mime_message_encode_8bit_parts (CAMEL_MIME_MESSAGE (message));
808         
809         cmdbuf = g_strdup ("DATA\r\n");
810         
811         d(fprintf (stderr, "sending : %s", cmdbuf));
812         
813         if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
814                 g_free (cmdbuf);
815                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
816                                       _("DATA request timed out: %s: mail not sent"),
817                                       g_strerror (errno));
818                 return FALSE;
819         }
820         g_free (cmdbuf);
821         
822         respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
823         
824         d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
825         
826         if (!respbuf || strncmp (respbuf, "354", 3)) {
827                 /* we should have gotten instructions on how to use the DATA command:
828                  * 354 Enter mail, end with "." on a line by itself
829                  */
830                 int error;
831                         
832                 error = respbuf ? atoi (respbuf) : 0;
833                 g_free (respbuf);
834                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
835                                       _("DATA response error: %s: mail not sent"),
836                                       get_smtp_error_string (error));
837                 return FALSE;
838         }
839
840         g_free (respbuf);
841         respbuf = NULL;
842         
843         /* setup stream filtering */
844         crlffilter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
845         filtered_stream = camel_stream_filter_new_with_stream (transport->ostream);
846         camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (crlffilter));
847         
848         if (camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), CAMEL_STREAM (filtered_stream)) == -1) {
849                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
850                                       _("DATA send timed out: message termination: "
851                                         "%s: mail not sent"),
852                                       g_strerror (errno));
853                 
854                 camel_object_unref (CAMEL_OBJECT (filtered_stream));
855                 
856                 return FALSE;
857         }
858         
859         camel_stream_flush (CAMEL_STREAM (filtered_stream));
860         camel_object_unref (CAMEL_OBJECT (filtered_stream));
861         
862         /* terminate the message body */
863         
864         d(fprintf (stderr, "sending : \\r\\n.\\r\\n\n"));
865         
866         if (camel_stream_write (transport->ostream, "\r\n.\r\n", 5) == -1) {
867                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
868                                       _("DATA send timed out: message termination: "
869                                         "%s: mail not sent"),
870                                       g_strerror (errno));
871                 return FALSE;
872         }
873         
874         do {
875                 /* Check for "250 Sender OK..." */
876                 g_free (respbuf);
877                 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
878                 
879                 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
880                 
881                 if (!respbuf || strncmp (respbuf, "250", 3)) {
882                         int error;
883                         
884                         error = respbuf ? atoi (respbuf) : 0;
885                         g_free (respbuf);
886                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
887                                               _("DATA response error: message termination: "
888                                                 "%s: mail not sent"),
889                                               get_smtp_error_string (error));
890                         return FALSE;
891                 }
892         } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
893         g_free (respbuf);
894         
895         return TRUE;
896 }
897
898 static gboolean
899 smtp_rset (CamelSmtpTransport *transport, CamelException *ex)
900 {
901         /* we are going to reset the smtp server (just to be nice) */
902         gchar *cmdbuf, *respbuf = NULL;
903         
904         cmdbuf = g_strdup ("RSET\r\n");
905         
906         d(fprintf (stderr, "sending : %s", cmdbuf));
907         
908         if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
909                 g_free (cmdbuf);
910                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
911                                       _("RSET request timed out: %s"),
912                                       g_strerror (errno));
913                 return FALSE;
914         }
915         g_free (cmdbuf);
916         
917         do {
918                 /* Check for "250" */
919                 g_free (respbuf);
920                 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
921                 
922                 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
923                 
924                 if (!respbuf || strncmp (respbuf, "250", 3)) {
925                         int error;
926                         
927                         error = respbuf ? atoi (respbuf) : 0;
928                         g_free (respbuf);
929                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
930                                               _("RSET response error: %s"),
931                                               get_smtp_error_string (error));
932                         return FALSE;
933                 }
934         } while (*(respbuf+3) == '-'); /* if we got "250-" then loop again */
935         g_free (respbuf);
936         
937         return TRUE;
938 }
939
940 static gboolean
941 smtp_quit (CamelSmtpTransport *transport, CamelException *ex)
942 {
943         /* we are going to reset the smtp server (just to be nice) */
944         gchar *cmdbuf, *respbuf = NULL;
945         
946         cmdbuf = g_strdup ("QUIT\r\n");
947         
948         d(fprintf (stderr, "sending : %s", cmdbuf));
949         
950         if (camel_stream_write (transport->ostream, cmdbuf, strlen (cmdbuf)) == -1) {
951                 g_free (cmdbuf);
952                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
953                                       _("QUIT request timed out: %s: non-fatal"),
954                                       g_strerror (errno));
955                 return FALSE;
956         }
957         g_free (cmdbuf);
958         
959         do {
960                 /* Check for "221" */
961                 g_free (respbuf);
962                 respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (transport->istream));
963                 
964                 d(fprintf (stderr, "received: %s\n", respbuf ? respbuf : "(null)"));
965                 
966                 if (!respbuf || strncmp (respbuf, "221", 3)) {
967                         int error;
968                         
969                         error = respbuf ? atoi (respbuf) : 0;
970                         g_free (respbuf);
971                         camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
972                                               _("QUIT response error: %s: non-fatal"),
973                                               get_smtp_error_string (error));
974                         return FALSE;
975                 }
976         } while (*(respbuf+3) == '-'); /* if we got "221-" then loop again */
977         g_free (respbuf);
978         
979         return TRUE;
980 }