smtp: Moved EHLO response handling to smtp_state_ehlo_resp()
authorSteve Holme <steve_holme@hotmail.com>
Fri, 15 Nov 2013 10:46:29 +0000 (10:46 +0000)
committerSteve Holme <steve_holme@hotmail.com>
Fri, 15 Nov 2013 10:49:17 +0000 (10:49 +0000)
Similar to the processing of untagged CAPABILITY responses in IMAP moved
the processing of multiline EHLO responses to smtp_state_ehlo_resp() and
introduced an internal response code of one to differentiate a multiline
continuation from the end of command. This also allows for the separate
processing of multiline responses from commands such as VRFY and EXPN.

lib/smtp.c

index 02af495..77d36e9 100644 (file)
@@ -230,78 +230,27 @@ static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
 {
   struct smtp_conn *smtpc = &conn->proto.smtpc;
   bool result = FALSE;
-  size_t wordlen;
 
+  /* Nothing for us */
   if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
-    return FALSE;       /* Nothing for us */
+    return FALSE;
 
   /* Do we have a command response? This should be the response code followed
      by a space and optionally some text as per RFC-5321 and as outlined in
      Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
-     only send the response code instead. */
-  result = (line[3] == ' ' || len == 5) ? TRUE : FALSE;
-  if(result)
+     only send the response code instead as per Section 4.2. */
+  if(line[3] == ' ' || len == 5) {
+    result = TRUE;
     *resp = curlx_sltosi(strtol(line, NULL, 10));
 
-  /* Are we processing EHLO command data? */
-  if(smtpc->state == SMTP_EHLO && (!result || (result && *resp/100 == 2))) {
-    line += 4;
-    len -= 4;
-
-    /* Does the server support the STARTTLS capability? */
-    if(len >= 8 && !memcmp(line, "STARTTLS", 8))
-      smtpc->tls_supported = TRUE;
-
-    /* Does the server support the SIZE capability? */
-    else if(len >= 4 && !memcmp(line, "SIZE", 4))
-      smtpc->size_supported = TRUE;
-
-    /* Do we have the authentication mechanism list? */
-    else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
-      line += 5;
-      len -= 5;
-
-      /* Loop through the data line */
-      for(;;) {
-        while(len &&
-              (*line == ' ' || *line == '\t' ||
-               *line == '\r' || *line == '\n')) {
-
-          line++;
-          len--;
-        }
-
-        if(!len)
-          break;
-
-        /* Extract the word */
-        for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
-              line[wordlen] != '\t' && line[wordlen] != '\r' &&
-              line[wordlen] != '\n';)
-          wordlen++;
-
-        /* Test the word for a matching authentication mechanism */
-        if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
-          smtpc->authmechs |= SASL_MECH_LOGIN;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
-          smtpc->authmechs |= SASL_MECH_PLAIN;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
-          smtpc->authmechs |= SASL_MECH_CRAM_MD5;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
-          smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
-          smtpc->authmechs |= SASL_MECH_GSSAPI;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
-          smtpc->authmechs |= SASL_MECH_EXTERNAL;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
-          smtpc->authmechs |= SASL_MECH_NTLM;
-        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
-          smtpc->authmechs |= SASL_MECH_XOAUTH2;
-
-        line += wordlen;
-        len -= wordlen;
-      }
-    }
+    /* Make sure real server never sends internal value */
+    if(*resp == 1)
+      *resp = 0;
+  }
+  /* Do we have a multiline (continuation) response? */
+  else if(line[3] == '-') {
+    result = TRUE;
+    *resp = 1;  /* Internal response code */
   }
 
   return result;
@@ -774,10 +723,13 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
   CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
+  const char *line = data->state.buffer;
+  size_t len = strlen(line);
+  size_t wordlen;
 
   (void)instate; /* no use for this yet */
 
-  if(smtpcode/100 != 2) {
+  if(smtpcode/100 != 2 && smtpcode != 1) {
     if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
      !conn->bits.user_passwd)
       result = smtp_perform_helo(conn);
@@ -786,21 +738,83 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
       result = CURLE_REMOTE_ACCESS_DENIED;
     }
   }
-  else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
-    /* We don't have a SSL/TLS connection yet, but SSL is requested */
-    if(smtpc->tls_supported)
-      /* Switch to TLS connection now */
-      result = smtp_perform_starttls(conn);
-    else if(data->set.use_ssl == CURLUSESSL_TRY)
-      /* Fallback and carry on with authentication */
-      result = smtp_perform_authenticate(conn);
-    else {
-      failf(data, "STARTTLS not supported.");
-      result = CURLE_USE_SSL_FAILED;
+  else {
+    line += 4;
+    len -= 4;
+
+    /* Does the server support the STARTTLS capability? */
+    if(len >= 8 && !memcmp(line, "STARTTLS", 8))
+      smtpc->tls_supported = TRUE;
+
+    /* Does the server support the SIZE capability? */
+    else if(len >= 4 && !memcmp(line, "SIZE", 4))
+      smtpc->size_supported = TRUE;
+
+    /* Do we have the authentication mechanism list? */
+    else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
+      line += 5;
+      len -= 5;
+
+      /* Loop through the data line */
+      for(;;) {
+        while(len &&
+              (*line == ' ' || *line == '\t' ||
+               *line == '\r' || *line == '\n')) {
+
+          line++;
+          len--;
+        }
+
+        if(!len)
+          break;
+
+        /* Extract the word */
+        for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
+              line[wordlen] != '\t' && line[wordlen] != '\r' &&
+              line[wordlen] != '\n';)
+          wordlen++;
+
+        /* Test the word for a matching authentication mechanism */
+        if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
+          smtpc->authmechs |= SASL_MECH_LOGIN;
+        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
+          smtpc->authmechs |= SASL_MECH_PLAIN;
+        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
+          smtpc->authmechs |= SASL_MECH_CRAM_MD5;
+        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
+          smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
+        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
+          smtpc->authmechs |= SASL_MECH_GSSAPI;
+        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
+          smtpc->authmechs |= SASL_MECH_EXTERNAL;
+        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
+          smtpc->authmechs |= SASL_MECH_NTLM;
+        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
+          smtpc->authmechs |= SASL_MECH_XOAUTH2;
+
+        line += wordlen;
+        len -= wordlen;
+      }
+    }
+
+    if(smtpcode != 1) {
+      if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
+        /* We don't have a SSL/TLS connection yet, but SSL is requested */
+        if(smtpc->tls_supported)
+          /* Switch to TLS connection now */
+          result = smtp_perform_starttls(conn);
+        else if(data->set.use_ssl == CURLUSESSL_TRY)
+          /* Fallback and carry on with authentication */
+          result = smtp_perform_authenticate(conn);
+        else {
+          failf(data, "STARTTLS not supported.");
+          result = CURLE_USE_SSL_FAILED;
+        }
+      }
+      else
+        result = smtp_perform_authenticate(conn);
     }
   }
-  else
-    result = smtp_perform_authenticate(conn);
 
   return result;
 }
@@ -1344,7 +1358,7 @@ static CURLcode smtp_statemach_act(struct connectdata *conn)
     return result;
 
   /* Store the latest response for later retrieval */
-  if(smtpc->state != SMTP_QUIT)
+  if(smtpc->state != SMTP_QUIT && smtpcode != 1)
     data->info.httpcode = smtpcode;
 
   if(smtpcode) {