imapext-2007
diff src/c-client/smtp.c @ 0:ada5e610ab86
imap-2007e
author | yuuji@gentei.org |
---|---|
date | Mon, 14 Sep 2009 15:17:45 +0900 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/c-client/smtp.c Mon Sep 14 15:17:45 2009 +0900 1.3 @@ -0,0 +1,793 @@ 1.4 +/* ======================================================================== 1.5 + * Copyright 1988-2008 University of Washington 1.6 + * 1.7 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.8 + * you may not use this file except in compliance with the License. 1.9 + * You may obtain a copy of the License at 1.10 + * 1.11 + * http://www.apache.org/licenses/LICENSE-2.0 1.12 + * 1.13 + * 1.14 + * ======================================================================== 1.15 + */ 1.16 + 1.17 +/* 1.18 + * Program: Simple Mail Transfer Protocol (SMTP) routines 1.19 + * 1.20 + * Author: Mark Crispin 1.21 + * Networks and Distributed Computing 1.22 + * Computing & Communications 1.23 + * University of Washington 1.24 + * Administration Building, AG-44 1.25 + * Seattle, WA 98195 1.26 + * Internet: MRC@CAC.Washington.EDU 1.27 + * 1.28 + * Date: 27 July 1988 1.29 + * Last Edited: 28 January 2008 1.30 + * 1.31 + * This original version of this file is 1.32 + * Copyright 1988 Stanford University 1.33 + * and was developed in the Symbolic Systems Resources Group of the Knowledge 1.34 + * Systems Laboratory at Stanford University in 1987-88, and was funded by the 1.35 + * Biomedical Research Technology Program of the National Institutes of Health 1.36 + * under grant number RR-00785. 1.37 + */ 1.38 + 1.39 + 1.40 +#include <ctype.h> 1.41 +#include <stdio.h> 1.42 +#include "c-client.h" 1.43 + 1.44 +/* Constants */ 1.45 + 1.46 +#define SMTPSSLPORT (long) 465 /* former assigned SSL TCP contact port */ 1.47 +#define SMTPGREET (long) 220 /* SMTP successful greeting */ 1.48 +#define SMTPAUTHED (long) 235 /* SMTP successful authentication */ 1.49 +#define SMTPOK (long) 250 /* SMTP OK code */ 1.50 +#define SMTPAUTHREADY (long) 334/* SMTP ready for authentication */ 1.51 +#define SMTPREADY (long) 354 /* SMTP ready for data */ 1.52 +#define SMTPSOFTFATAL (long) 421/* SMTP soft fatal code */ 1.53 +#define SMTPWANTAUTH (long) 505 /* SMTP authentication needed */ 1.54 +#define SMTPWANTAUTH2 (long) 530/* SMTP authentication needed */ 1.55 +#define SMTPUNAVAIL (long) 550 /* SMTP mailbox unavailable */ 1.56 +#define SMTPHARDERROR (long) 554/* SMTP miscellaneous hard failure */ 1.57 + 1.58 + 1.59 +/* Convenient access to protocol-specific data */ 1.60 + 1.61 +#define ESMTP stream->protocol.esmtp 1.62 + 1.63 + 1.64 +/* Function prototypes */ 1.65 + 1.66 +void *smtp_challenge (void *s,unsigned long *len); 1.67 +long smtp_response (void *s,char *response,unsigned long size); 1.68 +long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp); 1.69 +long smtp_rcpt (SENDSTREAM *stream,ADDRESS *adr,long *error); 1.70 +long smtp_send (SENDSTREAM *stream,char *command,char *args); 1.71 +long smtp_reply (SENDSTREAM *stream); 1.72 +long smtp_ehlo (SENDSTREAM *stream,char *host,NETMBX *mb); 1.73 +long smtp_fake (SENDSTREAM *stream,char *text); 1.74 +static long smtp_seterror (SENDSTREAM *stream,long code,char *text); 1.75 +long smtp_soutr (void *stream,char *s); 1.76 + 1.77 +/* Mailer parameters */ 1.78 + 1.79 +static unsigned long smtp_maxlogintrials = MAXLOGINTRIALS; 1.80 +static long smtp_port = 0; /* default port override */ 1.81 +static long smtp_sslport = 0; 1.82 + 1.83 + 1.84 +#ifndef RFC2821 1.85 +#define RFC2821 /* RFC 2821 compliance */ 1.86 +#endif 1.87 + 1.88 +/* SMTP limits, current as of RFC 2821 */ 1.89 + 1.90 +#define SMTPMAXLOCALPART 64 1.91 +#define SMTPMAXDOMAIN 255 1.92 +#define SMTPMAXPATH 256 1.93 + 1.94 + 1.95 +/* I have seen local parts of more than 64 octets, in spite of the SMTP 1.96 + * limits. So, we'll have a more generous limit that's still guaranteed 1.97 + * not to pop the buffer, and let the server worry about it. As of this 1.98 + * writing, it comes out to 240. Anyone with a mailbox name larger than 1.99 + * that is in serious need of a life or at least a new ISP! 23 June 1998 1.100 + */ 1.101 + 1.102 +#define MAXLOCALPART ((MAILTMPLEN - (SMTPMAXDOMAIN + SMTPMAXPATH + 32)) / 2) 1.103 + 1.104 +/* Mail Transfer Protocol manipulate driver parameters 1.105 + * Accepts: function code 1.106 + * function-dependent value 1.107 + * Returns: function-dependent return value 1.108 + */ 1.109 + 1.110 +void *smtp_parameters (long function,void *value) 1.111 +{ 1.112 + switch ((int) function) { 1.113 + case SET_MAXLOGINTRIALS: 1.114 + smtp_maxlogintrials = (unsigned long) value; 1.115 + break; 1.116 + case GET_MAXLOGINTRIALS: 1.117 + value = (void *) smtp_maxlogintrials; 1.118 + break; 1.119 + case SET_SMTPPORT: 1.120 + smtp_port = (long) value; 1.121 + break; 1.122 + case GET_SMTPPORT: 1.123 + value = (void *) smtp_port; 1.124 + break; 1.125 + case SET_SSLSMTPPORT: 1.126 + smtp_sslport = (long) value; 1.127 + break; 1.128 + case GET_SSLSMTPPORT: 1.129 + value = (void *) smtp_sslport; 1.130 + break; 1.131 + default: 1.132 + value = NIL; /* error case */ 1.133 + break; 1.134 + } 1.135 + return value; 1.136 +} 1.137 + 1.138 +/* Mail Transfer Protocol open connection 1.139 + * Accepts: network driver 1.140 + * service host list 1.141 + * port number 1.142 + * service name 1.143 + * SMTP open options 1.144 + * Returns: SEND stream on success, NIL on failure 1.145 + */ 1.146 + 1.147 +SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service, 1.148 + unsigned long port,long options) 1.149 +{ 1.150 + SENDSTREAM *stream = NIL; 1.151 + long reply; 1.152 + char *s,tmp[MAILTMPLEN]; 1.153 + NETSTREAM *netstream; 1.154 + NETMBX mb; 1.155 + if (!(hostlist && *hostlist)) mm_log ("Missing SMTP service host",ERROR); 1.156 + /* maximum domain name is 64 characters */ 1.157 + else do if (strlen (*hostlist) < SMTPMAXDOMAIN) { 1.158 + sprintf (tmp,"{%.1000s}",*hostlist); 1.159 + if (!mail_valid_net_parse_work (tmp,&mb,service ? service : "smtp") || 1.160 + mb.anoflag || mb.readonlyflag) { 1.161 + sprintf (tmp,"Invalid host specifier: %.80s",*hostlist); 1.162 + mm_log (tmp,ERROR); 1.163 + } 1.164 + else { /* light tryssl flag if requested */ 1.165 + mb.trysslflag = (options & SOP_TRYSSL) ? T : NIL; 1.166 + /* explicit port overrides all */ 1.167 + if (mb.port) port = mb.port; 1.168 + /* else /submit overrides port argument */ 1.169 + else if (!compare_cstring (mb.service,"submit")) { 1.170 + port = SUBMITTCPPORT; /* override port, use IANA name */ 1.171 + strcpy (mb.service,"submission"); 1.172 + } 1.173 + /* else port argument overrides SMTP port */ 1.174 + else if (!port) port = smtp_port ? smtp_port : SMTPTCPPORT; 1.175 + if (netstream = /* try to open ordinary connection */ 1.176 + net_open (&mb,dv,port, 1.177 + (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL), 1.178 + "*smtps",smtp_sslport ? smtp_sslport : SMTPSSLPORT)) { 1.179 + stream = (SENDSTREAM *) memset (fs_get (sizeof (SENDSTREAM)),0, 1.180 + sizeof (SENDSTREAM)); 1.181 + stream->netstream = netstream; 1.182 + stream->host = cpystr ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ? 1.183 + net_host (netstream) : mb.host); 1.184 + stream->debug = (mb.dbgflag || (options & OP_DEBUG)) ? T : NIL; 1.185 + if (options & SOP_SECURE) mb.secflag = T; 1.186 + /* get name of local host to use */ 1.187 + s = compare_cstring ("localhost",mb.host) ? 1.188 + net_localhost (netstream) : "localhost"; 1.189 + 1.190 + do reply = smtp_reply (stream); 1.191 + while ((reply < 100) || (stream->reply[3] == '-')); 1.192 + if (reply != SMTPGREET){/* get SMTP greeting */ 1.193 + sprintf (tmp,"SMTP greeting failure: %.80s",stream->reply); 1.194 + mm_log (tmp,ERROR); 1.195 + stream = smtp_close (stream); 1.196 + } 1.197 + /* try EHLO first, then HELO */ 1.198 + else if (((reply = smtp_ehlo (stream,s,&mb)) != SMTPOK) && 1.199 + ((reply = smtp_send (stream,"HELO",s)) != SMTPOK)) { 1.200 + sprintf (tmp,"SMTP hello failure: %.80s",stream->reply); 1.201 + mm_log (tmp,ERROR); 1.202 + stream = smtp_close (stream); 1.203 + } 1.204 + else { 1.205 + NETDRIVER *ssld =(NETDRIVER *)mail_parameters(NIL,GET_SSLDRIVER,NIL); 1.206 + sslstart_t stls = (sslstart_t) mail_parameters(NIL,GET_SSLSTART,NIL); 1.207 + ESMTP.ok = T; /* ESMTP server, start TLS if present */ 1.208 + if (!dv && stls && ESMTP.service.starttls && 1.209 + !mb.sslflag && !mb.notlsflag && 1.210 + (smtp_send (stream,"STARTTLS",NIL) == SMTPGREET)) { 1.211 + mb.tlsflag = T; /* TLS OK, get into TLS at this end */ 1.212 + stream->netstream->dtb = ssld; 1.213 + /* TLS started, negotiate it */ 1.214 + if (!(stream->netstream->stream = (*stls) 1.215 + (stream->netstream->stream,mb.host, 1.216 + (mb.tlssslv23 ? NIL : NET_TLSCLIENT) | 1.217 + (mb.novalidate ? NET_NOVALIDATECERT:NIL)))){ 1.218 + /* TLS negotiation failed after STARTTLS */ 1.219 + sprintf (tmp,"Unable to negotiate TLS with this server: %.80s", 1.220 + mb.host); 1.221 + mm_log (tmp,ERROR); 1.222 + /* close without doing QUIT */ 1.223 + if (stream->netstream) net_close (stream->netstream); 1.224 + stream->netstream = NIL; 1.225 + stream = smtp_close (stream); 1.226 + } 1.227 + /* TLS OK, re-negotiate EHLO */ 1.228 + else if ((reply = smtp_ehlo (stream,s,&mb)) != SMTPOK) { 1.229 + sprintf (tmp,"SMTP EHLO failure after STARTTLS: %.80s", 1.230 + stream->reply); 1.231 + mm_log (tmp,ERROR); 1.232 + stream = smtp_close (stream); 1.233 + } 1.234 + else ESMTP.ok = T; /* TLS OK and EHLO successful */ 1.235 + } 1.236 + else if (mb.tlsflag) {/* user specified /tls but can't do it */ 1.237 + sprintf (tmp,"TLS unavailable with this server: %.80s",mb.host); 1.238 + mm_log (tmp,ERROR); 1.239 + stream = smtp_close (stream); 1.240 + } 1.241 + 1.242 + /* remote name for authentication */ 1.243 + if (stream && ((mb.secflag || mb.user[0]))) { 1.244 + if (ESMTP.auth) { /* use authenticator? */ 1.245 + if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) { 1.246 + /* remote name for authentication */ 1.247 + strncpy (mb.host, 1.248 + (long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ? 1.249 + net_remotehost (netstream) : net_host (netstream), 1.250 + NETMAXHOST-1); 1.251 + mb.host[NETMAXHOST-1] = '\0'; 1.252 + } 1.253 + if (!smtp_auth (stream,&mb,tmp)) stream = smtp_close (stream); 1.254 + } 1.255 + else { /* no available authenticators? */ 1.256 + sprintf (tmp,"%sSMTP authentication not available: %.80s", 1.257 + mb.secflag ? "Secure " : "",mb.host); 1.258 + mm_log (tmp,ERROR); 1.259 + stream = smtp_close (stream); 1.260 + } 1.261 + } 1.262 + } 1.263 + } 1.264 + } 1.265 + } while (!stream && *++hostlist); 1.266 + if (stream) { /* set stream options if have a stream */ 1.267 + if (options &(SOP_DSN | SOP_DSN_NOTIFY_FAILURE | SOP_DSN_NOTIFY_DELAY | 1.268 + SOP_DSN_NOTIFY_SUCCESS | SOP_DSN_RETURN_FULL)) { 1.269 + ESMTP.dsn.want = T; 1.270 + if (options & SOP_DSN_NOTIFY_FAILURE) ESMTP.dsn.notify.failure = T; 1.271 + if (options & SOP_DSN_NOTIFY_DELAY) ESMTP.dsn.notify.delay = T; 1.272 + if (options & SOP_DSN_NOTIFY_SUCCESS) ESMTP.dsn.notify.success = T; 1.273 + if (options & SOP_DSN_RETURN_FULL) ESMTP.dsn.full = T; 1.274 + } 1.275 + if (options & SOP_8BITMIME) ESMTP.eightbit.want = T; 1.276 + } 1.277 + return stream; 1.278 +} 1.279 + 1.280 +/* SMTP authenticate 1.281 + * Accepts: stream to login 1.282 + * parsed network mailbox structure 1.283 + * scratch buffer 1.284 + * place to return user name 1.285 + * Returns: T on success, NIL on failure 1.286 + */ 1.287 + 1.288 +long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp) 1.289 +{ 1.290 + unsigned long trial,auths; 1.291 + char *lsterr = NIL; 1.292 + char usr[MAILTMPLEN]; 1.293 + AUTHENTICATOR *at; 1.294 + long ret = NIL; 1.295 + for (auths = ESMTP.auth, stream->saslcancel = NIL; 1.296 + !ret && stream->netstream && auths && 1.297 + (at = mail_lookup_auth (find_rightmost_bit (&auths) + 1)); ) { 1.298 + if (lsterr) { /* previous authenticator failed? */ 1.299 + sprintf (tmp,"Retrying using %s authentication after %.80s", 1.300 + at->name,lsterr); 1.301 + mm_log (tmp,NIL); 1.302 + fs_give ((void **) &lsterr); 1.303 + } 1.304 + trial = 0; /* initial trial count */ 1.305 + tmp[0] = '\0'; /* empty buffer */ 1.306 + if (stream->netstream) do { 1.307 + if (lsterr) { 1.308 + sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr); 1.309 + mm_log (tmp,WARN); 1.310 + fs_give ((void **) &lsterr); 1.311 + } 1.312 + stream->saslcancel = NIL; 1.313 + if (smtp_send (stream,"AUTH",at->name) == SMTPAUTHREADY) { 1.314 + /* hide client authentication responses */ 1.315 + if (!(at->flags & AU_SECURE)) stream->sensitive = T; 1.316 + if ((*at->client) (smtp_challenge,smtp_response,"smtp",mb,stream, 1.317 + &trial,usr)) { 1.318 + if (stream->replycode == SMTPAUTHED) { 1.319 + ESMTP.auth = NIL; /* disable authenticators */ 1.320 + ret = LONGT; 1.321 + } 1.322 + /* if main program requested cancellation */ 1.323 + else if (!trial) mm_log ("SMTP Authentication cancelled",ERROR); 1.324 + } 1.325 + stream->sensitive = NIL;/* unhide */ 1.326 + } 1.327 + /* remember response if error and no cancel */ 1.328 + if (!ret && trial) lsterr = cpystr (stream->reply); 1.329 + } while (!ret && stream->netstream && trial && 1.330 + (trial < smtp_maxlogintrials)); 1.331 + } 1.332 + if (lsterr) { /* previous authenticator failed? */ 1.333 + if (!stream->saslcancel) { /* don't do this if a cancel */ 1.334 + sprintf (tmp,"Can not authenticate to SMTP server: %.80s",lsterr); 1.335 + mm_log (tmp,ERROR); 1.336 + } 1.337 + fs_give ((void **) &lsterr); 1.338 + } 1.339 + return ret; /* authentication failed */ 1.340 +} 1.341 + 1.342 +/* Get challenge to authenticator in binary 1.343 + * Accepts: stream 1.344 + * pointer to returned size 1.345 + * Returns: challenge or NIL if not challenge 1.346 + */ 1.347 + 1.348 +void *smtp_challenge (void *s,unsigned long *len) 1.349 +{ 1.350 + char tmp[MAILTMPLEN]; 1.351 + void *ret = NIL; 1.352 + SENDSTREAM *stream = (SENDSTREAM *) s; 1.353 + if ((stream->replycode == SMTPAUTHREADY) && 1.354 + !(ret = rfc822_base64 ((unsigned char *) stream->reply + 4, 1.355 + strlen (stream->reply + 4),len))) { 1.356 + sprintf (tmp,"SMTP SERVER BUG (invalid challenge): %.80s",stream->reply+4); 1.357 + mm_log (tmp,ERROR); 1.358 + } 1.359 + return ret; 1.360 +} 1.361 + 1.362 + 1.363 +/* Send authenticator response in BASE64 1.364 + * Accepts: MAIL stream 1.365 + * string to send 1.366 + * length of string 1.367 + * Returns: T, always 1.368 + */ 1.369 + 1.370 +long smtp_response (void *s,char *response,unsigned long size) 1.371 +{ 1.372 + SENDSTREAM *stream = (SENDSTREAM *) s; 1.373 + unsigned long i,j; 1.374 + char *t,*u; 1.375 + if (response) { /* make CRLFless BASE64 string */ 1.376 + if (size) { 1.377 + for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0; 1.378 + j < i; j++) if (t[j] > ' ') *u++ = t[j]; 1.379 + *u = '\0'; /* tie off string */ 1.380 + i = smtp_send (stream,t,NIL); 1.381 + fs_give ((void **) &t); 1.382 + } 1.383 + else i = smtp_send (stream,"",NIL); 1.384 + } 1.385 + else { /* abort requested */ 1.386 + i = smtp_send (stream,"*",NIL); 1.387 + stream->saslcancel = T; /* mark protocol-requested SASL cancel */ 1.388 + } 1.389 + return LONGT; 1.390 +} 1.391 + 1.392 +/* Mail Transfer Protocol close connection 1.393 + * Accepts: SEND stream 1.394 + * Returns: NIL always 1.395 + */ 1.396 + 1.397 +SENDSTREAM *smtp_close (SENDSTREAM *stream) 1.398 +{ 1.399 + if (stream) { /* send "QUIT" */ 1.400 + if (stream->netstream) { /* do close actions if have netstream */ 1.401 + smtp_send (stream,"QUIT",NIL); 1.402 + if (stream->netstream) /* could have been closed during "QUIT" */ 1.403 + net_close (stream->netstream); 1.404 + } 1.405 + /* clean up */ 1.406 + if (stream->host) fs_give ((void **) &stream->host); 1.407 + if (stream->reply) fs_give ((void **) &stream->reply); 1.408 + if (ESMTP.dsn.envid) fs_give ((void **) &ESMTP.dsn.envid); 1.409 + if (ESMTP.atrn.domains) fs_give ((void **) &ESMTP.atrn.domains); 1.410 + fs_give ((void **) &stream);/* flush the stream */ 1.411 + } 1.412 + return NIL; 1.413 +} 1.414 + 1.415 +/* Mail Transfer Protocol deliver mail 1.416 + * Accepts: SEND stream 1.417 + * delivery option (MAIL, SEND, SAML, SOML) 1.418 + * message envelope 1.419 + * message body 1.420 + * Returns: T on success, NIL on failure 1.421 + */ 1.422 + 1.423 +long smtp_mail (SENDSTREAM *stream,char *type,ENVELOPE *env,BODY *body) 1.424 +{ 1.425 + RFC822BUFFER buf; 1.426 + char tmp[SENDBUFLEN+1]; 1.427 + long error = NIL; 1.428 + long retry = NIL; 1.429 + buf.f = smtp_soutr; /* initialize buffer */ 1.430 + buf.s = stream->netstream; 1.431 + buf.end = (buf.beg = buf.cur = tmp) + SENDBUFLEN; 1.432 + tmp[SENDBUFLEN] = '\0'; /* must have additional null guard byte */ 1.433 + if (!(env->to || env->cc || env->bcc)) { 1.434 + /* no recipients in request */ 1.435 + smtp_seterror (stream,SMTPHARDERROR,"No recipients specified"); 1.436 + return NIL; 1.437 + } 1.438 + do { /* make sure stream is in good shape */ 1.439 + smtp_send (stream,"RSET",NIL); 1.440 + if (retry) { /* need to retry with authentication? */ 1.441 + NETMBX mb; 1.442 + /* yes, build remote name for authentication */ 1.443 + sprintf (tmp,"{%.200s/smtp%s}<none>", 1.444 + (long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ? 1.445 + ((long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ? 1.446 + net_remotehost (stream->netstream) : 1.447 + net_host (stream->netstream)) : 1.448 + stream->host, 1.449 + (stream->netstream->dtb == 1.450 + (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL)) ? 1.451 + "/ssl" : ""); 1.452 + mail_valid_net_parse (tmp,&mb); 1.453 + if (!smtp_auth (stream,&mb,tmp)) return NIL; 1.454 + retry = NIL; /* no retry at this point */ 1.455 + } 1.456 + 1.457 + strcpy (tmp,"FROM:<"); /* compose "MAIL FROM:<return-path>" */ 1.458 +#ifdef RFC2821 1.459 + if (env->return_path && env->return_path->host && 1.460 + !((strlen (env->return_path->mailbox) > SMTPMAXLOCALPART) || 1.461 + (strlen (env->return_path->host) > SMTPMAXDOMAIN))) { 1.462 + rfc822_cat (tmp,env->return_path->mailbox,NIL); 1.463 + sprintf (tmp + strlen (tmp),"@%s",env->return_path->host); 1.464 + } 1.465 +#else /* old code with A-D-L support */ 1.466 + if (env->return_path && env->return_path->host && 1.467 + !((env->return_path->adl && 1.468 + (strlen (env->return_path->adl) > SMTPMAXPATH)) || 1.469 + (strlen (env->return_path->mailbox) > SMTPMAXLOCALPART) || 1.470 + (strlen (env->return_path->host) > SMTPMAXDOMAIN))) 1.471 + rfc822_address (tmp,env->return_path); 1.472 +#endif 1.473 + strcat (tmp,">"); 1.474 + if (ESMTP.ok) { 1.475 + if (ESMTP.eightbit.ok && ESMTP.eightbit.want) 1.476 + strcat (tmp," BODY=8BITMIME"); 1.477 + if (ESMTP.dsn.ok && ESMTP.dsn.want) { 1.478 + strcat (tmp,ESMTP.dsn.full ? " RET=FULL" : " RET=HDRS"); 1.479 + if (ESMTP.dsn.envid) 1.480 + sprintf (tmp + strlen (tmp)," ENVID=%.100s",ESMTP.dsn.envid); 1.481 + } 1.482 + } 1.483 + /* send "MAIL FROM" command */ 1.484 + switch (smtp_send (stream,type,tmp)) { 1.485 + case SMTPUNAVAIL: /* mailbox unavailable? */ 1.486 + case SMTPWANTAUTH: /* wants authentication? */ 1.487 + case SMTPWANTAUTH2: 1.488 + if (ESMTP.auth) retry = T;/* yes, retry with authentication */ 1.489 + case SMTPOK: /* looks good */ 1.490 + break; 1.491 + default: /* other failure */ 1.492 + return NIL; 1.493 + } 1.494 + /* negotiate the recipients */ 1.495 + if (!retry && env->to) retry = smtp_rcpt (stream,env->to,&error); 1.496 + if (!retry && env->cc) retry = smtp_rcpt (stream,env->cc,&error); 1.497 + if (!retry && env->bcc) retry = smtp_rcpt (stream,env->bcc,&error); 1.498 + if (!retry && error) { /* any recipients failed? */ 1.499 + smtp_send (stream,"RSET",NIL); 1.500 + smtp_seterror (stream,SMTPHARDERROR,"One or more recipients failed"); 1.501 + return NIL; 1.502 + } 1.503 + } while (retry); 1.504 + /* negotiate data command */ 1.505 + if (!(smtp_send (stream,"DATA",NIL) == SMTPREADY)) return NIL; 1.506 + /* send message data */ 1.507 + if (!rfc822_output_full (&buf,env,body, 1.508 + ESMTP.eightbit.ok && ESMTP.eightbit.want)) { 1.509 + smtp_fake (stream,"SMTP connection broken (message data)"); 1.510 + return NIL; /* can't do much else here */ 1.511 + } 1.512 + /* send trailing dot */ 1.513 + return (smtp_send (stream,".",NIL) == SMTPOK) ? LONGT : NIL; 1.514 +} 1.515 + 1.516 +/* Simple Mail Transfer Protocol send VERBose 1.517 + * Accepts: SMTP stream 1.518 + * Returns: T if successful, else NIL 1.519 + * 1.520 + * Descriptive text formerly in [al]pine sources: 1.521 + * At worst, this command may cause the SMTP connection to get nuked. Modern 1.522 + * sendmail's recognize it, and possibly other SMTP implementations (the "ON" 1.523 + * arg is for PMDF). What's more, if it works, the reply code and accompanying 1.524 + * text may vary from server to server. 1.525 + */ 1.526 + 1.527 +long smtp_verbose (SENDSTREAM *stream) 1.528 +{ 1.529 + /* accept any 2xx reply code */ 1.530 + return ((smtp_send (stream,"VERB","ON") / (long) 100) == 2) ? LONGT : NIL; 1.531 +} 1.532 + 1.533 +/* Internal routines */ 1.534 + 1.535 + 1.536 +/* Simple Mail Transfer Protocol send recipient 1.537 + * Accepts: SMTP stream 1.538 + * address list 1.539 + * pointer to error flag 1.540 + * Returns: T if should retry, else NIL 1.541 + */ 1.542 + 1.543 +long smtp_rcpt (SENDSTREAM *stream,ADDRESS *adr,long *error) 1.544 +{ 1.545 + char *s,tmp[2*MAILTMPLEN],orcpt[MAILTMPLEN]; 1.546 + while (adr) { /* for each address on the list */ 1.547 + /* clear any former error */ 1.548 + if (adr->error) fs_give ((void **) &adr->error); 1.549 + if (adr->host) { /* ignore group syntax */ 1.550 + /* enforce SMTP limits to protect the buffer */ 1.551 + if (strlen (adr->mailbox) > MAXLOCALPART) { 1.552 + adr->error = cpystr ("501 Recipient name too long"); 1.553 + *error = T; 1.554 + } 1.555 + else if ((strlen (adr->host) > SMTPMAXDOMAIN)) { 1.556 + adr->error = cpystr ("501 Recipient domain too long"); 1.557 + *error = T; 1.558 + } 1.559 +#ifndef RFC2821 /* old code with A-D-L support */ 1.560 + else if (adr->adl && (strlen (adr->adl) > SMTPMAXPATH)) { 1.561 + adr->error = cpystr ("501 Path too long"); 1.562 + *error = T; 1.563 + } 1.564 +#endif 1.565 + 1.566 + else { 1.567 + strcpy (tmp,"TO:<"); /* compose "RCPT TO:<return-path>" */ 1.568 +#ifdef RFC2821 1.569 + rfc822_cat (tmp,adr->mailbox,NIL); 1.570 + sprintf (tmp + strlen (tmp),"@%s>",adr->host); 1.571 +#else /* old code with A-D-L support */ 1.572 + rfc822_address (tmp,adr); 1.573 + strcat (tmp,">"); 1.574 +#endif 1.575 + /* want notifications */ 1.576 + if (ESMTP.ok && ESMTP.dsn.ok && ESMTP.dsn.want) { 1.577 + /* yes, start with prefix */ 1.578 + strcat (tmp," NOTIFY="); 1.579 + s = tmp + strlen (tmp); 1.580 + if (ESMTP.dsn.notify.failure) strcat (s,"FAILURE,"); 1.581 + if (ESMTP.dsn.notify.delay) strcat (s,"DELAY,"); 1.582 + if (ESMTP.dsn.notify.success) strcat (s,"SUCCESS,"); 1.583 + /* tie off last comma */ 1.584 + if (*s) s[strlen (s) - 1] = '\0'; 1.585 + else strcat (tmp,"NEVER"); 1.586 + if (adr->orcpt.addr) { 1.587 + sprintf (orcpt,"%.498s;%.498s", 1.588 + adr->orcpt.type ? adr->orcpt.type : "rfc822", 1.589 + adr->orcpt.addr); 1.590 + sprintf (tmp + strlen (tmp)," ORCPT=%.500s",orcpt); 1.591 + } 1.592 + } 1.593 + switch (smtp_send (stream,"RCPT",tmp)) { 1.594 + case SMTPOK: /* looks good */ 1.595 + break; 1.596 + case SMTPUNAVAIL: /* mailbox unavailable? */ 1.597 + case SMTPWANTAUTH: /* wants authentication? */ 1.598 + case SMTPWANTAUTH2: 1.599 + if (ESMTP.auth) return T; 1.600 + default: /* other failure */ 1.601 + *error = T; /* note that an error occurred */ 1.602 + adr->error = cpystr (stream->reply); 1.603 + } 1.604 + } 1.605 + } 1.606 + adr = adr->next; /* do any subsequent recipients */ 1.607 + } 1.608 + return NIL; /* no retry called for */ 1.609 +} 1.610 + 1.611 +/* Simple Mail Transfer Protocol send command 1.612 + * Accepts: SEND stream 1.613 + * text 1.614 + * Returns: reply code 1.615 + */ 1.616 + 1.617 +long smtp_send (SENDSTREAM *stream,char *command,char *args) 1.618 +{ 1.619 + long ret; 1.620 + char *s = (char *) fs_get (strlen (command) + (args ? strlen (args) + 1 : 0) 1.621 + + 3); 1.622 + /* build the complete command */ 1.623 + if (args) sprintf (s,"%s %s",command,args); 1.624 + else strcpy (s,command); 1.625 + if (stream->debug) mail_dlog (s,stream->sensitive); 1.626 + strcat (s,"\015\012"); 1.627 + /* send the command */ 1.628 + if (stream->netstream && net_soutr (stream->netstream,s)) { 1.629 + do stream->replycode = smtp_reply (stream); 1.630 + while ((stream->replycode < 100) || (stream->reply[3] == '-')); 1.631 + ret = stream->replycode; 1.632 + } 1.633 + else ret = smtp_fake (stream,"SMTP connection broken (command)"); 1.634 + fs_give ((void **) &s); 1.635 + return ret; 1.636 +} 1.637 + 1.638 + 1.639 +/* Simple Mail Transfer Protocol get reply 1.640 + * Accepts: SMTP stream 1.641 + * Returns: reply code 1.642 + */ 1.643 + 1.644 +long smtp_reply (SENDSTREAM *stream) 1.645 +{ 1.646 + smtpverbose_t pv = (smtpverbose_t) mail_parameters (NIL,GET_SMTPVERBOSE,NIL); 1.647 + long reply; 1.648 + /* flush old reply */ 1.649 + if (stream->reply) fs_give ((void **) &stream->reply); 1.650 + /* get reply */ 1.651 + if (stream->netstream && (stream->reply = net_getline (stream->netstream))) { 1.652 + if (stream->debug) mm_dlog (stream->reply); 1.653 + /* return response code */ 1.654 + reply = atol (stream->reply); 1.655 + if (pv && (reply < 100)) (*pv) (stream->reply); 1.656 + } 1.657 + else reply = smtp_fake (stream,"SMTP connection broken (reply)"); 1.658 + return reply; 1.659 +} 1.660 + 1.661 +/* Simple Mail Transfer Protocol send EHLO 1.662 + * Accepts: SMTP stream 1.663 + * host name to use in EHLO 1.664 + * NETMBX structure 1.665 + * Returns: reply code 1.666 + */ 1.667 + 1.668 +long smtp_ehlo (SENDSTREAM *stream,char *host,NETMBX *mb) 1.669 +{ 1.670 + unsigned long i,j; 1.671 + long flags = (mb->secflag ? AU_SECURE : NIL) | 1.672 + (mb->authuser[0] ? AU_AUTHUSER : NIL); 1.673 + char *s,*t,*r,tmp[MAILTMPLEN]; 1.674 + /* clear ESMTP data */ 1.675 + memset (&ESMTP,0,sizeof (ESMTP)); 1.676 + if (mb->loser) return 500; /* never do EHLO if a loser */ 1.677 + sprintf (tmp,"EHLO %s",host); /* build the complete command */ 1.678 + if (stream->debug) mm_dlog (tmp); 1.679 + strcat (tmp,"\015\012"); 1.680 + /* send the command */ 1.681 + if (!net_soutr (stream->netstream,tmp)) 1.682 + return smtp_fake (stream,"SMTP connection broken (EHLO)"); 1.683 + /* got an OK reply? */ 1.684 + do if ((i = smtp_reply (stream)) == SMTPOK) { 1.685 + /* hack for AUTH= */ 1.686 + if (stream->reply[4] && stream->reply[5] && stream->reply[6] && 1.687 + stream->reply[7] && (stream->reply[8] == '=')) stream->reply[8] = ' '; 1.688 + /* get option code */ 1.689 + if (!(s = strtok_r (stream->reply+4," ",&r))); 1.690 + /* have option, does it have a value */ 1.691 + else if ((t = strtok_r (NIL," ",&r)) && *t) { 1.692 + /* EHLO options which take arguments */ 1.693 + if (!compare_cstring (s,"SIZE")) { 1.694 + if (isdigit (*t)) ESMTP.size.limit = strtoul (t,&t,10); 1.695 + ESMTP.size.ok = T; 1.696 + } 1.697 + else if (!compare_cstring (s,"DELIVERBY")) { 1.698 + if (isdigit (*t)) ESMTP.deliverby.minby = strtoul (t,&t,10); 1.699 + ESMTP.deliverby.ok = T; 1.700 + } 1.701 + else if (!compare_cstring (s,"ATRN")) { 1.702 + ESMTP.atrn.domains = cpystr (t); 1.703 + ESMTP.atrn.ok = T; 1.704 + } 1.705 + else if (!compare_cstring (s,"AUTH")) 1.706 + do if ((j = mail_lookup_auth_name (t,flags)) && 1.707 + (--j < MAXAUTHENTICATORS)) ESMTP.auth |= (1 << j); 1.708 + while ((t = strtok_r (NIL," ",&r)) && *t); 1.709 + } 1.710 + /* EHLO options which do not take arguments */ 1.711 + else if (!compare_cstring (s,"SIZE")) ESMTP.size.ok = T; 1.712 + else if (!compare_cstring (s,"8BITMIME")) ESMTP.eightbit.ok = T; 1.713 + else if (!compare_cstring (s,"DSN")) ESMTP.dsn.ok = T; 1.714 + else if (!compare_cstring (s,"ATRN")) ESMTP.atrn.ok = T; 1.715 + else if (!compare_cstring (s,"SEND")) ESMTP.service.send = T; 1.716 + else if (!compare_cstring (s,"SOML")) ESMTP.service.soml = T; 1.717 + else if (!compare_cstring (s,"SAML")) ESMTP.service.saml = T; 1.718 + else if (!compare_cstring (s,"EXPN")) ESMTP.service.expn = T; 1.719 + else if (!compare_cstring (s,"HELP")) ESMTP.service.help = T; 1.720 + else if (!compare_cstring (s,"TURN")) ESMTP.service.turn = T; 1.721 + else if (!compare_cstring (s,"ETRN")) ESMTP.service.etrn = T; 1.722 + else if (!compare_cstring (s,"STARTTLS")) ESMTP.service.starttls = T; 1.723 + else if (!compare_cstring (s,"RELAY")) ESMTP.service.relay = T; 1.724 + else if (!compare_cstring (s,"PIPELINING")) ESMTP.service.pipe = T; 1.725 + else if (!compare_cstring (s,"ENHANCEDSTATUSCODES")) 1.726 + ESMTP.service.ensc = T; 1.727 + else if (!compare_cstring (s,"BINARYMIME")) ESMTP.service.bmime = T; 1.728 + else if (!compare_cstring (s,"CHUNKING")) ESMTP.service.chunk = T; 1.729 + } 1.730 + while ((i < 100) || (stream->reply[3] == '-')); 1.731 + /* disable LOGIN if PLAIN also advertised */ 1.732 + if ((j = mail_lookup_auth_name ("PLAIN",NIL)) && (--j < MAXAUTHENTICATORS) && 1.733 + (ESMTP.auth & (1 << j)) && 1.734 + (j = mail_lookup_auth_name ("LOGIN",NIL)) && (--j < MAXAUTHENTICATORS)) 1.735 + ESMTP.auth &= ~(1 << j); 1.736 + return i; /* return the response code */ 1.737 +} 1.738 + 1.739 +/* Simple Mail Transfer Protocol set fake error and abort 1.740 + * Accepts: SMTP stream 1.741 + * error text 1.742 + * Returns: SMTPSOFTFATAL, always 1.743 + */ 1.744 + 1.745 +long smtp_fake (SENDSTREAM *stream,char *text) 1.746 +{ 1.747 + if (stream->netstream) { /* close net connection if still open */ 1.748 + net_close (stream->netstream); 1.749 + stream->netstream = NIL; 1.750 + } 1.751 + /* set last error */ 1.752 + return smtp_seterror (stream,SMTPSOFTFATAL,text); 1.753 +} 1.754 + 1.755 + 1.756 +/* Simple Mail Transfer Protocol set error 1.757 + * Accepts: SMTP stream 1.758 + * SMTP error code 1.759 + * error text 1.760 + * Returns: error code 1.761 + */ 1.762 + 1.763 +static long smtp_seterror (SENDSTREAM *stream,long code,char *text) 1.764 +{ 1.765 + /* flush any old reply */ 1.766 + if (stream->reply ) fs_give ((void **) &stream->reply); 1.767 + /* set up pseudo-reply string */ 1.768 + stream->reply = (char *) fs_get (20+strlen (text)); 1.769 + sprintf (stream->reply,"%ld %s",code,text); 1.770 + return code; /* return error code */ 1.771 +} 1.772 + 1.773 + 1.774 +/* Simple Mail Transfer Protocol filter mail 1.775 + * Accepts: stream 1.776 + * string 1.777 + * Returns: T on success, NIL on failure 1.778 + */ 1.779 + 1.780 +long smtp_soutr (void *stream,char *s) 1.781 +{ 1.782 + char c,*t; 1.783 + /* "." on first line */ 1.784 + if (s[0] == '.') net_sout (stream,".",1); 1.785 + /* find lines beginning with a "." */ 1.786 + while (t = strstr (s,"\015\012.")) { 1.787 + c = *(t += 3); /* remember next character after "." */ 1.788 + *t = '\0'; /* tie off string */ 1.789 + /* output prefix */ 1.790 + if (!net_sout (stream,s,t-s)) return NIL; 1.791 + *t = c; /* restore delimiter */ 1.792 + s = t - 1; /* push pointer up to the "." */ 1.793 + } 1.794 + /* output remainder of text */ 1.795 + return *s ? net_soutr (stream,s) : T; 1.796 +}