/* $Id$ */
-#define SMTP_PORT 2525
-
#include "sysdep.h"
#include <stdlib.h>
#include <unistd.h>
#include "msgbase.h"
#include "tools.h"
#include "internet_addressing.h"
+#include "genstamp.h"
+
-struct citsmtp {
+struct citsmtp { /* Information about the current session */
int command_state;
struct usersupp vrfy_buffer;
int vrfy_count;
char vrfy_match[256];
char from[256];
+ int number_of_recipients;
+ int delivery_mode;
};
-enum {
+
+enum { /* Command states for login authentication */
smtp_command,
smtp_user,
smtp_password
};
-#define SMTP ((struct citsmtp *)CtdlGetUserData(SYM_SMTP))
+enum { /* Delivery modes */
+ smtp_deliver_local,
+ smtp_deliver_remote
+};
-long SYM_SMTP;
+#define SMTP ((struct citsmtp *)CtdlGetUserData(SYM_SMTP))
+#define SMTP_RECP ((char *)CtdlGetUserData(SYM_SMTP_RECP))
+long SYM_SMTP;
+long SYM_SMTP_RECP;
/*
* Here's where our SMTP session begins its happy day.
*/
void smtp_greeting(void) {
- strcpy(CC->cs_clientname, "Citadel SMTP");
+ strcpy(CC->cs_clientname, "SMTP session");
+ CC->internal_pgm = 1;
+ CC->cs_flags |= CS_STEALTH;
CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp));
+ CtdlAllocUserData(SYM_SMTP_RECP, 256);
+ sprintf(SMTP_RECP, "%s", "");
cprintf("220 Welcome to the Citadel/UX ESMTP server at %s\n",
config.c_fqdn);
*/
void smtp_help(void) {
cprintf("214-Here's the frequency, Kenneth:\n");
+ cprintf("214- DATA\n");
cprintf("214- EHLO\n");
cprintf("214- EXPN\n");
cprintf("214- HELO\n");
cprintf("214- MAIL\n");
cprintf("214- NOOP\n");
cprintf("214- QUIT\n");
+ cprintf("214- RCPT\n");
cprintf("214- RSET\n");
cprintf("214- VRFY\n");
cprintf("214 I could tell you more, but then I'd have to kill you.\n");
if (CtdlTryPassword(password) == pass_ok) {
cprintf("235 Authentication successful.\n");
lprintf(9, "SMTP auth login successful\n");
+ CC->internal_pgm = 0;
+ CC->cs_flags &= ~CS_STEALTH;
}
else {
cprintf("500 Authentication failed.\n");
* the user only sends email from his/her own address.
*/
if (CC->logged_in) {
- lprintf(9, "Me-checking <%s>\n", SMTP->from);
cvt = convert_internet_address(user, node, SMTP->from);
lprintf(9, "cvt=%d, citaddr=<%s@%s>\n", cvt, user, node);
if ( (cvt != 0) || (strcasecmp(user, CC->usersupp.fullname))) {
}
}
+ /* Otherwise, make sure outsiders aren't trying to forge mail from
+ * this system.
+ */
+ 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 */
+ cprintf("550 You must log in to send mail from %s\n",
+ config.c_fqdn);
+ strcpy(SMTP->from, "");
+ return;
+ }
+ }
+
cprintf("250 Sender ok. Groovy.\n");
}
+/*
+ * Implements the "RCPT To:" command
+ */
+void smtp_rcpt(char *argbuf) {
+ int cvt;
+ char user[256];
+ char node[256];
+ char recp[256];
+ int is_spam = 0; /* FIX implement anti-spamming */
+
+ if (strlen(SMTP->from) == 0) {
+ cprintf("503 MAIL first, then RCPT. Duh.\n");
+ return;
+ }
+
+ if (strncasecmp(argbuf, "To:", 3)) {
+ cprintf("501 Syntax error\n");
+ return;
+ }
+
+ strcpy(recp, &argbuf[3]);
+ striplt(recp);
+ alias(recp);
+
+ cvt = convert_internet_address(user, node, recp);
+ sprintf(recp, "%s@%s", user, node);
+
+
+ switch(cvt) {
+ case rfc822_address_locally_validated:
+ cprintf("250 %s is a valid recipient.\n", user);
+ ++SMTP->number_of_recipients;
+ CtdlReallocUserData(SYM_SMTP_RECP,
+ strlen(SMTP_RECP) + 1024 );
+ strcat(SMTP_RECP, "local|");
+ strcat(SMTP_RECP, user);
+ strcat(SMTP_RECP, "|0\n");
+ return;
+
+ case rfc822_no_such_user:
+ cprintf("550 %s: no such user\n", recp);
+ return;
+
+ case rfc822_address_invalid:
+ if (is_spam) {
+ cprintf("551 Away with thee, spammer!\n");
+ }
+ else {
+ cprintf("250 Remote recipient %s ok\n", recp);
+ ++SMTP->number_of_recipients;
+ CtdlReallocUserData(SYM_SMTP_RECP,
+ strlen(SMTP_RECP) + 1024 );
+ strcat(SMTP_RECP, "remote|");
+ strcat(SMTP_RECP, recp);
+ strcat(SMTP_RECP, "|0\n");
+ return;
+ }
+ return;
+ }
+
+ cprintf("599 Unknown error\n");
+}
+
+
+
+
+
+/*
+ * Back end for smtp_data() ... this does the actual delivery of the message
+ * Returns 0 on success, nonzero on failure
+ */
+int smtp_message_delivery(struct CtdlMessage *msg) {
+ char user[256];
+ char node[256];
+ char name[256];
+ int successful_saves = 0;
+ int failed_saves = 0;
+ long msgid = (-1L);
+
+ lprintf(9, "smtp_message_delivery() called\n");
+
+ /* Fill in 'from' fields with envelope information if missing */
+ process_rfc822_addr(SMTP->from, user, node, name);
+ if (msg->cm_fields['A']==NULL) msg->cm_fields['A'] = strdoop(user);
+ if (msg->cm_fields['N']==NULL) msg->cm_fields['N'] = strdoop(node);
+ if (msg->cm_fields['H']==NULL) msg->cm_fields['H'] = strdoop(name);
+
+ /* Save the message in the queue */
+ msgid = CtdlSaveMsg(msg,
+ "",
+ SMTP_SPOOLOUT_ROOM,
+ MES_LOCAL,
+ 1);
+ ++successful_saves;
+
+ /* FIX go thru each local user and save to boxes
+ then stuff remote users in queue list
+ then delete from queue if num remote users is 0
+ */
+
+ return(failed_saves);
+}
+
+
+
+/*
+ * Implements the DATA command
+ */
+void smtp_data(void) {
+ char *body;
+ struct CtdlMessage *msg;
+ int retval;
+ char nowstamp[256];
+
+ if (strlen(SMTP->from) == 0) {
+ cprintf("503 Need MAIL command first.\n");
+ return;
+ }
+
+ if (SMTP->number_of_recipients < 1) {
+ cprintf("503 Need RCPT command first.\n");
+ return;
+ }
+
+ cprintf("354 Transmit message now; terminate with '.' by itself\n");
+
+ 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",
+ config.c_fqdn,
+ nowstamp);
+
+ body = CtdlReadMessageBody(".", config.c_maxmsglen, body);
+ if (body == NULL) {
+ cprintf("550 Unable to save message text: internal error.\n");
+ return;
+ }
+
+ lprintf(9, "Converting message...\n");
+ msg = convert_internet_message(body);
+
+ /* If the user is locally authenticated, FORCE the From: header to
+ * show up as the real sender
+ */
+ if (CC->logged_in) {
+ if (msg->cm_fields['A'] != NULL) phree(msg->cm_fields['A']);
+ if (msg->cm_fields['N'] != NULL) phree(msg->cm_fields['N']);
+ if (msg->cm_fields['H'] != NULL) phree(msg->cm_fields['H']);
+ msg->cm_fields['A'] = strdoop(CC->usersupp.fullname);
+ msg->cm_fields['N'] = strdoop(config.c_nodename);
+ msg->cm_fields['H'] = strdoop(config.c_humannode);
+ }
+
+ retval = smtp_message_delivery(msg);
+ CtdlFreeMessage(msg);
+
+ if (!retval) {
+ cprintf("250 Message accepted for delivery.\n");
+ }
+ else {
+ cprintf("550 Internal error.\n");
+ }
+}
+
+
+
+
/*
* Main command loop for SMTP sessions.
*/
smtp_auth(&cmdbuf[5]);
}
+ else if (!strncasecmp(cmdbuf, "DATA", 4)) {
+ smtp_data();
+ }
+
else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
smtp_hello(&cmdbuf[5], 1);
}
return;
}
+ else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
+ smtp_rcpt(&cmdbuf[5]);
+ }
+
else if (!strncasecmp(cmdbuf, "RSET", 4)) {
smtp_rset();
}
}
else {
- cprintf("500 I'm afraid I can't do that, Dave.\n");
+ cprintf("502 I'm afraid I can't do that, Dave.\n");
}
}
-
char *Dynamic_Module_Init(void)
{
SYM_SMTP = CtdlGetDynamicSymbol();
+ SYM_SMTP_RECP = CtdlGetDynamicSymbol();
CtdlRegisterServiceHook(SMTP_PORT,
smtp_greeting,
smtp_command_loop);
+ create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0);
return "$Id$";
}
+