2005-12-21 [colin] 1.9.100cvs101
[claws.git] / src / common / smtp.c
index 346c63e5f4eae85ffcb431e99fc3023b00976fcc..4dfe262512bf4b2edd0d1f271a85b82bc942f58b 100644 (file)
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
 #ifdef HAVE_CONFIG_H
 #endif
 
 #include <glib.h>
+#ifdef ENABLE_NLS
+#include <glib/gi18n.h>
+#else
+#define _(a) (a)
+#define N_(a) (a)
+#endif
 #include <stdio.h>
 #include <string.h>
 
-#include "intl.h"
 #include "smtp.h"
 #include "md5.h"
 #include "base64.h"
 
 static void smtp_session_destroy(Session *session);
 
-static gint smtp_from(SMTPSession *session);
-
 static gint smtp_auth(SMTPSession *session);
 static gint smtp_starttls(SMTPSession *session);
 static gint smtp_auth_cram_md5(SMTPSession *session);
 static gint smtp_auth_login(SMTPSession *session);
+static gint smtp_auth_plain(SMTPSession *session);
 
 static gint smtp_ehlo(SMTPSession *session);
 static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg);
@@ -48,8 +52,7 @@ static gint smtp_helo(SMTPSession *session);
 static gint smtp_rcpt(SMTPSession *session);
 static gint smtp_data(SMTPSession *session);
 static gint smtp_send_data(SMTPSession *session);
-/* static gint smtp_rset(SMTPSession *session); */
-static gint smtp_quit(SMTPSession *session);
+static gint smtp_make_ready(SMTPSession *session);
 static gint smtp_eom(SMTPSession *session);
 
 static gint smtp_session_recv_msg(Session *session, const gchar *msg);
@@ -95,6 +98,7 @@ Session *smtp_session_new(void)
        session->avail_auth_type           = 0;
        session->forced_auth_type          = 0;
        session->auth_type                 = 0;
+       session->esmtp_flags               = 0;
 
        session->error_val                 = SM_OK;
        session->error_msg                 = NULL;
@@ -116,7 +120,7 @@ static void smtp_session_destroy(Session *session)
        g_free(smtp_session->error_msg);
 }
 
-static gint smtp_from(SMTPSession *session)
+gint smtp_from(SMTPSession *session)
 {
        gchar buf[MSGBUFSIZE];
        gchar *mail_size = NULL;
@@ -125,7 +129,7 @@ static gint smtp_from(SMTPSession *session)
 
        session->state = SMTP_FROM;
        
-       if (session->is_esmtp)
+       if (session->is_esmtp && (session->esmtp_flags & ESMTP_SIZE)!=0)
                mail_size = g_strdup_printf(" SIZE=%d", session->send_data_len);
        else
                mail_size = g_strdup("");
@@ -140,7 +144,8 @@ static gint smtp_from(SMTPSession *session)
 
        g_free(mail_size);
 
-       session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
+       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0)
+               return SM_ERROR;
        log_print("%sSMTP> %s\n", (session->is_esmtp?"E":""), buf);
 
        return SM_OK;
@@ -161,6 +166,10 @@ static gint smtp_auth(SMTPSession *session)
                 (session->forced_auth_type == 0 &&
                  (session->avail_auth_type & SMTPAUTH_LOGIN) != 0))
                smtp_auth_login(session);
+       else if (session->forced_auth_type == SMTPAUTH_PLAIN ||
+                (session->forced_auth_type == 0 &&
+                 (session->avail_auth_type & SMTPAUTH_PLAIN) != 0))
+               smtp_auth_plain(session);
        else {
                log_warning(_("SMTP AUTH not available\n"));
                return SM_AUTHFAIL;
@@ -180,13 +189,15 @@ static gint smtp_auth_recv(SMTPSession *session, const gchar *msg)
                if (!strncmp(msg, "334 ", 4)) {
                        base64_encode(buf, session->user, strlen(session->user));
 
-                       session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
-                                        buf);
+                       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
+                                        buf) < 0)
+                               return SM_ERROR;
                        log_print("ESMTP> [USERID]\n");
                } else {
                        /* Server rejects AUTH */
-                       session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
-                                        "*");
+                       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
+                                        "*") < 0)
+                               return SM_ERROR;
                        log_print("ESMTP> *\n");
                }
                break;
@@ -218,21 +229,24 @@ static gint smtp_auth_recv(SMTPSession *session, const gchar *msg)
                        base64_encode(response64, response, strlen(response));
                        g_free(response);
 
-                       session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
-                                        response64);
+                       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
+                                        response64) < 0)
+                               return SM_ERROR;
                        log_print("ESMTP> %s\n", response64);
                        g_free(response64);
                } else {
                        /* Server rejects AUTH */
-                       session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
-                                        "*");
+                       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL,
+                                        "*") < 0)
+                               return SM_ERROR;
                        log_print("ESMTP> *\n");
                }
                break;
        case SMTPAUTH_DIGEST_MD5:
         default:
                /* stop smtp_auth when no correct authtype */
-               session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "*");
+               if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "*") < 0)
+                       return SM_ERROR;
                log_print("ESMTP> *\n");
                break;
        }
@@ -252,7 +266,9 @@ static gint smtp_auth_login_user_recv(SMTPSession *session, const gchar *msg)
                /* Server rejects AUTH */
                g_snprintf(buf, sizeof(buf), "*");
 
-       session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
+       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0)
+               return SM_ERROR;
+
        log_print("ESMTP> [PASSWORD]\n");
 
        return SM_OK;
@@ -268,7 +284,8 @@ static gint smtp_ehlo(SMTPSession *session)
 
        g_snprintf(buf, sizeof(buf), "EHLO %s",
                   session->hostname ? session->hostname : get_domain_name());
-       session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
+       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0)
+               return SM_ERROR;
        log_print("ESMTP> %s\n", buf);
 
        return SM_OK;
@@ -282,6 +299,8 @@ static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg)
                if (*p == '-' || *p == ' ') p++;
                if (g_ascii_strncasecmp(p, "AUTH", 4) == 0) {
                        p += 5;
+                       if (strcasestr(p, "PLAIN"))
+                               session->avail_auth_type |= SMTPAUTH_PLAIN;
                        if (strcasestr(p, "LOGIN"))
                                session->avail_auth_type |= SMTPAUTH_LOGIN;
                        if (strcasestr(p, "CRAM-MD5"))
@@ -292,6 +311,11 @@ static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg)
                if (g_ascii_strncasecmp(p, "SIZE", 4) == 0) {
                        p += 5;
                        session->max_message_size = atoi(p);
+                       session->esmtp_flags |= ESMTP_SIZE;
+               }
+               if (g_ascii_strncasecmp(p, "STARTTLS", 8) == 0) {
+                       p += 9;
+                       session->avail_auth_type |= SMTPAUTH_TLS_AVAILABLE;
                }
                return SM_OK;
        } else if ((msg[0] == '1' || msg[0] == '2' || msg[0] == '3') &&
@@ -308,7 +332,8 @@ static gint smtp_starttls(SMTPSession *session)
 {
        session->state = SMTP_STARTTLS;
 
-       session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "STARTTLS");
+       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "STARTTLS") < 0)
+               return SM_ERROR;
        log_print("ESMTP> STARTTLS\n");
 
        return SM_OK;
@@ -319,18 +344,73 @@ static gint smtp_auth_cram_md5(SMTPSession *session)
        session->state = SMTP_AUTH;
        session->auth_type = SMTPAUTH_CRAM_MD5;
 
-       session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH CRAM-MD5");
+       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH CRAM-MD5") < 0)
+               return SM_ERROR;
        log_print("ESMTP> AUTH CRAM-MD5\n");
 
        return SM_OK;
 }
 
+static gint smtp_auth_plain(SMTPSession *session)
+{
+       gchar buf[MSGBUFSIZE];
+
+       /* 
+        * +1      +1      +1
+        * \0<user>\0<pass>\0 
+        */
+       int b64len = (1 + strlen(session->user) + 1 + strlen(session->pass) + 1);
+       gchar *b64buf = g_malloc(b64len);
+
+       /* use the char *ptr to walk the base64 string with embedded \0 */
+       char  *a = b64buf;
+       int  b64cnt = 0;
+
+       session->state = SMTP_AUTH_PLAIN;
+       session->auth_type = SMTPAUTH_PLAIN;
+
+       memset(buf, 0, sizeof buf);
+
+       /*
+        * have to construct the string bit by bit. sprintf can't do it in one.
+        * first field is null, so string is \0<user>\0<password>
+        */
+       *a = 0;
+       a++;
+
+       g_snprintf (a, b64len - 1, "%s", session->user);
+
+       b64cnt = strlen(session->user)+1;
+       a += b64cnt;
+
+       g_snprintf (a, b64len - b64cnt - 1, "%s", session->pass);
+       b64cnt += strlen(session->pass) + 1;    
+
+       /*
+        * reuse the char *ptr to offset into the textbuf to meld
+        * the plaintext ESMTP message and the base64 string value
+        */
+       strcpy(buf, "AUTH PLAIN ");
+       a = buf + strlen(buf);
+       base64_encode(a, b64buf, b64cnt);
+
+       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0)
+               return SM_ERROR;
+
+       log_print("ESMTP> [AUTH PLAIN]\n");
+
+       g_free(b64buf);
+
+       return SM_OK;
+}
+
 static gint smtp_auth_login(SMTPSession *session)
 {
        session->state = SMTP_AUTH;
        session->auth_type = SMTPAUTH_LOGIN;
 
-       session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH LOGIN");
+       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH LOGIN") < 0)
+               return SM_ERROR;
        log_print("ESMTP> AUTH LOGIN\n");
 
        return SM_OK;
@@ -344,7 +424,8 @@ static gint smtp_helo(SMTPSession *session)
 
        g_snprintf(buf, sizeof(buf), "HELO %s",
                   session->hostname ? session->hostname : get_domain_name());
-       session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
+       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0)
+               return SM_ERROR;
        log_print("SMTP> %s\n", buf);
 
        return SM_OK;
@@ -365,7 +446,8 @@ static gint smtp_rcpt(SMTPSession *session)
                g_snprintf(buf, sizeof(buf), "RCPT TO:%s", to);
        else
                g_snprintf(buf, sizeof(buf), "RCPT TO:<%s>", to);
-       session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf);
+       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf) < 0)
+               return SM_ERROR;
        log_print("SMTP> %s\n", buf);
 
        session->cur_to = session->cur_to->next;
@@ -377,7 +459,8 @@ static gint smtp_data(SMTPSession *session)
 {
        session->state = SMTP_DATA;
 
-       session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "DATA");
+       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "DATA") < 0)
+               return SM_ERROR;
        log_print("SMTP> DATA\n");
 
        return SM_OK;
@@ -393,19 +476,14 @@ static gint smtp_send_data(SMTPSession *session)
        return SM_OK;
 }
 
-#if 0
-static gint smtp_rset(SMTPSession *session)
+static gint smtp_make_ready(SMTPSession *session)
 {
-       session->state = SMTP_RSET;
-
-       session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "RSET");
-       log_print("SMTP> RSET\n");
+       session->state = SMTP_MAIL_SENT_OK;
 
        return SM_OK;
 }
-#endif
 
-static gint smtp_quit(SMTPSession *session)
+gint smtp_quit(SMTPSession *session)
 {
        session->state = SMTP_QUIT;
 
@@ -419,7 +497,8 @@ static gint smtp_eom(SMTPSession *session)
 {
        session->state = SMTP_EOM;
 
-       session_send_msg(SESSION(session), SESSION_MSG_NORMAL, ".");
+       if (session_send_msg(SESSION(session), SESSION_MSG_NORMAL, ".") < 0)
+               return SM_ERROR;
        log_print("SMTP> . (EOM)\n");
 
        return SM_OK;
@@ -429,7 +508,8 @@ static gint smtp_session_recv_msg(Session *session, const gchar *msg)
 {
        SMTPSession *smtp_session = SMTP_SESSION(session);
        gboolean cont = FALSE;
-
+       gint ret = 0;
+       
        if (strlen(msg) < 4) {
                log_warning(_("bad SMTP response\n"));
                return -1;
@@ -439,6 +519,7 @@ static gint smtp_session_recv_msg(Session *session, const gchar *msg)
        case SMTP_EHLO:
        case SMTP_STARTTLS:
        case SMTP_AUTH:
+       case SMTP_AUTH_PLAIN:
        case SMTP_AUTH_LOGIN_USER:
        case SMTP_AUTH_LOGIN_PASS:
        case SMTP_AUTH_CRAM_MD5:
@@ -501,15 +582,15 @@ static gint smtp_session_recv_msg(Session *session, const gchar *msg)
 #else
                if (smtp_session->user || smtp_session->is_esmtp)
 #endif
-                       smtp_ehlo(smtp_session);
+                       ret = smtp_ehlo(smtp_session);
                else
-                       smtp_helo(smtp_session);
+                       ret = smtp_helo(smtp_session);
                break;
        case SMTP_HELO:
-               smtp_from(smtp_session);
+               ret = smtp_from(smtp_session);
                break;
        case SMTP_EHLO:
-               smtp_ehlo_recv(smtp_session, msg);
+               ret = smtp_ehlo_recv(smtp_session, msg);
                if (cont == TRUE)
                        break;
                if (smtp_session->max_message_size > 0
@@ -526,15 +607,23 @@ static gint smtp_session_recv_msg(Session *session, const gchar *msg)
 #if USE_OPENSSL
                if (session->ssl_type == SSL_STARTTLS &&
                    smtp_session->tls_init_done == FALSE) {
-                       smtp_starttls(smtp_session);
+                       ret = smtp_starttls(smtp_session);
                        break;
                }
 #endif
                if (smtp_session->user) {
-                       if (smtp_auth(smtp_session) != SM_OK)
-                               smtp_from(smtp_session);
+                       if (smtp_auth(smtp_session) != SM_OK) {
+#if USE_OPENSSL
+                               if (session->ssl_type == SSL_NONE
+                               &&  smtp_session->tls_init_done == FALSE
+                               &&  (smtp_session->avail_auth_type & SMTPAUTH_TLS_AVAILABLE))
+                                       ret = smtp_starttls(smtp_session);
+                               else
+#endif
+                                       ret = smtp_from(smtp_session);
+                       }
                } else
-                       smtp_from(smtp_session);
+                       ret = smtp_from(smtp_session);
                break;
        case SMTP_STARTTLS:
 #if USE_OPENSSL
@@ -545,34 +634,35 @@ static gint smtp_session_recv_msg(Session *session, const gchar *msg)
                        return -1;
                }
                smtp_session->tls_init_done = TRUE;
-               smtp_ehlo(smtp_session);
+               ret = smtp_ehlo(smtp_session);
 #endif
                break;
        case SMTP_AUTH:
-               smtp_auth_recv(smtp_session, msg);
+               ret = smtp_auth_recv(smtp_session, msg);
                break;
        case SMTP_AUTH_LOGIN_USER:
-               smtp_auth_login_user_recv(smtp_session, msg);
+               ret = smtp_auth_login_user_recv(smtp_session, msg);
                break;
+       case SMTP_AUTH_PLAIN:
        case SMTP_AUTH_LOGIN_PASS:
        case SMTP_AUTH_CRAM_MD5:
-               smtp_from(smtp_session);
+               ret = smtp_from(smtp_session);
                break;
        case SMTP_FROM:
                if (smtp_session->cur_to)
-                       smtp_rcpt(smtp_session);
+                       ret = smtp_rcpt(smtp_session);
                break;
        case SMTP_RCPT:
                if (smtp_session->cur_to)
-                       smtp_rcpt(smtp_session);
+                       ret = smtp_rcpt(smtp_session);
                else
-                       smtp_data(smtp_session);
+                       ret = smtp_data(smtp_session);
                break;
        case SMTP_DATA:
-               smtp_send_data(smtp_session);
+               ret = smtp_send_data(smtp_session);
                break;
        case SMTP_EOM:
-               smtp_quit(smtp_session);
+               smtp_make_ready(smtp_session);
                break;
        case SMTP_QUIT:
                session_disconnect(session);
@@ -584,14 +674,16 @@ static gint smtp_session_recv_msg(Session *session, const gchar *msg)
                return -1;
        }
 
-       if (cont)
+       if (cont && ret == SM_OK)
                return session_recv_msg(session);
 
-       return 0;
+       if (ret != SM_OK)
+               smtp_session->error_val = SM_ERROR;
+
+       return ret == SM_OK ? 0 : -1;
 }
 
 static gint smtp_session_send_data_finished(Session *session, guint len)
 {
-       smtp_eom(SMTP_SESSION(session));
-       return 0;
+       return smtp_eom(SMTP_SESSION(session));
 }