| 1 | 
 /* | 
 
 
 
 
 
 | 2 | 
     This file is part of libquickmail. | 
 
 
 
 
 
 | 3 | 
  | 
 
 
 
 
 
 | 4 | 
     libquickmail is free software: you can redistribute it and/or modify | 
 
 
 
 
 
 | 5 | 
     it under the terms of the GNU General Public License as published by | 
 
 
 
 
 
 | 6 | 
     the Free Software Foundation, either version 3 of the License, or | 
 
 
 
 
 
 | 7 | 
     (at your option) any later version. | 
 
 
 
 
 
 | 8 | 
  | 
 
 
 
 
 
 | 9 | 
     libquickmail is distributed in the hope that it will be useful, | 
 
 
 
 
 
 | 10 | 
     but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 
 
 
 
 
 | 11 | 
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 
 
 
 
 
 | 12 | 
     GNU General Public License for more details. | 
 
 
 
 
 
 | 13 | 
  | 
 
 
 
 
 
 | 14 | 
     You should have received a copy of the GNU General Public License | 
 
 
 
 
 
 | 15 | 
     along with libquickmail.  If not, see <http://www.gnu.org/licenses/>. | 
 
 
 
 
 
 | 16 | 
 */ | 
 
 
 
 
 
 | 17 | 
  | 
 
 
 
 
 
 | 18 | 
 #include "smtpsocket.h" | 
 
 
 
 
 
 | 19 | 
 #include <stdlib.h> | 
 
 
 
 
 
 | 20 | 
 #include <ctype.h> | 
 
 
 
 
 
 | 21 | 
 #include <string.h> | 
 
 
 
 
 
 | 22 | 
 #if _MSC_VER | 
 
 
 
 
 
 | 23 | 
 #define va_copy(dst,src) ((dst) = (src)) | 
 
 
 
 
 
 | 24 | 
 #endif | 
 
 
 
 
 
 | 25 | 
  | 
 
 
 
 
 
 | 26 | 
 //////////////////////////////////////////////////////////////////////// | 
 
 
 
 
 
 | 27 | 
  | 
 
 
 
 
 
 | 28 | 
 #define DEBUG_ERROR(errmsg) | 
 
 
 
 
 
 | 29 | 
 static const char* ERRMSG_MEMORY_ALLOCATION_ERROR = "Memory allocation error"; | 
 
 
 
 
 
 | 30 | 
  | 
 
 
 
 
 
 | 31 | 
 //////////////////////////////////////////////////////////////////////// | 
 
 
 
 
 
 | 32 | 
  | 
 
 
 
 
 
 | 33 | 
 SOCKET socket_open (const char* smtpserver, unsigned int smtpport, char** errmsg) | 
 
 
 
 
 
 | 34 | 
 { | 
 
 
 
 
 
 | 35 | 
   struct in_addr ipv4addr; | 
 
 
 
 
 
 | 36 | 
   SOCKET sock; | 
 
 
 
 
 
 | 37 | 
   struct sockaddr_in remote_sock_addr; | 
 
 
 
 
 
 | 38 | 
   static const struct linger linger_option = {-1, 2};   //linger 2 seconds when disconnecting | 
 
 
 
 
 
 | 39 | 
   //determine IPv4 address of SMTP server | 
 
 
 
 
 
 | 40 | 
   ipv4addr.s_addr = inet_addr(smtpserver); | 
 
 
 
 
 
 | 41 | 
   if (ipv4addr.s_addr == INADDR_NONE) { | 
 
 
 
 
 
 | 42 | 
     struct hostent* addr; | 
 
 
 
 
 
 | 43 | 
     if ((addr = gethostbyname(smtpserver)) != NULL && (addr->h_addrtype == AF_INET && addr->h_length >= 1 && ((struct in_addr*)addr->h_addr)->s_addr != 0)) | 
 
 
 
 
 
 | 44 | 
       memcpy(&ipv4addr, addr->h_addr, sizeof(ipv4addr)); | 
 
 
 
 
 
 | 45 | 
   } | 
 
 
 
 
 
 | 46 | 
   if (ipv4addr.s_addr == INADDR_NONE) { | 
 
 
 
 
 
 | 47 | 
     if (errmsg) | 
 
 
 
 
 
 | 48 | 
       *errmsg = "Unable to resolve SMTP server host name"; | 
 
 
 
 
 
 | 49 | 
     return INVALID_SOCKET; | 
 
 
 
 
 
 | 50 | 
   } | 
 
 
 
 
 
 | 51 | 
   //create socket | 
 
 
 
 
 
 | 52 | 
   sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | 
 
 
 
 
 
 | 53 | 
   if (sock == INVALID_SOCKET) { | 
 
 
 
 
 
 | 54 | 
     if (errmsg) | 
 
 
 
 
 
 | 55 | 
       *errmsg = "Error creating socket for SMTP connection"; | 
 
 
 
 
 
 | 56 | 
     return INVALID_SOCKET; | 
 
 
 
 
 
 | 57 | 
   } | 
 
 
 
 
 
 | 58 | 
   //connect | 
 
 
 
 
 
 | 59 | 
   remote_sock_addr.sin_family = AF_INET; | 
 
 
 
 
 
 | 60 | 
   remote_sock_addr.sin_port = htons(smtpport); | 
 
 
 
 
 
 | 61 | 
   remote_sock_addr.sin_addr.s_addr = ipv4addr.s_addr; | 
 
 
 
 
 
 | 62 | 
   if (connect(sock, (struct sockaddr*)&remote_sock_addr, sizeof(remote_sock_addr)) != 0) { | 
 
 
 
 
 
 | 63 | 
     if (errmsg) | 
 
 
 
 
 
 | 64 | 
       *errmsg = "Error connecting to SMTP server"; | 
 
 
 
 
 
 | 65 | 
     socket_close(sock); | 
 
 
 
 
 
 | 66 | 
     return INVALID_SOCKET; | 
 
 
 
 
 
 | 67 | 
   } | 
 
 
 
 
 
 | 68 | 
   //set linger option | 
 
 
 
 
 
 | 69 | 
   setsockopt(sock, SOL_SOCKET, SO_LINGER, (const char*)&linger_option, sizeof(linger_option)); | 
 
 
 
 
 
 | 70 | 
   return sock; | 
 
 
 
 
 
 | 71 | 
 } | 
 
 
 
 
 
 | 72 | 
  | 
 
 
 
 
 
 | 73 | 
 void socket_close (SOCKET sock) | 
 
 
 
 
 
 | 74 | 
 { | 
 
 
 
 
 
 | 75 | 
 #ifndef _WIN32 | 
 
 
 
 
 
 | 76 | 
   shutdown(sock, 2); | 
 
 
 
 
 
 | 77 | 
 #else | 
 
 
 
 
 
 | 78 | 
   closesocket(sock); | 
 
 
 
 
 
 | 79 | 
 #endif | 
 
 
 
 
 
 | 80 | 
 } | 
 
 
 
 
 
 | 81 | 
  | 
 
 
 
 
 
 | 82 | 
 int socket_send (SOCKET sock, const char* buf, int len) | 
 
 
 
 
 
 | 83 | 
 { | 
 
 
 
 
 
 | 84 | 
   int total_sent = 0; | 
 
 
 
 
 
 | 85 | 
   int l = 0; | 
 
 
 
 
 
 | 86 | 
   if (sock == 0 || !buf) | 
 
 
 
 
 
 | 87 | 
     return 0; | 
 
 
 
 
 
 | 88 | 
   if (len < 0) | 
 
 
 
 
 
 | 89 | 
     len = strlen(buf); | 
 
 
 
 
 
 | 90 | 
   while (len > 0 && (l = send(sock, buf, len, 0)) < len) { | 
 
 
 
 
 
 | 91 | 
     if (l == SOCKET_ERROR || l > len) | 
 
 
 
 
 
 | 92 | 
       return (total_sent > 0 ? total_sent : -1); | 
 
 
 
 
 
 | 93 | 
     total_sent += l; | 
 
 
 
 
 
 | 94 | 
     buf += l; | 
 
 
 
 
 
 | 95 | 
     len -= l; | 
 
 
 
 
 
 | 96 | 
   } | 
 
 
 
 
 
 | 97 | 
   return total_sent + l; | 
 
 
 
 
 
 | 98 | 
 } | 
 
 
 
 
 
 | 99 | 
  | 
 
 
 
 
 
 | 100 | 
 int socket_data_waiting (SOCKET sock, int timeoutseconds) | 
 
 
 
 
 
 | 101 | 
 { | 
 
 
 
 
 
 | 102 | 
   fd_set rfds; | 
 
 
 
 
 
 | 103 | 
   struct timeval tv; | 
 
 
 
 
 
 | 104 | 
   if (sock == 0) | 
 
 
 
 
 
 | 105 | 
     return 0; | 
 
 
 
 
 
 | 106 | 
   //make a set with only this socket | 
 
 
 
 
 
 | 107 | 
   FD_ZERO(&rfds); | 
 
 
 
 
 
 | 108 | 
   FD_SET(sock, &rfds); | 
 
 
 
 
 
 | 109 | 
   //make a timeval with the supplied timeout | 
 
 
 
 
 
 | 110 | 
   tv.tv_sec = timeoutseconds; | 
 
 
 
 
 
 | 111 | 
   tv.tv_usec = 0; | 
 
 
 
 
 
 | 112 | 
   //check the socket | 
 
 
 
 
 
 | 113 | 
   return (select(1, &rfds, NULL, NULL, &tv) > 0); | 
 
 
 
 
 
 | 114 | 
 } | 
 
 
 
 
 
 | 115 | 
  | 
 
 
 
 
 
 | 116 | 
 char* socket_receive_smtp (SOCKET sock) | 
 
 
 
 
 
 | 117 | 
 { | 
 
 
 
 
 
 | 118 | 
   char* buf = NULL; | 
 
 
 
 
 
 | 119 | 
   int bufsize = READ_BUFFER_CHUNK_SIZE; | 
 
 
 
 
 
 | 120 | 
   int pos = 0; | 
 
 
 
 
 
 | 121 | 
   int linestart; | 
 
 
 
 
 
 | 122 | 
   int n; | 
 
 
 
 
 
 | 123 | 
   if ((buf = (char*)malloc(bufsize)) == NULL) { | 
 
 
 
 
 
 | 124 | 
     DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR) | 
 
 
 
 
 
 | 125 | 
     return NULL; | 
 
 
 
 
 
 | 126 | 
   } | 
 
 
 
 
 
 | 127 | 
   do { | 
 
 
 
 
 
 | 128 | 
     //insert line break if response is multiple lines | 
 
 
 
 
 
 | 129 | 
     if (pos > 0) { | 
 
 
 
 
 
 | 130 | 
       buf[pos++] = '\n'; | 
 
 
 
 
 
 | 131 | 
       if (pos >= bufsize) { | 
 
 
 
 
 
 | 132 | 
         char* newbuf; | 
 
 
 
 
 
 | 133 | 
         if ((newbuf = (char*)realloc(buf, bufsize + READ_BUFFER_CHUNK_SIZE)) == NULL) { | 
 
 
 
 
 
 | 134 | 
           free(buf); | 
 
 
 
 
 
 | 135 | 
           DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR) | 
 
 
 
 
 
 | 136 | 
           return NULL; | 
 
 
 
 
 
 | 137 | 
         } | 
 
 
 
 
 
 | 138 | 
         buf = newbuf; | 
 
 
 
 
 
 | 139 | 
         bufsize += READ_BUFFER_CHUNK_SIZE; | 
 
 
 
 
 
 | 140 | 
       } | 
 
 
 
 
 
 | 141 | 
     } | 
 
 
 
 
 
 | 142 | 
     //add each character read until it is a line break | 
 
 
 
 
 
 | 143 | 
     linestart = pos; | 
 
 
 
 
 
 | 144 | 
     while ((n = recv(sock, buf + pos, 1, 0)) == 1) { | 
 
 
 
 
 
 | 145 | 
       //detect optional carriage return (CR) | 
 
 
 
 
 
 | 146 | 
       if (buf[pos] == '\r') | 
 
 
 
 
 
 | 147 | 
         if (recv(sock, buf + pos, 1, 0) < 1) | 
 
 
 
 
 
 | 148 | 
           return NULL; | 
 
 
 
 
 
 | 149 | 
       //detect line feed (LF) | 
 
 
 
 
 
 | 150 | 
       if (buf[pos] == '\n') | 
 
 
 
 
 
 | 151 | 
         break; | 
 
 
 
 
 
 | 152 | 
       //enlarge buffer if necessary | 
 
 
 
 
 
 | 153 | 
       if (++pos >= bufsize) { | 
 
 
 
 
 
 | 154 | 
         char* newbuf; | 
 
 
 
 
 
 | 155 | 
         if ((newbuf = (char*)realloc(buf, bufsize + READ_BUFFER_CHUNK_SIZE)) == NULL) { | 
 
 
 
 
 
 | 156 | 
           free(buf); | 
 
 
 
 
 
 | 157 | 
           DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR) | 
 
 
 
 
 
 | 158 | 
           return NULL; | 
 
 
 
 
 
 | 159 | 
         } | 
 
 
 
 
 
 | 160 | 
         buf = newbuf; | 
 
 
 
 
 
 | 161 | 
         bufsize += READ_BUFFER_CHUNK_SIZE; | 
 
 
 
 
 
 | 162 | 
       } | 
 
 
 
 
 
 | 163 | 
     } | 
 
 
 
 
 
 | 164 | 
     //exit on error (e.g. if connection is closed) | 
 
 
 
 
 
 | 165 | 
     if (n < 1) | 
 
 
 
 
 
 | 166 | 
       return NULL; | 
 
 
 
 
 
 | 167 | 
   } while (!isdigit(buf[linestart]) || !isdigit(buf[linestart + 1]) || !isdigit(buf[linestart + 2]) || buf[linestart + 3] != ' '); | 
 
 
 
 
 
 | 168 | 
   buf[pos] = 0; | 
 
 
 
 
 
 | 169 | 
   return buf; | 
 
 
 
 
 
 | 170 | 
 } | 
 
 
 
 
 
 | 171 | 
  | 
 
 
 
 
 
 | 172 | 
 int socket_get_smtp_code (SOCKET sock, char** message) | 
 
 
 
 
 
 | 173 | 
 { | 
 
 
 
 
 
 | 174 | 
   int code; | 
 
 
 
 
 
 | 175 | 
   char* buf = socket_receive_smtp(sock); | 
 
 
 
 
 
 | 176 | 
   if (!buf || strlen(buf) < 4 || (buf[3] != ' ' && buf[3] != '-')) { | 
 
 
 
 
 
 | 177 | 
     free(buf); | 
 
 
 
 
 
 | 178 | 
     return 999; | 
 
 
 
 
 
 | 179 | 
   } | 
 
 
 
 
 
 | 180 | 
   //get code | 
 
 
 
 
 
 | 181 | 
   buf[3] = 0; | 
 
 
 
 
 
 | 182 | 
   code = atoi(buf); | 
 
 
 
 
 
 | 183 | 
   //get error message (if needed) | 
 
 
 
 
 
 | 184 | 
   if (message /*&& code >= 400*/) | 
 
 
 
 
 
 | 185 | 
     *message = strdup(buf + 4); | 
 
 
 
 
 
 | 186 | 
   //clean up and return | 
 
 
 
 
 
 | 187 | 
   free(buf); | 
 
 
 
 
 
 | 188 | 
   return code; | 
 
 
 
 
 
 | 189 | 
 } | 
 
 
 
 
 
 | 190 | 
  | 
 
 
 
 
 
 | 191 | 
 int socket_smtp_command (SOCKET sock, FILE* debuglog, const char* template, ...) | 
 
 
 
 
 
 | 192 | 
 { | 
 
 
 
 
 
 | 193 | 
   char* message; | 
 
 
 
 
 
 | 194 | 
   int statuscode; | 
 
 
 
 
 
 | 195 | 
   //send command (if one is supplied) | 
 
 
 
 
 
 | 196 | 
   if (template) { | 
 
 
 
 
 
 | 197 | 
     va_list ap; | 
 
 
 
 
 
 | 198 | 
     va_list aq; | 
 
 
 
 
 
 | 199 | 
     char* cmd; | 
 
 
 
 
 
 | 200 | 
     int cmdlen; | 
 
 
 
 
 
 | 201 | 
     va_start(ap, template); | 
 
 
 
 
 
 | 202 | 
     //construct command to send | 
 
 
 
 
 
 | 203 | 
     va_copy(aq, ap); | 
 
 
 
 
 
 | 204 | 
     cmdlen = vsnprintf(NULL, 0, template, aq); | 
 
 
 
 
 
 | 205 | 
     va_end(aq); | 
 
 
 
 
 
 | 206 | 
     if ((cmd = (char*)malloc(cmdlen + 3)) == NULL) { | 
 
 
 
 
 
 | 207 | 
       DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR) | 
 
 
 
 
 
 | 208 | 
       if (debuglog) | 
 
 
 
 
 
 | 209 | 
         fprintf(debuglog, ERRMSG_MEMORY_ALLOCATION_ERROR); | 
 
 
 
 
 
 | 210 | 
       va_end(ap); | 
 
 
 
 
 
 | 211 | 
       return 999; | 
 
 
 
 
 
 | 212 | 
     } | 
 
 
 
 
 
 | 213 | 
     vsnprintf(cmd, cmdlen + 1, template, ap); | 
 
 
 
 
 
 | 214 | 
     //log command to send | 
 
 
 
 
 
 | 215 | 
     if (debuglog) | 
 
 
 
 
 
 | 216 | 
       fprintf(debuglog, "SMTP> %s\n", cmd); | 
 
 
 
 
 
 | 217 | 
     //append CR+LF | 
 
 
 
 
 
 | 218 | 
     strcpy(cmd + cmdlen, "\r\n"); | 
 
 
 
 
 
 | 219 | 
     cmdlen += 2; | 
 
 
 
 
 
 | 220 | 
     //send command | 
 
 
 
 
 
 | 221 | 
     statuscode = socket_send(sock, cmd, cmdlen); | 
 
 
 
 
 
 | 222 | 
                 //clean up | 
 
 
 
 
 
 | 223 | 
     free(cmd); | 
 
 
 
 
 
 | 224 | 
     va_end(ap); | 
 
 
 
 
 
 | 225 | 
     if (statuscode < 0) | 
 
 
 
 
 
 | 226 | 
       return 999; | 
 
 
 
 
 
 | 227 | 
   } | 
 
 
 
 
 
 | 228 | 
   //receive result | 
 
 
 
 
 
 | 229 | 
   message = NULL; | 
 
 
 
 
 
 | 230 | 
   statuscode = socket_get_smtp_code(sock, &message); | 
 
 
 
 
 
 | 231 | 
   if (debuglog) | 
 
 
 
 
 
 | 232 | 
     fprintf(debuglog, "SMTP< %i %s\n", statuscode, (message ? message : "")); | 
 
 
 
 
 
 | 233 | 
   free(message); | 
 
 
 
 
 
 | 234 | 
   return statuscode; | 
 
 
 
 
 
 | 235 | 
 } |