#include <sys/wait.h>
#include <string.h>
#include <limits.h>
+#include <time.h>
#include "citadel.h"
#include "server.h"
-#include <time.h>
#include "sysdep_decls.h"
#include "citserver.h"
#include "support.h"
#include "tools.h"
#include "internet_addressing.h"
#include "genstamp.h"
+#include "domain.h"
+#include "clientsocket.h"
struct citsmtp { /* Information about the current session */
int command_state;
+ char helo_node[256];
struct usersupp vrfy_buffer;
int vrfy_count;
char vrfy_match[256];
/*****************************************************************************/
+
+
/*
* Here's where our SMTP session begins its happy day.
*/
*/
void smtp_hello(char *argbuf, int is_esmtp) {
+ safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
+
if (!is_esmtp) {
cprintf("250 Greetings and joyous salutations.\r\n");
}
else {
cvt = convert_internet_address(user, node, SMTP->from);
lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
- if (!strcasecmp(node, config.c_nodename)) { /* FIX use fcn */
+ if (CtdlHostAlias(node) == hostalias_localhost) {
cprintf("550 You must log in to send mail from %s\r\n",
- config.c_fqdn);
+ node);
strcpy(SMTP->from, "");
return;
}
generate_rfc822_datestamp(nowstamp, time(NULL));
body = mallok(4096);
+
if (body != NULL) sprintf(body,
"Received: from %s\n"
" by %s;\n"
" %s\n",
- "FIX.FIX.com",
+ SMTP->helo_node,
config.c_fqdn,
nowstamp);
* Called by smtp_do_procmsg() to attempt delivery to one SMTP host
*
*/
-void smtp_try(char *key, char *addr, int *status, char *dsn) {
+void smtp_try(char *key, char *addr, int *status, char *dsn, long msgnum)
+{
+ char buf[256];
+ int sock = (-1);
+ char mxhosts[1024];
+ int num_mxhosts;
+ int mx;
+ char user[256], node[256], name[256];
+
+ /* Parse out the host portion of the recipient address */
+ process_rfc822_addr(addr, user, node, name);
+ lprintf(9, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
+ user, node, name);
+
+ num_mxhosts = getmx(mxhosts, node);
+ lprintf(9, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
+ if (num_mxhosts < 1) {
+ *status = 5;
+ sprintf(dsn, "No MX hosts found for <%s>", node);
+ return;
+ }
+
+ for (mx=0; mx<num_mxhosts; ++mx) {
+ extract(buf, mxhosts, mx);
+ lprintf(9, "Trying <%s>\n", buf);
+ sock = sock_connect(buf, "25", "tcp");
+ sprintf(dsn, "Could not connect: %s", strerror(errno));
+ if (sock >= 0) lprintf(9, "Connected!\n");
+ if (sock < 0) sprintf(dsn, "%s", strerror(errno));
+ if (sock >= 0) break;
+ }
+
+ if (sock < 0) {
+ *status = 4; /* dsn is already filled in */
+ return;
+ }
+
+ /* Process the SMTP greeting from the server */
+ if (sock_gets(sock, buf) < 0) {
+ *status = 4;
+ strcpy(dsn, "Connection broken during SMTP conversation");
+ sock_close(sock);
+ return;
+ }
+ lprintf(9, "%s\n", buf);
+ if (buf[0] != '2') {
+ if (buf[0] == '4') {
+ *status = 4;
+ strcpy(dsn, &buf[4]);
+ sock_close(sock);
+ return;
+ }
+ else {
+ *status = 5;
+ strcpy(dsn, &buf[4]);
+ sock_close(sock);
+ return;
+ }
+ }
+
+ /* At this point we know we are talking to a real SMTP server */
+
+ /* Do a HELO command */
+ sprintf(buf, "HELO %s", config.c_fqdn);
+ sock_puts(sock, buf);
+ if (sock_gets(sock, buf) < 0) {
+ *status = 4;
+ strcpy(dsn, "Connection broken during SMTP conversation");
+ sock_close(sock);
+ return;
+ }
+ lprintf(9, "%s\n", buf);
+ if (buf[0] != '2') {
+ if (buf[0] == '4') {
+ *status = 4;
+ strcpy(dsn, &buf[4]);
+ sock_close(sock);
+ return;
+ }
+ else {
+ *status = 5;
+ strcpy(dsn, &buf[4]);
+ sock_close(sock);
+ return;
+ }
+ }
+
+
+ /* HELO succeeded, now try the MAIL From: command */
+ sprintf(buf, "MAIL From: FIX@uncnsrd.mt-kisco.ny.us"); /* FIX */
+ sock_puts(sock, buf);
+ if (sock_gets(sock, buf) < 0) {
+ *status = 4;
+ strcpy(dsn, "Connection broken during SMTP conversation");
+ sock_close(sock);
+ return;
+ }
+ lprintf(9, "%s\n", buf);
+ if (buf[0] != '2') {
+ if (buf[0] == '4') {
+ *status = 4;
+ strcpy(dsn, &buf[4]);
+ sock_close(sock);
+ return;
+ }
+ else {
+ *status = 5;
+ strcpy(dsn, &buf[4]);
+ sock_close(sock);
+ return;
+ }
+ }
+
+
+ /* MAIL succeeded, now try the RCPT To: command */
+ sprintf(buf, "RCPT To: %s", addr);
+ sock_puts(sock, buf);
+ if (sock_gets(sock, buf) < 0) {
+ *status = 4;
+ strcpy(dsn, "Connection broken during SMTP conversation");
+ sock_close(sock);
+ return;
+ }
+ lprintf(9, "%s\n", buf);
+ if (buf[0] != '2') {
+ if (buf[0] == '4') {
+ *status = 4;
+ strcpy(dsn, &buf[4]);
+ sock_close(sock);
+ return;
+ }
+ else {
+ *status = 5;
+ strcpy(dsn, &buf[4]);
+ sock_close(sock);
+ return;
+ }
+ }
+
+
+ /* RCPT succeeded, now try the DATA command */
+ sock_puts(sock, "DATA");
+ if (sock_gets(sock, buf) < 0) {
+ *status = 4;
+ strcpy(dsn, "Connection broken during SMTP conversation");
+ sock_close(sock);
+ return;
+ }
+ lprintf(9, "%s\n", buf);
+ if (buf[0] != '3') {
+ if (buf[0] == '4') {
+ *status = 3;
+ strcpy(dsn, &buf[4]);
+ sock_close(sock);
+ return;
+ }
+ else {
+ *status = 5;
+ strcpy(dsn, &buf[4]);
+ sock_close(sock);
+ return;
+ }
+ }
- *status = 3;
- strcpy(dsn, "smtp_try() is not finished yet");
+ /* If we reach this point, the server is expecting data */
+
+ CtdlRedirectOutput(NULL, sock);
+ CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
+ CtdlRedirectOutput(NULL, -1);
+
+ sock_puts(sock, ".");
+ if (sock_gets(sock, buf) < 0) {
+ *status = 4;
+ strcpy(dsn, "Connection broken during SMTP conversation");
+ sock_close(sock);
+ return;
+ }
+ lprintf(9, "%s\n", buf);
+ if (buf[0] != '2') {
+ if (buf[0] == '4') {
+ *status = 4;
+ strcpy(dsn, &buf[4]);
+ sock_close(sock);
+ return;
+ }
+ else {
+ *status = 5;
+ strcpy(dsn, &buf[4]);
+ sock_close(sock);
+ return;
+ }
+ }
+
+ /* We did it! */
+ strcpy(dsn, &buf[4]);
+ *status = 2;
+
+ sock_puts(sock, "QUIT");
+ sock_gets(sock, buf);
+ lprintf(9, "%s\n", buf);
+ sock_close(sock);
}
+/*
+ * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to remove
+ * all of the completed deliveries from a set of delivery instructions.
+ *
+ * It returns the number of incomplete deliveries remaining.
+ */
+int smtp_purge_completed_deliveries(char *instr) {
+ int i;
+ int lines;
+ int status;
+ char buf[1024];
+ char key[1024];
+ char addr[1024];
+ char dsn[1024];
+ int completed;
+ int incomplete = 0;
+
+ lines = num_tokens(instr, '\n');
+ for (i=0; i<lines; ++i) {
+ extract_token(buf, instr, i, '\n');
+ extract(key, buf, 0);
+ extract(addr, buf, 1);
+ status = extract_int(buf, 2);
+ extract(dsn, buf, 3);
+
+ completed = 0;
+
+ if (
+ (!strcasecmp(key, "local"))
+ || (!strcasecmp(key, "remote"))
+ || (!strcasecmp(key, "ignet"))
+ || (!strcasecmp(key, "room"))
+ ) {
+ if (status == 2) completed = 1;
+ else ++incomplete;
+ }
+
+ if (completed) {
+ remove_token(instr, i, '\n');
+ --i;
+ --lines;
+ }
+ }
+
+ return(incomplete);
+}
+
+
/*
* smtp_do_procmsg()
*
char addr[1024];
char dsn[1024];
long text_msgid = (-1);
+ int incomplete_deliveries_remaining;
msg = CtdlFetchMessage(msgnum);
if (msg == NULL) {
extract_token(buf, instr, i, '\n');
if (num_tokens(buf, '|') < 2) {
lprintf(9, "removing <%s>\n", buf);
- remove_token(instr, i, '|');
+ remove_token(instr, i, '\n');
--lines;
--i;
}
}
if (text_msgid < 0L) {
- lprintf(3, "SMTP: no 'msgid' directive found!\n", msgnum);
+ lprintf(3, "SMTP: no 'msgid' directive found!\n");
phree(instr);
return;
}
--i;
--lines;
lprintf(9, "SMTP: Trying <%s>\n", addr);
- smtp_try(key, addr, &status, dsn);
+ smtp_try(key, addr, &status, dsn, text_msgid);
if (status != 2) {
if (results == NULL) {
results = mallok(1024);
phree(results);
}
- /* Delete the instructions and replace with the updated ones */
- CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
- msg = mallok(sizeof(struct CtdlMessage));
- memset(msg, 0, sizeof(struct CtdlMessage));
- msg->cm_magic = CTDLMESSAGE_MAGIC;
- msg->cm_anon_type = MES_NORMAL;
- msg->cm_format_type = FMT_RFC822;
- msg->cm_fields['M'] = instr;
- CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
- CtdlFreeMessage(msg);
+
+
+ /*
+ * Go through the delivery list, deleting completed deliveries
+ */
+ incomplete_deliveries_remaining =
+ smtp_purge_completed_deliveries(instr);
+
+
+ /*
+ * No delivery instructions remain, so delete both the instructions
+ * message and the message message.
+ */
+ if (incomplete_deliveries_remaining <= 0) {
+ CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
+ CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, text_msgid, NULL);
+ }
+
+
+ /*
+ * Uncompleted delivery instructions remain, so delete the old
+ * instructions and replace with the updated ones.
+ */
+ if (incomplete_deliveries_remaining > 0) {
+ CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgnum, NULL);
+ msg = mallok(sizeof(struct CtdlMessage));
+ memset(msg, 0, sizeof(struct CtdlMessage));
+ msg->cm_magic = CTDLMESSAGE_MAGIC;
+ msg->cm_anon_type = MES_NORMAL;
+ msg->cm_format_type = FMT_RFC822;
+ msg->cm_fields['M'] = malloc(strlen(instr)+256);
+ sprintf(msg->cm_fields['M'],
+ "Content-type: %s\n\n%s\n", SPOOLMIME, instr);
+ phree(instr);
+ CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL, 1);
+ CtdlFreeMessage(msg);
+ }
+
}
-
-
/*****************************************************************************/
/* MODULE INITIALIZATION STUFF */
/*****************************************************************************/
smtp_greeting,
smtp_command_loop);
create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0);
+ CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
return "$Id$";
}