-#ifdef MPOP
-
-int
-sm_bulk (char *file)
-{
- int cc, i, j, k, result;
- long pos;
- char *dp, *bp, *cp, s;
- char buffer[BUFSIZ], sender[BUFSIZ];
- FILE *fp, *gp;
-
- gp = NULL;
- k = strlen (file) - sizeof(".bulk");
- if ((fp = fopen (file, "r")) == NULL) {
- return sm_perror("unable to read %s: ", file);
- }
- if (sm_debug) {
- printf ("reading file %s\n", file);
- fflush (stdout);
- }
-
- i = j = 0;
- while (fgets (buffer, sizeof(buffer), fp)) {
- if (j++ == 0)
- strncpy (sender, buffer + sizeof("MAIL FROM:") - 1, sizeof(sender));
- if (strcmp (buffer, "DATA\r\n") == 0) {
- i = 1;
- break;
- }
- }
- if (i == 0) {
- if (sm_debug) {
- printf ("no DATA...\n");
- fflush (stdout);
- }
-losing0:
- snprintf (buffer, sizeof(buffer), "%s.bad", file);
- rename (file, buffer);
- if (gp) {
- snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
- unlink (buffer);
- fclose (gp);
- }
- fclose (fp);
- return RP_OK;
- }
- if (j < 3) {
- if (sm_debug) {
- printf ("no %srecipients...\n", j < 1 ? "sender or " : "");
- fflush (stdout);
- }
- goto losing0;
- }
-
- if ((cp = malloc ((size_t) (cc = (pos = ftell (fp)) + 1))) == NULL) {
- sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
-losing1: ;
- sm_reply.code = NOTOK;
- fclose (fp);
- return RP_BHST;
- }
- fseek (fp, 0L, SEEK_SET);
- for (dp = cp, i = 0; i++ < j; dp += strlen (dp))
- if (fgets (dp, cc - (dp - cp), fp) == NULL) {
- sm_reply.length = strlen (strcpy (sm_reply.text, "premature eof"));
-losing2:
- free (cp);
- goto losing1;
- }
- *dp = NULL;
-
- for (dp = cp, i = cc - 1; i > 0; dp += cc, i -= cc)
- if ((cc = write (fileno (sm_wfp), dp, i)) == NOTOK) {
- int len;
-losing3:
- sm_perror("error writing to server: ");
- goto losing2;
- }
- else
- if (sm_debug) {
- printf ("wrote %d octets to server\n", cc);
- fflush (stdout);
- }
-
- for (dp = cp, i = 0; i++ < j; dp = strchr(dp, '\n'), dp++) {
- if (sm_debug) {
- if (bp = strchr(dp, '\r'))
- *bp = NULL;
- printf ("=> %s\n", dp);
- fflush (stdout);
- if (bp)
- *bp = '\r';
- }
-
- switch (smhear () + (i == 1 ? 1000 : i != j ? 2000 : 3000)) {
- case 1000 + 250:
- sm_addrs = 0;
- result = RP_OK;
- break;
-
- case 1000 + 500:
- case 1000 + 501:
- case 1000 + 552:
- case 2000 + 500:
- case 2000 + 501:
- result = RP_PARM;
- smtalk (SM_RSET, "RSET");
- free (cp);
- goto losing0;
-
- case 2000 + 250:
- case 2000 + 251:
- sm_addrs++;
- result = RP_OK;
- break;
-
- case 2000 + 451:
-#ifdef SENDMAILBUG
- sm_addrs++;
- result = RP_OK;
- break;
-#endif
- case 2000 + 421:
- case 2000 + 450:
- case 2000 + 452:
- result = RP_NO;
- goto bad_addr;
-
- case 2000 + 550:
- case 2000 + 551:
- case 2000 + 552:
- case 2000 + 553:
- result = RP_USER;
-bad_addr:
- if (k <= 0 || strcmp (sender, "<>\r\n") == 0)
- break;
- if (gp == NULL) {
- int l;
- snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
- if ((gp = fopen (buffer, "w+")) == NULL)
- goto bad_data;
- fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
- l = strlen (sender);
- fprintf (gp,
- "To: %*.*s\r\nSubject: Invalid addresses (%s)\r\n",
- l - 4, l - 4, sender + 1, file);
- fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
- dtimenow (0), LocalName ());
- }
- if (bp = strchr(dp, '\r'))
- *bp = NULL;
- fprintf (gp, "=> %s\r\n", dp);
- if (bp)
- *bp = '\r';
- fprintf (gp, "<= %s\r\n", rp_string (result));
- fflush (gp);
- break;
-
- case 3000 + 354:
-#ifdef SENDMAILBUG
-ok_data:
-#endif
- result = RP_OK;
- break;
-
- case 3000 + 451:
-#ifdef SENDMAILBUG
- goto ok_data;
-#endif
- case 3000 + 421:
- result = RP_NO;
-bad_data:
- smtalk (SM_RSET, "RSET");
- free (cp);
- if (gp) {
- snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
- unlink (buffer);
- fclose (gp);
- }
- fclose (fp);
- return result;
-
- case 3000 + 500:
- case 3000 + 501:
- case 3000 + 503:
- case 3000 + 554:
- smtalk (SM_RSET, "RSET");
- free (cp);
- goto no_dice;
-
- default:
- result = RP_RPLY;
- goto bad_data;
- }
- }
- free (cp);
-
- {
-#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
- struct stat st;
-
- if (fstat (fileno (sm_wfp), &st) == NOTOK || (cc = st.st_blksize) < BUFSIZ)
- cc = BUFSIZ;
-#else
- cc = BUFSIZ;
-#endif
- if ((cp = malloc ((size_t) cc)) == NULL) {
- smtalk (SM_RSET, "RSET");
- sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
- goto losing1;
- }
- }
-
- fseek (fp, pos, SEEK_SET);
- for (;;) {
- int eof = 0;
-
- for (dp = cp, i = cc; i > 0; dp += j, i -= j)
- if ((j = fread (cp, sizeof(*cp), i, fp)) == OK) {
- if (ferror (fp)) {
- sm_perror("error reading %s: ", file);
- goto losing2;
- }
- cc = dp - cp;
- eof = 1;
- break;
- }
-
- for (dp = cp, i = cc; i > 0; dp += j, i -= j)
- if ((j = write (fileno (sm_wfp), dp, i)) == NOTOK)
- goto losing3;
- else
- if (sm_debug) {
- printf ("wrote %d octets to server\n", j);
- fflush (stdout);
- }
-
- if (eof)
- break;
- }
- free (cp);
-
- switch (smhear ()) {
- case 250:
- case 251:
-#ifdef SENDMAILBUG
-ok_dot:
-#endif
- result = RP_OK;
- unlink (file);
- break;
-
- case 451:
-#ifdef SENDMAILBUG
- goto ok_dot;
-#endif
- case 452:
- default:
- result = RP_NO;
- if (gp) {
- snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
- unlink (buffer);
- fclose (gp);
- gp = NULL;
- }
- break;
-
- case 552:
- case 554:
-no_dice:
- result = RP_NDEL;
- if (k <= 0 || strcmp (sender, "<>\r\n") == 0) {
- unlink (file);
- break;
- }
- if (gp) {
- fflush (gp);
- ftruncate (fileno (gp), 0L);
- fseek (gp, 0L, SEEK_SET);
- }
- else {
- snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
- if ((gp = fopen (buffer, "w")) == NULL)
- break;
- }
- fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
- i = strlen (sender);
- fprintf (gp, "To: %*.*s\r\nSubject: Failed mail (%s)\r\n",
- i - 4, i - 4, sender + 1, file);
- fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
- dtimenow (0), LocalName ());
- break;
- }
-
- if (gp) {
- fputs ("\r\n------- Begin Returned message\r\n\r\n", gp);
- fseek (fp, pos, SEEK_SET);
- while (fgets (buffer, sizeof(buffer), fp)) {
- if (buffer[0] == '-')
- fputs ("- ", gp);
- if (strcmp (buffer, ".\r\n"))
- fputs (buffer, gp);
- }
- fputs ("\r\n------- End Returned Message\r\n\r\n.\r\n", gp);
- fflush (gp);
- if (!ferror (gp))
- unlink (file);
- fclose (gp);
- }
- fclose (fp);
-
- return result;
-}
-#endif /* MPOP */
-
-
-#ifdef CYRUS_SASL
-/*
- * This function implements SASL authentication for SMTP. If this function
- * completes successfully, then authentication is successful and we've
- * (optionally) negotiated a security layer.
- *
- * Right now we don't support session encryption.
- */
-static int
-sm_auth_sasl(char *user, char *mechlist, char *host)
-{
- int result, status;
- unsigned int buflen, outlen;
- char *buf, outbuf[BUFSIZ];
- const char *chosen_mech;
- sasl_security_properties_t secprops;
- sasl_ssf_t *ssf;
- int *outbufmax;
-
- /*
- * Initialize the callback contexts
- */
-
- if (user == NULL)
- user = getusername();
-
- callbacks[SM_SASL_N_CB_USER].context = user;
- callbacks[SM_SASL_N_CB_AUTHNAME].context = user;
-
- /*
- * This is a _bit_ of a hack ... but if the hostname wasn't supplied
- * to us on the command line, then call getpeername and do a
- * reverse-address lookup on the IP address to get the name.
- */
-
- if (!host) {
- struct sockaddr_in sin;
- int len = sizeof(sin);
- struct hostent *hp;
-
- if (getpeername(fileno(sm_wfp), (struct sockaddr *) &sin, &len) < 0) {
- sm_ierror("getpeername on SMTP socket failed: %s",
- strerror(errno));
- return NOTOK;
- }
-
- if ((hp = gethostbyaddr((void *) &sin.sin_addr, sizeof(sin.sin_addr),
- sin.sin_family)) == NULL) {
- sm_ierror("DNS lookup on IP address %s failed",
- inet_ntoa(sin.sin_addr));
- return NOTOK;
- }
-
- host = strdup(hp->h_name);
- }
-
- sasl_pw_context[0] = host;
- sasl_pw_context[1] = user;
-
- callbacks[SM_SASL_N_CB_PASS].context = sasl_pw_context;
-
- result = sasl_client_init(callbacks);
-
- if (result != SASL_OK) {
- sm_ierror("SASL library initialization failed: %s",
- sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- result = sasl_client_new("smtp", host, NULL, NULL, NULL, 0, &conn);
-
- if (result != SASL_OK) {
- sm_ierror("SASL client initialization failed: %s",
- sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- /*
- * Initialize the security properties
- */
-
- memset(&secprops, 0, sizeof(secprops));
- secprops.maxbufsize = BUFSIZ;
- secprops.max_ssf = 0; /* XXX change this when we do encryption */
-
- result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
-
- if (result != SASL_OK) {
- sm_ierror("SASL security property initialization failed: %s",
- sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- /*
- * Start the actual protocol. Feed the mech list into the library
- * and get out a possible initial challenge
- */
-
- result = sasl_client_start(conn, mechlist, NULL, (const char **) &buf,
- &buflen, (const char **) &chosen_mech);
-
- if (result != SASL_OK && result != SASL_CONTINUE) {
- sm_ierror("SASL client start failed: %s",
- sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- /*
- * If we got an initial challenge, send it as part of the AUTH
- * command; otherwise, just send a plain AUTH command.
- */
-
- if (buflen) {
- status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
- if (status != SASL_OK) {
- sm_ierror("SASL base64 encode failed: %s",
- sasl_errstring(status, NULL, NULL));
- return NOTOK;
- }
-
- status = smtalk(SM_AUTH, "AUTH %s %s", chosen_mech, outbuf);
- } else
- status = smtalk(SM_AUTH, "AUTH %s", chosen_mech);
-
- /*
- * Now we loop until we either fail, get a SASL_OK, or a 235
- * response code. Receive the challenges and process them until
- * we're all done.
- */
-
- while (result == SASL_CONTINUE) {
-
- /*
- * If we get a 235 response, that means authentication has
- * succeeded and we need to break out of the loop (yes, even if
- * we still get SASL_CONTINUE from sasl_client_step()).
- *
- * Otherwise, if we get a message that doesn't seem to be a
- * valid response, then abort
- */
-
- if (status == 235)
- break;
- else if (status < 300 || status > 399)
- return RP_BHST;
-
- /*
- * Special case; a zero-length response from the SMTP server
- * is returned as a single =. If we get that, then set buflen
- * to be zero. Otherwise, just decode the response.
- */
-
- if (strcmp("=", sm_reply.text) == 0) {
- outlen = 0;
- } else {
- result = sasl_decode64(sm_reply.text, sm_reply.length,
- outbuf, sizeof(outbuf), &outlen);
-
- if (result != SASL_OK) {
- smtalk(SM_AUTH, "*");
- sm_ierror("SASL base64 decode failed: %s",
- sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
- }
-
- result = sasl_client_step(conn, outbuf, outlen, NULL,
- (const char **) &buf, &buflen);
-
- if (result != SASL_OK && result != SASL_CONTINUE) {
- smtalk(SM_AUTH, "*");
- sm_ierror("SASL client negotiation failed: %s",
- sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
-
- if (status != SASL_OK) {
- smtalk(SM_AUTH, "*");
- sm_ierror("SASL base64 encode failed: %s",
- sasl_errstring(status, NULL, NULL));
- return NOTOK;
- }
-
- status = smtalk(SM_AUTH, outbuf);
- }
-
- /*
- * Make sure that we got the correct response
- */
-
- if (status < 200 || status > 299)
- return RP_BHST;
-
- /*
- * We _should_ have completed the authentication successfully.
- * Get a few properties from the authentication exchange.
- */
-
- result = sasl_getprop(conn, SASL_MAXOUTBUF, (const void **) &outbufmax);
-
- if (result != SASL_OK) {
- sm_ierror("Cannot retrieve SASL negotiated output buffer size: %s",
- sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- maxoutbuf = *outbufmax;
-
- result = sasl_getprop(conn, SASL_SSF, (const void **) &ssf);
-
- sasl_ssf = *ssf;
-
- if (result != SASL_OK) {
- sm_ierror("Cannot retrieve SASL negotiated security strength "
- "factor: %s", sasl_errstring(result, NULL, NULL));
- return NOTOK;
- }
-
- if (maxoutbuf == 0 || maxoutbuf > BUFSIZ)
- maxoutbuf = BUFSIZ;
-
- sasl_complete = 1;
-
- return RP_OK;
-}
-
-/*
- * Our callback functions to feed data to the SASL library
- */
-
-static int
-sm_get_user(void *context, int id, const char **result, unsigned *len)
-{
- char *user = (char *) context;
-
- if (! result || ((id != SASL_CB_USER) && (id != SASL_CB_AUTHNAME)))
- return SASL_BADPARAM;
-
- *result = user;
- if (len)
- *len = strlen(user);
-
- return SASL_OK;
-}
-