| | @@ -117,11 +117,13 @@ |
| 117 | 117 | char *zHostname; /* Hostname of SMTP server for zDest */ |
| 118 | 118 | u32 smtpFlags; /* Flags changing the operation */ |
| 119 | 119 | FILE *logFile; /* Write session transcript to this log file */ |
| 120 | 120 | Blob *pTranscript; /* Record session transcript here */ |
| 121 | 121 | const char *zLabel; /* Either "CS" or "SC" */ |
| 122 | + int atEof; /* True after connection closes */ |
| 122 | 123 | char *zErr; /* Error message */ |
| 124 | + Blob inbuf; /* Input buffer */ |
| 123 | 125 | }; |
| 124 | 126 | |
| 125 | 127 | /* Allowed values for SmtpSession.smtpFlags */ |
| 126 | 128 | #define SMTP_TRACE_STDOUT 0x00001 /* Debugging info to console */ |
| 127 | 129 | #define SMTP_TRACE_FILE 0x00002 /* Debugging info to logFile */ |
| | @@ -132,10 +134,11 @@ |
| 132 | 134 | /* |
| 133 | 135 | ** Shutdown an SmtpSession |
| 134 | 136 | */ |
| 135 | 137 | void smtp_session_free(SmtpSession *pSession){ |
| 136 | 138 | socket_close(); |
| 139 | + blob_zero(&pSession->inbuf); |
| 137 | 140 | fossil_free(pSession->zHostname); |
| 138 | 141 | fossil_free(pSession->zErr); |
| 139 | 142 | fossil_free(pSession); |
| 140 | 143 | } |
| 141 | 144 | |
| | @@ -159,27 +162,30 @@ |
| 159 | 162 | memset(p, 0, sizeof(*p)); |
| 160 | 163 | p->zFrom = zFrom; |
| 161 | 164 | p->zDest = zDest; |
| 162 | 165 | p->zLabel = zDest==0 ? "CS" : "SC"; |
| 163 | 166 | p->smtpFlags = smtpFlags; |
| 167 | + blob_init(&p->inbuf, 0, 0); |
| 164 | 168 | va_start(ap, smtpFlags); |
| 165 | 169 | if( smtpFlags & SMTP_TRACE_FILE ){ |
| 166 | 170 | p->logFile = va_arg(ap, FILE*); |
| 167 | 171 | }else if( smtpFlags & SMTP_TRACE_BLOB ){ |
| 168 | 172 | p->pTranscript = va_arg(ap, Blob*); |
| 169 | 173 | } |
| 170 | 174 | va_end(ap); |
| 171 | 175 | p->zHostname = smtp_mx_host(zDest); |
| 172 | 176 | if( p->zHostname==0 ){ |
| 177 | + p->atEof = 1; |
| 173 | 178 | p->zErr = mprintf("cannot locate SMTP server for \"%s\"", zDest); |
| 174 | 179 | return p; |
| 175 | 180 | } |
| 176 | 181 | memset(&url, 0, sizeof(url)); |
| 177 | 182 | url.name = p->zHostname; |
| 178 | 183 | url.port = 25; |
| 179 | 184 | socket_global_init(); |
| 180 | 185 | if( socket_open(&url) ){ |
| 186 | + p->atEof = 1; |
| 181 | 187 | p->zErr = socket_errmsg(); |
| 182 | 188 | socket_close(); |
| 183 | 189 | } |
| 184 | 190 | return p; |
| 185 | 191 | } |
| | @@ -190,10 +196,11 @@ |
| 190 | 196 | static void smtp_send_line(SmtpSession *p, const char *zFormat, ...){ |
| 191 | 197 | Blob b = empty_blob; |
| 192 | 198 | va_list ap; |
| 193 | 199 | char *z; |
| 194 | 200 | int n; |
| 201 | + if( p->atEof ) return; |
| 195 | 202 | va_start(ap, zFormat); |
| 196 | 203 | blob_vappendf(&b, zFormat, ap); |
| 197 | 204 | va_end(ap); |
| 198 | 205 | z = blob_buffer(&b); |
| 199 | 206 | n = blob_size(&b); |
| | @@ -212,27 +219,60 @@ |
| 212 | 219 | socket_send(0, z, n); |
| 213 | 220 | blob_zero(&b); |
| 214 | 221 | } |
| 215 | 222 | |
| 216 | 223 | /* |
| 217 | | -** Read a line of input received from the SMTP server. Append |
| 218 | | -** the received line onto the end of the blob. |
| 224 | +** Read a line of input received from the SMTP server. Make in point |
| 225 | +** to the next input line. |
| 226 | +** |
| 227 | +** Content is actually read into the p->in buffer. Then blob_line() |
| 228 | +** is used to extract individual lines, passing each to "in". |
| 219 | 229 | */ |
| 220 | 230 | static void smtp_recv_line(SmtpSession *p, Blob *in){ |
| 221 | | - int n = blob_size(in); |
| 222 | | - int iStart = n; |
| 223 | | - char *z; |
| 224 | | - do{ |
| 225 | | - size_t got; |
| 226 | | - blob_resize(in, n+1000); |
| 227 | | - z = blob_buffer(in); |
| 228 | | - got = socket_receive(0, z+n, 1000); |
| 229 | | - in->nUsed += got; |
| 230 | | - n += got; |
| 231 | | - }while( n<1 || z[n-1]!='\n' ); |
| 232 | | - z = blob_buffer(in) + iStart; |
| 233 | | - n = blob_size(in) - iStart - 1; |
| 231 | + int n = blob_size(&p->inbuf); |
| 232 | + char *z = blob_buffer(&p->inbuf); |
| 233 | + int i = blob_tell(&p->inbuf); |
| 234 | + int nDelay = 0; |
| 235 | + if( i<n && z[n-1]=='\n' ){ |
| 236 | + blob_line(&p->inbuf, in); |
| 237 | + }else if( p->atEof ){ |
| 238 | + blob_init(in, 0, 0); |
| 239 | + }else{ |
| 240 | + if( n>0 && i>=n ){ |
| 241 | + blob_truncate(&p->inbuf, 0); |
| 242 | + blob_rewind(&p->inbuf); |
| 243 | + n = 0; |
| 244 | + } |
| 245 | + do{ |
| 246 | + size_t got; |
| 247 | + blob_resize(&p->inbuf, n+1000); |
| 248 | + z = blob_buffer(&p->inbuf); |
| 249 | + got = socket_receive(0, z+n, 1000, 1); |
| 250 | + if( got>0 ){ |
| 251 | + in->nUsed += got; |
| 252 | + n += got; |
| 253 | + z[n] = 0; |
| 254 | + if( n>0 && z[n-1]=='\n' ) break; |
| 255 | + if( got==1000 ) continue; |
| 256 | + } |
| 257 | + nDelay++; |
| 258 | + if( nDelay>100 ){ |
| 259 | + blob_init(in, 0, 0); |
| 260 | + p->zErr = mprintf("timeout"); |
| 261 | + socket_close(); |
| 262 | + p->atEof = 1; |
| 263 | + return; |
| 264 | + }else{ |
| 265 | + sqlite3_sleep(25); |
| 266 | + } |
| 267 | + }while( n<1 || z[n-1]!='\n' ); |
| 268 | + blob_truncate(&p->inbuf, n); |
| 269 | + blob_line(&p->inbuf, in); |
| 270 | + } |
| 271 | + z = blob_buffer(in); |
| 272 | + n = blob_size(in); |
| 273 | + if( n && z[n-1]=='\n' ) n--; |
| 234 | 274 | if( n && z[n-1]=='\r' ) n--; |
| 235 | 275 | if( p->smtpFlags & SMTP_TRACE_STDOUT ){ |
| 236 | 276 | fossil_print("%c: %.*s\n", p->zLabel[0], n, z); |
| 237 | 277 | } |
| 238 | 278 | if( p->smtpFlags & SMTP_TRACE_FILE ){ |
| | @@ -251,15 +291,25 @@ |
| 251 | 291 | Blob *in, /* Buffer used to hold the reply */ |
| 252 | 292 | int *piCode, /* The return code */ |
| 253 | 293 | int *pbMore, /* True if the reply is not complete */ |
| 254 | 294 | char **pzArg /* Argument */ |
| 255 | 295 | ){ |
| 296 | + int n; |
| 297 | + char *z; |
| 256 | 298 | blob_truncate(in, 0); |
| 257 | 299 | smtp_recv_line(p, in); |
| 258 | | - *piCode = atoi(blob_str(in)); |
| 259 | | - *pbMore = blob_size(in)>=4 && blob_str(in)[3]=='-'; |
| 260 | | - *pzArg = blob_size(in)>=4 ? blob_str(in)+4 : ""; |
| 300 | + z = blob_str(in); |
| 301 | + n = blob_size(in); |
| 302 | + if( z[0]=='#' ){ |
| 303 | + *piCode = 0; |
| 304 | + *pbMore = 1; |
| 305 | + *pzArg = z; |
| 306 | + }else{ |
| 307 | + *piCode = atoi(z); |
| 308 | + *pbMore = n>=4 && z[3]=='-'; |
| 309 | + *pzArg = n>=4 ? z+4 : ""; |
| 310 | + } |
| 261 | 311 | } |
| 262 | 312 | |
| 263 | 313 | /* |
| 264 | 314 | ** Have the client send a QUIT message. |
| 265 | 315 | */ |
| | @@ -306,26 +356,31 @@ |
| 306 | 356 | } |
| 307 | 357 | |
| 308 | 358 | /* |
| 309 | 359 | ** COMMAND: test-smtp-probe |
| 310 | 360 | ** |
| 311 | | -** Usage: %fossil test-smtp-probe DOMAIN ME |
| 361 | +** Usage: %fossil test-smtp-probe DOMAIN [ME] |
| 312 | 362 | ** |
| 313 | 363 | ** Interact with the SMTP server for DOMAIN by setting up a connection |
| 314 | 364 | ** and then immediately shutting it back down. Log all interaction |
| 315 | 365 | ** on the console. Use ME as the domain name of the sender. |
| 316 | 366 | */ |
| 317 | 367 | void test_smtp_probe(void){ |
| 318 | | - char *zHost; |
| 319 | 368 | SmtpSession *p; |
| 320 | 369 | int rc; |
| 321 | | - if( g.argc!=4 ) usage("DOMAIN ME"); |
| 322 | | - zHost = smtp_mx_host(g.argv[2]); |
| 323 | | - if( zHost==0 ){ |
| 324 | | - fossil_fatal("cannot resolve the MX for \"%s\"", g.argv[2]); |
| 370 | + const char *zDomain; |
| 371 | + const char *zSelf; |
| 372 | + if( g.argc!=3 && g.argc!=4 ) usage("DOMAIN [ME]"); |
| 373 | + zDomain = g.argv[2]; |
| 374 | + zSelf = g.argc==4 ? g.argv[3] : "fossil-scm.org"; |
| 375 | + p = smtp_session_new(zSelf, zDomain, SMTP_TRACE_STDOUT); |
| 376 | + if( p->zErr ){ |
| 377 | + fossil_fatal("%s", p->zErr); |
| 325 | 378 | } |
| 326 | | - fossil_print("Contacting host \"%s\"\n", zHost); |
| 327 | | - p = smtp_session_new(g.argv[3], zHost, SMTP_TRACE_STDOUT); |
| 379 | + fossil_print("Connection to \"%s\"\n", p->zHostname); |
| 328 | 380 | rc = smtp_client_startup(p); |
| 329 | 381 | if( !rc ) smtp_client_quit(p); |
| 382 | + if( p->zErr ){ |
| 383 | + fossil_fatal("ERROR: %s\n", p->zErr); |
| 384 | + } |
| 330 | 385 | smtp_session_free(p); |
| 331 | 386 | } |
| 332 | 387 | |