2 * smtp.c -- nmh SMTP interface
9 #include <zotnet/mts/mts.h>
11 #include <h/signals.h>
17 * This module implements an interface to SendMail very similar
18 * to the MMDF mm_(3) routines. The sm_() routines herein talk
19 * SMTP to a sendmail process, mapping SMTP reply codes into
24 * On older 4.2BSD machines without the POSIX function `sigaction',
25 * the alarm handing stuff for time-outs will NOT work due to the way
26 * syscalls get restarted. This is not really crucial, since SendMail
27 * is generally well-behaved in this area.
32 * It appears that some versions of Sendmail will return Code 451
33 * when they don't really want to indicate a failure.
34 * "Code 451 almost always means sendmail has deferred; we don't
35 * really want bomb out at this point since sendmail will rectify
36 * things later." So, if you define SENDMAILBUG, Code 451 is
37 * considered the same as Code 250. Yuck!
44 #define NBITS ((sizeof (int)) * 8)
47 * these codes must all be different!
49 #define SM_OPEN 90 /* Changed from 30 in case of nameserver flakiness */
60 static int sm_addrs = 0;
61 static int sm_alarmed = 0;
62 static int sm_child = NOTOK;
63 static int sm_debug = 0;
64 static int sm_nl = TRUE;
65 static int sm_verbose = 0;
67 static FILE *sm_rfp = NULL;
68 static FILE *sm_wfp = NULL;
71 static int sm_ispool = 0;
72 static char sm_tmpfil[BUFSIZ];
75 static char *sm_noreply = "No reply text given";
76 static char *sm_moreply = "; ";
78 struct smtp sm_reply; /* global... */
83 char *EHLOkeys[MAXEHLO + 1];
88 static int smtp_init (char *, char *, int, int, int, int, int);
89 static int sendmail_init (char *, char *, int, int, int, int, int);
91 static int rclient (char *, char *, char *);
92 static int sm_ierror (char *fmt, ...);
93 static int smtalk (int time, char *fmt, ...);
94 static int sm_wrecord (char *, int);
95 static int sm_wstream (char *, int);
96 static int sm_werror (void);
97 static int smhear (void);
98 static int sm_rrecord (char *, int *);
99 static int sm_rerror (void);
100 static RETSIGTYPE alrmser (int);
101 static char *EHLOset (char *);
105 * smtp.c's own static copy of several nmh library subroutines
107 static char **smail_brkstring (char *, char *, char *);
108 static int smail_brkany (char, char *);
109 char **smail_copyip (char **, char **, int);
112 /* from zotnet/mts/client.c */
113 int client (char *, char *, char *, int, char *, int);
116 sm_init (char *client, char *server, int watch, int verbose,
117 int debug, int onex, int queued)
119 if (sm_mts == MTS_SMTP)
120 return smtp_init (client, server, watch, verbose,
121 debug, onex, queued);
123 return sendmail_init (client, server, watch, verbose,
124 debug, onex, queued);
128 smtp_init (char *client, char *server, int watch, int verbose,
129 int debug, int onex, int queued)
131 int result, sd1, sd2;
136 sm_verbose = verbose;
144 if (sm_rfp != NULL && sm_wfp != NULL)
147 if (client == NULL || *client == '\0') {
151 client = LocalName(); /* no clientname -> LocalName */
156 if (client == NULL || *client == '\0')
157 client = "localhost";
160 if ((sd1 = rclient (server, "tcp", "smtp")) == NOTOK)
171 if ((sm_wfp = fdopen (sd1, "w")) == NULL) {
174 return sm_ierror ("unable to fdopen");
177 sm_reply.text[sm_reply.length = 0] = NULL;
178 return (sm_reply.code = RP_OK);
182 if ((sd2 = dup (sd1)) == NOTOK) {
184 return sm_ierror ("unable to dup");
187 SIGNAL (SIGALRM, alrmser);
188 SIGNAL (SIGPIPE, SIG_IGN);
190 if ((sm_rfp = fdopen (sd1, "r")) == NULL
191 || (sm_wfp = fdopen (sd2, "w")) == NULL) {
194 sm_rfp = sm_wfp = NULL;
195 return sm_ierror ("unable to fdopen");
213 * Give EHLO or HELO command
215 if (client && *client) {
217 result = smtalk (SM_HELO, "EHLO %s", client);
220 if (result >= 500 && result <= 599)
221 result = smtalk (SM_HELO, "HELO %s", client);
230 if (watch && EHLOset ("XVRB"))
231 smtalk (SM_HELO, "VERB on");
232 if (onex && EHLOset ("XONE"))
233 smtalk (SM_HELO, "ONEX");
234 if (queued && EHLOset ("XQUE"))
235 smtalk (SM_HELO, "QUED");
241 sendmail_init (char *client, char *server, int watch, int verbose,
242 int debug, int onex, int queued)
251 sm_verbose = verbose;
253 if (sm_rfp != NULL && sm_wfp != NULL)
256 if (client == NULL || *client == '\0') {
260 client = LocalName(); /* no clientname -> LocalName */
264 if (client == NULL || *client == '\0')
265 client = "localhost";
268 if (pipe (pdi) == NOTOK)
269 return sm_ierror ("no pipes");
270 if (pipe (pdo) == NOTOK) {
273 return sm_ierror ("no pipes");
276 for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++)
285 return sm_ierror ("unable to fork");
288 if (pdo[0] != fileno (stdin))
289 dup2 (pdo[0], fileno (stdin));
290 if (pdi[1] != fileno (stdout))
291 dup2 (pdi[1], fileno (stdout));
292 if (pdi[1] != fileno (stderr))
293 dup2 (pdi[1], fileno (stderr));
294 for (i = fileno (stderr) + 1; i < NBITS; i++)
298 vec[vecp++] = r1bindex (sendmail, '/');
301 vec[vecp++] = watch ? "-odi" : queued ? "-odq" : "-odb";
302 vec[vecp++] = "-oem";
307 # endif /* not RAND */
308 #endif /* not ZMAILER */
313 execvp (sendmail, vec);
314 fprintf (stderr, "unable to exec ");
316 _exit (-1); /* NOTREACHED */
319 SIGNAL (SIGALRM, alrmser);
320 SIGNAL (SIGPIPE, SIG_IGN);
324 if ((sm_rfp = fdopen (pdi[0], "r")) == NULL
325 || (sm_wfp = fdopen (pdo[1], "w")) == NULL) {
328 sm_rfp = sm_wfp = NULL;
329 return sm_ierror ("unable to fdopen");
344 if (client && *client) {
346 result = smtalk (SM_HELO, "EHLO %s", client);
349 if (500 <= result && result <= 599)
350 result = smtalk (SM_HELO, "HELO %s", client);
364 smtalk (SM_HELO, "ONEX");
367 smtalk (SM_HELO, "VERB on");
374 # define MAXARGS 1000
378 rclient (char *server, char *protocol, char *service)
381 char response[BUFSIZ];
386 if ((sd = client (server, protocol, service, FALSE, response, sizeof(response))) != NOTOK)
390 if (!server && servers && (cp = strchr(servers, '/'))) {
392 char *arguments[MAXARGS];
394 smail_copyip (smail_brkstring (cp = getcpy (servers), " ", "\n"), arguments, MAXARGS);
396 for (ap = arguments; *ap; ap++)
400 if ((dp = strrchr(*ap, '/')) && *++dp == NULL)
402 snprintf (sm_tmpfil, sizeof(sm_tmpfil), "%s/smtpXXXXXX", *ap);
404 sd = mkstemp (sm_tmpfil);
408 if ((sd = creat (sm_tmpfil, 0600)) != NOTOK) {
421 sm_ierror ("%s", response);
427 #include <sys/socket.h>
428 #include <netinet/in.h>
429 #include <arpa/inet.h>
432 #endif /* CYRUS_SASL */
435 sm_winit (int mode, char *from)
440 if (sm_ispool && !sm_wfp) {
441 strlen (strcpy (sm_reply.text, "unable to create new spool file"));
442 sm_reply.code = NOTOK;
465 switch (smtalk (SM_MAIL, "%s FROM:<%s>", smtpcom, from)) {
482 sm_wadr (char *mbox, char *host, char *path)
484 switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
486 path ? path : "", mbox, host)) {
496 #endif /* SENDMAILBUG */
521 switch (smtalk (SM_DATA, "DATA")) {
530 #endif /* SENDMAILBUG */
547 sm_wtxt (char *buffer, int len)
553 result = sm_wstream (buffer, len);
556 return (result == NOTOK ? RP_BHST : RP_OK);
563 if (sm_wstream ((char *) NULL, 0) == NOTOK)
566 switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
574 #endif /* SENDMAILBUG */
592 if (sm_mts == MTS_SENDMAIL) {
603 if (sm_rfp == NULL && sm_wfp == NULL)
608 smtalk (SM_QUIT, "QUIT");
612 sm_note.code = sm_reply.code;
613 strncpy (sm_note.text, sm_reply.text, sm_note.length = sm_reply.length);/* fall */
615 if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
617 if (sm_mts == MTS_SMTP)
618 smtalk (SM_QUIT, "QUIT");
620 kill (sm_child, SIGKILL);
625 sm_reply.code = sm_note.code;
626 strncpy (sm_reply.text, sm_note.text, sm_reply.length = sm_note.length);
643 if (sm_rfp != NULL) {
648 if (sm_wfp != NULL) {
654 if (sm_mts == MTS_SMTP)
657 status = pidwait (sm_child, OK);
661 sm_rfp = sm_wfp = NULL;
662 return (status ? RP_BHST : RP_OK);
671 int cc, i, j, k, result;
673 char *dp, *bp, *cp, s;
674 char buffer[BUFSIZ], sender[BUFSIZ];
678 k = strlen (file) - sizeof(".bulk");
679 if ((fp = fopen (file, "r")) == NULL) {
682 snprintf (sm_reply.text, sizeof(sm_reply.text),
683 "unable to read %s: ", file);
687 if ((s = strerror (errno)))
688 strncpy (bp, s, sizeof(sm_reply.text) - len);
690 snprintf (bp, sizeof(sm_reply.text) - len, "Error %d", errno);
691 sm_reply.length = strlen (sm_reply.text);
692 sm_reply.code = NOTOK;
696 printf ("reading file %s\n", file);
701 while (fgets (buffer, sizeof(buffer), fp)) {
703 strncpy (sender, buffer + sizeof("MAIL FROM:") - 1, sizeof(sender));
704 if (strcmp (buffer, "DATA\r\n") == 0) {
711 printf ("no DATA...\n");
715 snprintf (buffer, sizeof(buffer), "%s.bad", file);
716 rename (file, buffer);
718 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
727 printf ("no %srecipients...\n", j < 1 ? "sender or " : "");
733 if ((cp = malloc ((size_t) (cc = (pos = ftell (fp)) + 1))) == NULL) {
734 sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
736 sm_reply.code = NOTOK;
740 fseek (fp, 0L, SEEK_SET);
741 for (dp = cp, i = 0; i++ < j; dp += strlen (dp))
742 if (fgets (dp, cc - (dp - cp), fp) == NULL) {
743 sm_reply.length = strlen (strcpy (sm_reply.text, "premature eof"));
750 for (dp = cp, i = cc - 1; i > 0; dp += cc, i -= cc)
751 if ((cc = write (fileno (sm_wfp), dp, i)) == NOTOK) {
754 strcpy (sm_reply.text, "error writing to server: ",
755 sizeof(sm_reply.text));
759 if ((s = strerror (errno)))
760 strncpy (bp, s, sizeof(sm_reply.text) - len);
762 snprintf (bp, sizeof(sm_reply.text) - len,
763 "unknown error %d", errno);
764 sm_reply.length = strlen (sm_reply.text);
769 printf ("wrote %d octets to server\n", cc);
773 for (dp = cp, i = 0; i++ < j; dp = strchr(dp, '\n'), dp++) {
775 if (bp = strchr(dp, '\r'))
777 printf ("=> %s\n", dp);
783 switch (smhear () + (i == 1 ? 1000 : i != j ? 2000 : 3000)) {
795 smtalk (SM_RSET, "RSET");
823 if (k <= 0 || strcmp (sender, "<>\r\n") == 0)
827 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
828 if ((gp = fopen (buffer, "w+")) == NULL)
830 fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
833 "To: %*.*s\r\nSubject: Invalid addresses (%s)\r\n",
834 l - 4, l - 4, sender + 1, file);
835 fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
836 dtimenow (0), LocalName ());
838 if (bp = strchr(dp, '\r'))
840 fprintf (gp, "=> %s\r\n", dp);
843 fprintf (gp, "<= %s\r\n", rp_string (result));
861 smtalk (SM_RSET, "RSET");
864 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
875 smtalk (SM_RSET, "RSET");
887 #ifdef HAVE_ST_BLKSIZE
890 if (fstat (fileno (sm_wfp), &st) == NOTOK || (cc = st.st_blksize) < BUFSIZ)
895 if ((cp = malloc ((size_t) cc)) == NULL) {
896 smtalk (SM_RSET, "RSET");
897 sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
902 fseek (fp, pos, SEEK_SET);
906 for (dp = cp, i = cc; i > 0; dp += j, i -= j)
907 if ((j = fread (cp, sizeof(*cp), i, fp)) == OK) {
911 snprintf (sm_reply.text, sizeof(sm_reply.text),
912 "error reading %s: ", file);
916 if ((s = strerror (errno)))
917 strncpy (bp, s, sizeof(sm_reply.text) - len);
919 snprintf (bp, sizeof(sm_reply.text) - len,
920 "unknown error %d", errno);
921 sm_reply.length = strlen (sm_reply.text);
929 for (dp = cp, i = cc; i > 0; dp += j, i -= j)
930 if ((j = write (fileno (sm_wfp), dp, i)) == NOTOK)
934 printf ("wrote %d octets to server\n", j);
961 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
972 if (k <= 0 || strcmp (sender, "<>\r\n") == 0) {
978 ftruncate (fileno (gp), 0L);
979 fseek (gp, 0L, SEEK_SET);
982 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
983 if ((gp = fopen (buffer, "w")) == NULL)
986 fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
988 fprintf (gp, "To: %*.*s\r\nSubject: Failed mail (%s)\r\n",
989 i - 4, i - 4, sender + 1, file);
990 fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
991 dtimenow (0), LocalName ());
996 fputs ("\r\n------- Begin Returned message\r\n\r\n", gp);
997 fseek (fp, pos, SEEK_SET);
998 while (fgets (buffer, sizeof(buffer), fp)) {
999 if (buffer[0] == '-')
1001 if (strcmp (buffer, ".\r\n"))
1004 fputs ("\r\n------- End Returned Message\r\n\r\n.\r\n", gp);
1019 * This function implements SASL authentication for SMTP. If this function
1020 * completes successfully, then authentication is successful and we've
1021 * (optionally) negotiated a security layer.
1023 * Right now we don't support session encryption.
1026 sm_auth_sasl(char *user, char *mechlist, char *host)
1028 int result, status, outlen;
1029 unsigned int buflen;
1030 char *buf, outbuf[BUFSIZ];
1031 const char *chosen_mech;
1032 sasl_security_properties_t secprops;
1033 sasl_external_properties_t extprops;
1038 * Initialize the callback contexts
1042 user = getusername();
1044 callbacks[SM_SASL_N_CB_USER].context = user;
1047 * This is a _bit_ of a hack ... but if the hostname wasn't supplied
1048 * to us on the command line, then call getpeername and do a
1049 * reverse-address lookup on the IP address to get the name.
1053 struct sockaddr_in sin;
1054 int len = sizeof(sin);
1057 if (getpeername(fileno(sm_wfp), (struct sockaddr *) &sin, &len) < 0) {
1058 sm_ierror("getpeername on SMTP socket failed: %s",
1063 if ((hp = gethostbyaddr((void *) &sin.sin_addr, sizeof(sin.sin_addr),
1064 sin.sin_family)) == NULL) {
1065 sm_ierror("DNS lookup on IP address %s failed",
1066 inet_ntoa(sin.sin_addr));
1070 host = strdup(hp->h_name);
1073 sasl_pw_context[0] = host;
1074 sasl_pw_context[1] = user;
1076 callbacks[SM_SASL_N_CB_PASS].context = sasl_pw_context;
1078 result = sasl_client_init(callbacks);
1080 if (result != SASL_OK) {
1081 sm_ierror("SASL library initialization failed: %s",
1082 sasl_errstring(result, NULL, NULL));
1086 result = sasl_client_new("smtp", host, NULL, SASL_SECURITY_LAYER, &conn);
1088 if (result != SASL_OK) {
1089 sm_ierror("SASL client initialization failed: %s",
1090 sasl_errstring(result, NULL, NULL));
1095 * Initialize the security properties
1098 memset(&secprops, 0, sizeof(secprops));
1099 secprops.maxbufsize = BUFSIZ;
1100 secprops.max_ssf = 0; /* XXX change this when we do encryption */
1101 memset(&extprops, 0, sizeof(extprops));
1103 result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
1105 if (result != SASL_OK) {
1106 sm_ierror("SASL security property initialization failed: %s",
1107 sasl_errstring(result, NULL, NULL));
1111 result = sasl_setprop(conn, SASL_SSF_EXTERNAL, &extprops);
1113 if (result != SASL_OK) {
1114 sm_ierror("SASL external property initialization failed: %s",
1115 sasl_errstring(result, NULL, NULL));
1120 * Start the actual protocol. Feed the mech list into the library
1121 * and get out a possible initial challenge
1124 result = sasl_client_start(conn, mechlist, NULL, NULL, &buf, &buflen,
1127 if (result != SASL_OK && result != SASL_CONTINUE) {
1128 sm_ierror("SASL client start failed: %s",
1129 sasl_errstring(result, NULL, NULL));
1134 * If we got an initial challenge, send it as part of the AUTH
1135 * command; otherwise, just send a plain AUTH command.
1139 status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
1141 if (status != SASL_OK) {
1142 sm_ierror("SASL base64 encode failed: %s",
1143 sasl_errstring(status, NULL, NULL));
1147 status = smtalk(SM_AUTH, "AUTH %s %s", chosen_mech, outbuf);
1149 status = smtalk(SM_AUTH, "AUTH %s", chosen_mech);
1152 * Now we loop until we either fail, get a SASL_OK, or a 235
1153 * response code. Receive the challenges and process them until
1157 while (result == SASL_CONTINUE) {
1160 * If we get a 235 response, that means authentication has
1161 * succeeded and we need to break out of the loop (yes, even if
1162 * we still get SASL_CONTINUE from sasl_client_step()).
1164 * Otherwise, if we get a message that doesn't seem to be a
1165 * valid response, then abort
1170 else if (status < 300 || status > 399)
1174 * Special case; a zero-length response from the SMTP server
1175 * is returned as a single =. If we get that, then set buflen
1176 * to be zero. Otherwise, just decode the response.
1179 if (strcmp("=", sm_reply.text) == 0) {
1182 result = sasl_decode64(sm_reply.text, sm_reply.length,
1185 if (result != SASL_OK) {
1186 smtalk(SM_AUTH, "*");
1187 sm_ierror("SASL base64 decode failed: %s",
1188 sasl_errstring(result, NULL, NULL));
1193 result = sasl_client_step(conn, outbuf, outlen, NULL, &buf, &buflen);
1195 if (result != SASL_OK && result != SASL_CONTINUE) {
1196 smtalk(SM_AUTH, "*");
1197 sm_ierror("SASL client negotiation failed: %s",
1198 sasl_errstring(result, NULL, NULL));
1202 status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
1205 if (status != SASL_OK) {
1206 smtalk(SM_AUTH, "*");
1207 sm_ierror("SASL base64 encode failed: %s",
1208 sasl_errstring(status, NULL, NULL));
1212 status = smtalk(SM_AUTH, outbuf);
1216 * Make sure that we got the correct response
1219 if (status < 200 || status > 299)
1223 * Depending on the mechanism, we need to do a FINAL call to
1224 * sasl_client_step(). Do that now.
1227 result = sasl_client_step(conn, NULL, 0, NULL, &buf, &buflen);
1229 if (result != SASL_OK) {
1230 sm_ierror("SASL final client negotiation failed: %s",
1231 sasl_errstring(result, NULL, NULL));
1236 * We _should_ have completed the authentication successfully.
1237 * Get a few properties from the authentication exchange.
1240 result = sasl_getprop(conn, SASL_MAXOUTBUF, (void **) &outbufmax);
1242 if (result != SASL_OK) {
1243 sm_ierror("Cannot retrieve SASL negotiated output buffer size: %s",
1244 sasl_errstring(result, NULL, NULL));
1248 maxoutbuf = *outbufmax;
1250 result = sasl_getprop(conn, SASL_SSF, (void **) &ssf);
1254 if (result != SASL_OK) {
1255 sm_ierror("Cannot retrieve SASL negotiated security strength "
1256 "factor: %s", sasl_errstring(result, NULL, NULL));
1260 if (maxoutbuf == 0 || maxoutbuf > BUFSIZ)
1269 * Our callback functions to feed data to the SASL library
1273 sm_get_user(void *context, int id, const char **result, unsigned *len)
1275 char *user = (char *) context;
1277 if (! result || id != SASL_CB_USER)
1278 return SASL_BADPARAM;
1282 *len = strlen(user);
1288 sm_get_pass(sasl_conn_t *conn, void *context, int id,
1289 sasl_secret_t **psecret)
1291 char **pw_context = (char **) context;
1295 if (! psecret || id != SASL_CB_PASS)
1296 return SASL_BADPARAM;
1298 ruserpass(pw_context[0], &(pw_context[1]), &pass);
1302 *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
1309 (*psecret)->len = len;
1310 strcpy((*psecret)->data, pass);
1315 #endif /* CYRUS_SASL */
1318 sm_ierror (char *fmt, ...)
1323 vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
1326 sm_reply.length = strlen (sm_reply.text);
1327 sm_reply.code = NOTOK;
1334 smtalk (int time, char *fmt, ...)
1338 char buffer[BUFSIZ];
1341 vsnprintf (buffer, sizeof(buffer), fmt, ap);
1345 printf ("=> %s\n", buffer);
1353 if (strcmp (buffer, ".") == 0)
1355 fprintf (sm_wfp, "%s\r\n", buffer);
1359 if (ferror (sm_wfp))
1360 return sm_werror ();
1361 snprintf (file, sizeof(file), "%s%c.bulk", sm_tmpfil,
1362 (char) (sm_ispool + 'a' - 1));
1363 if (rename (sm_tmpfil, file) == NOTOK) {
1367 snprintf (sm_reply.text, sizeof(sm_reply.text),
1368 "error renaming %s to %s: ", sm_tmpfil, file);
1372 if ((s = strerror (errno)))
1373 strncpy (bp, s, sizeof(sm_reply.text) - len);
1375 snprintf (bp, sizeof(sm_reply.text) - len,
1376 "unknown error %d", errno);
1377 sm_reply.length = strlen (sm_reply.text);
1378 sm_reply.code = NOTOK;
1382 if (sm_wfp = fopen (sm_tmpfil, "w"))
1383 chmod (sm_tmpfil, 0600);
1394 ftruncate (fileno (sm_wfp), 0L);
1395 fseek (sm_wfp, 0L, SEEK_SET);
1414 printf ("<= %d\n", result);
1418 sm_reply.text[sm_reply.length = 0] = NULL;
1419 return (sm_reply.code = result);
1424 alarm ((unsigned) time);
1425 if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK)
1434 * write the buffer to the open SMTP channel
1438 sm_wrecord (char *buffer, int len)
1441 return sm_werror ();
1443 fwrite (buffer, sizeof(*buffer), len, sm_wfp);
1444 fputs ("\r\n", sm_wfp);
1447 return (ferror (sm_wfp) ? sm_werror () : OK);
1452 sm_wstream (char *buffer, int len)
1455 static char lc = '\0';
1458 return sm_werror ();
1460 if (buffer == NULL && len == 0) {
1462 fputs ("\r\n", sm_wfp);
1464 return (ferror (sm_wfp) ? sm_werror () : OK);
1467 for (bp = buffer; len > 0; bp++, len--) {
1471 fputc ('\r', sm_wfp);
1476 fputc ('.', sm_wfp);/* FALL THROUGH */
1480 fputc (*bp, sm_wfp);
1481 if (ferror (sm_wfp))
1482 return sm_werror ();
1487 return (ferror (sm_wfp) ? sm_werror () : OK);
1492 * On some systems, strlen and strcpy are defined as preprocessor macros. This
1493 * causes compile problems with the #ifdef MPOP in the middle. Should the
1494 * #ifdef MPOP be removed, remove these #undefs.
1507 strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no socket opened"
1508 : sm_alarmed ? "write to socket timed out"
1510 : sm_ispool ? "error writing to spool file"
1512 : "error writing to socket"));
1514 return (sm_reply.code = NOTOK);
1521 int i, code, cont, bc, rc, more;
1523 char **ehlo, buffer[BUFSIZ];
1526 static int at_least_once = 0;
1528 if (at_least_once) {
1531 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1545 sm_reply.length = 0;
1546 sm_reply.text[0] = 0;
1548 rc = sizeof(sm_reply.text) - 1;
1550 for (more = FALSE; sm_rrecord (bp = buffer, &bc) != NOTOK;) {
1552 printf ("<= %s\n", buffer);
1557 && strncmp (buffer, "250", sizeof("250") - 1) == 0
1558 && (buffer[3] == '-' || doingEHLO == 2)
1560 if (doingEHLO == 2) {
1561 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
1562 strcpy (*ehlo++, buffer + 4);
1564 if (ehlo >= EHLOkeys + MAXEHLO)
1574 for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--)
1580 for (; bc > 0 && isspace (*bp); bp++, bc--)
1582 if (bc > 0 && *bp == '-') {
1585 for (; bc > 0 && isspace (*bp); bp++, bc--)
1590 if (code != sm_reply.code || cont)
1594 sm_reply.code = code;
1597 strncpy (buffer, sm_noreply, sizeof(buffer));
1599 bc = strlen (sm_noreply);
1603 if ((i = min (bc, rc)) > 0) {
1604 strncpy (rp, bp, i);
1607 if (more && rc > strlen (sm_moreply) + 1) {
1608 strncpy (sm_reply.text + rc, sm_moreply, sizeof(sm_reply.text) - rc);
1609 rc += strlen (sm_moreply);
1614 if (sm_reply.code < 100) {
1616 printf ("%s\n", sm_reply.text);
1622 sm_reply.length = rp - sm_reply.text;
1623 return sm_reply.code;
1630 sm_rrecord (char *buffer, int *len)
1633 return sm_rerror ();
1635 buffer[*len = 0] = 0;
1637 fgets (buffer, BUFSIZ, sm_rfp);
1638 *len = strlen (buffer);
1639 if (ferror (sm_rfp) || feof (sm_rfp))
1640 return sm_rerror ();
1641 if (buffer[*len - 1] != '\n')
1642 while (getc (sm_rfp) != '\n' && !ferror (sm_rfp) && !feof (sm_rfp))
1645 if (buffer[*len - 2] == '\r')
1647 buffer[*len - 1] = 0;
1656 if (sm_mts == MTS_SMTP)
1658 strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no socket opened"
1659 : sm_alarmed ? "read from socket timed out"
1660 : feof (sm_rfp) ? "premature end-of-file on socket"
1661 : "error reading from socket"));
1664 strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no pipe opened"
1665 : sm_alarmed ? "read from pipe timed out"
1666 : feof (sm_rfp) ? "premature end-of-file on pipe"
1667 : "error reading from pipe"));
1669 return (sm_reply.code = NOTOK);
1676 #ifndef RELIABLE_SIGNALS
1677 SIGNAL (SIGALRM, alrmser);
1682 printf ("timed out...\n");
1689 rp_string (int code)
1692 static char buffer[BUFSIZ];
1694 switch (sm_reply.code != NOTOK ? code : NOTOK) {
1714 snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
1734 snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
1735 text, sm_reply.code, sm_reply.text);
1742 static char *broken[MAXARGS + 1];
1745 smail_brkstring (char *strg, char *brksep, char *brkterm)
1752 for (bi = 0; bi < MAXARGS; bi++) {
1753 while (smail_brkany (c = *sp, brksep))
1755 if (!c || smail_brkany (c, brkterm)) {
1762 while ((c = *++sp) && !smail_brkany (c, brksep) && !smail_brkany (c, brkterm))
1765 broken[MAXARGS] = 0;
1772 * returns 1 if chr in strg, 0 otherwise
1775 smail_brkany (char chr, char *strg)
1780 for (sp = strg; *sp; sp++)
1787 * copy a string array and return pointer to end
1790 smail_copyip (char **p, char **q, int len_q)
1792 while (*p && --len_q > 0)
1811 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1813 if (strncmp (ep, s, len) == 0) {
1814 for (ep += len; *ep == ' '; ep++)