once per minute by any worker thread.
* msgbase.c: removed dependence on nested functions in CtdlOutputMsg() by
replacing them with an API call CtdlRedirectOutput() in sysdep.c, which
can temporarily redirect a session's output to an arbitrary file or socket.
* serv_smtp.c: implemented the purging of messages in the queue for which all
deliveries have been completed.
* serv_smtp.c: removed temporary 'QQQQ' server command and replaced it with
a timer event hook that runs the queue once per minute (this needs to be
made more robust)
$Log$
+Revision 1.458 2000/02/14 04:36:14 ajc
+* sysdep.c: added new event hook type EVT_TIMER. Timer event hooks are called
+ once per minute by any worker thread.
+* msgbase.c: removed dependence on nested functions in CtdlOutputMsg() by
+ replacing them with an API call CtdlRedirectOutput() in sysdep.c, which
+ can temporarily redirect a session's output to an arbitrary file or socket.
+* serv_smtp.c: implemented the purging of messages in the queue for which all
+ deliveries have been completed.
+* serv_smtp.c: removed temporary 'QQQQ' server command and replaced it with
+ a timer event hook that runs the queue once per minute (this needs to be
+ made more robust)
+
Revision 1.457 2000/02/08 21:00:47 ajc
* Implemented the deprecated "LAST" command in POP3. Some clients need it.
* POP3 sessions now set the last-read pointer in Mail>.
Fri Jul 10 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
* Initial CVS import
-
tv.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(housepipe[0], &readfds);
- select(housepipe[0] + 1, &readfds, 0L, 0L, &tv);
+ select(housepipe[0] + 1, &readfds, NULL, NULL, &tv);
if (FD_ISSET(housepipe[0], &readfds)) {
did_something = 1;
}
/*
- * Get a message off disk. (returns om_* values found in msgbase.h)
- *
+ * Callback function for mime parser that wants to display text
*/
-int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */
- int mode, /* how would you like that message? */
- int headers_only, /* eschew the message body? */
- int do_proto, /* do Citadel protocol responses? */
- FILE *outfp,
- int outsock,
- int crlf /* Use CRLF newlines instead of LF? */
-) {
- int a, i, k;
- char buf[1024];
- time_t xtime;
- CIT_UBYTE ch;
- char allkeys[256];
- char display_name[256];
- struct CtdlMessage *TheMessage;
- char *mptr;
- char *nl; /* newline string */
-
- /* buffers needed for RFC822 translation */
- char suser[256];
- char luser[256];
- char snode[256];
- char lnode[256];
- char mid[256];
- /* */
-
-
- /* BEGIN NESTED FUNCTION omprintf() */
- void omprintf(const char *format, ...) {
- va_list arg_ptr;
- char buf[256];
-
- va_start(arg_ptr, format);
- if (vsnprintf(buf, sizeof buf, format, arg_ptr) == -1)
- buf[sizeof buf - 2] = '\n';
- if (outfp != NULL) {
- fwrite(buf, strlen(buf), 1, outfp);
- }
- else if (outsock >= 0) {
- write(outsock, buf, strlen(buf));
- }
- else {
- client_write(buf, strlen(buf));
- }
- va_end(arg_ptr);
- }
- /* END NESTED FUNCTION omprintf() */
-
- /* BEGIN NESTED FUNCTION omfmout() */
- void omfmout(char *mptr) {
- int b, c;
- int real = 0;
- int old = 0;
- CIT_UBYTE ch;
- char aaa[140];
- char buffer[256];
-
- strcpy(aaa, "");
- old = 255;
- strcpy(buffer, "");
- c = 1; /* c is the current pos */
-
-FMTA: ch = *mptr++;
- old = real;
- real = ch;
- if (ch <= 0)
- goto FMTEND;
-
- if (((ch == 13) || (ch == 10)) && (old != 13) && (old != 10))
- ch = 32;
- if (((old == 13) || (old == 10)) && (isspace(real))) {
- omprintf("%s", nl);
- c = 1;
- }
- if (ch > 126)
- goto FMTA;
-
- if (ch > 32) {
- if (((strlen(aaa) + c) > (75)) && (strlen(aaa) > (75))) {
- omprintf("%s%s", nl, aaa);
- c = strlen(aaa);
- aaa[0] = 0;
- }
- b = strlen(aaa);
- aaa[b] = ch;
- aaa[b + 1] = 0;
- }
- if (ch == 32) {
- if ((strlen(aaa) + c) > (75)) {
- omprintf("%s", nl);
- c = 1;
- }
- omprintf("%s ", aaa);
- ++c;
- c = c + strlen(aaa);
- strcpy(aaa, "");
- goto FMTA;
- }
- if ((ch == 13) || (ch == 10)) {
- omprintf("%s%s", aaa, nl);
- c = 1;
- strcpy(aaa, "");
- goto FMTA;
- }
- goto FMTA;
-
-FMTEND: omprintf("%s%s", aaa, nl);
- }
- /* END NESTED FUNCTION omfmout() */
-
- /* BEGIN NESTED FUNCTION fixed_output() */
- /*
- * Callback function for mime parser that wants to display text
- */
- void fixed_output(char *name, char *filename, char *partnum, char *disp,
- void *content, char *cbtype, size_t length)
+void fixed_output(char *name, char *filename, char *partnum, char *disp,
+ void *content, char *cbtype, size_t length)
{
char *ptr;
char *wptr;
size_t wlen;
+ CIT_UBYTE ch;
if (!strcasecmp(cbtype, "multipart/alternative")) {
strcpy(ma->prefix, partnum);
wptr = content;
while (wlen--) {
ch = *wptr++;
- if (ch==10) omprintf("%s", nl);
- else omprintf("%c", ch);
+ if (ch==10) cprintf("\r\n");
+ else cprintf("%c", ch);
}
}
else if (!strcasecmp(cbtype, "text/html")) {
wptr = ptr;
while (wlen--) {
ch = *wptr++;
- if (ch==10) omprintf("%s", nl);
- else omprintf("%c", ch);
+ if (ch==10) cprintf("\r\n");
+ else cprintf("%c", ch);
}
phree(ptr);
}
else if (strncasecmp(cbtype, "multipart/", 10)) {
- omprintf("Part %s: %s (%s) (%d bytes)%s",
- partnum, filename, cbtype, length, nl);
+ cprintf("Part %s: %s (%s) (%d bytes)\r\n",
+ partnum, filename, cbtype, length);
}
}
- /* END NESTED FUNCTION fixed_output() */
+
+/*
+ * Get a message off disk. (returns om_* values found in msgbase.h)
+ *
+ */
+int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */
+ int mode, /* how would you like that message? */
+ int headers_only, /* eschew the message body? */
+ int do_proto, /* do Citadel protocol responses? */
+ int crlf /* Use CRLF newlines instead of LF? */
+) {
+ int a, i, k;
+ char buf[1024];
+ time_t xtime;
+ CIT_UBYTE ch;
+ char allkeys[256];
+ char display_name[256];
+ struct CtdlMessage *TheMessage;
+ char *mptr;
+ char *nl; /* newline string */
+
+ /* buffers needed for RFC822 translation */
+ char suser[256];
+ char luser[256];
+ char snode[256];
+ char lnode[256];
+ char mid[256];
+ /* */
lprintf(7, "CtdlOutputMsg() msgnum=%ld, mode=%d\n",
msg_num, mode);
strcpy(snode, NODENAME);
strcpy(lnode, HUMANNODE);
if (mode == MT_RFC822) {
- omprintf("X-UIDL: %ld%s", msg_num, nl);
+ cprintf("X-UIDL: %ld%s", msg_num, nl);
for (i = 0; i < 256; ++i) {
if (TheMessage->cm_fields[i]) {
mptr = TheMessage->cm_fields[i];
if (i == 'A') {
strcpy(luser, mptr);
} else if (i == 'P') {
- omprintf("Path: %s%s", mptr, nl);
+ cprintf("Path: %s%s", mptr, nl);
for (a = 0; a < strlen(mptr); ++a) {
if (mptr[a] == '!') {
strcpy(mptr, &mptr[a + 1]);
}
strcpy(suser, mptr);
} else if (i == 'U')
- omprintf("Subject: %s%s", mptr, nl);
+ cprintf("Subject: %s%s", mptr, nl);
else if (i == 'I')
strcpy(mid, mptr);
else if (i == 'H')
strcpy(lnode, mptr);
else if (i == 'O')
- omprintf("X-Citadel-Room: %s%s",
+ cprintf("X-Citadel-Room: %s%s",
mptr, nl);
else if (i == 'N')
strcpy(snode, mptr);
else if (i == 'R')
- omprintf("To: %s%s", mptr, nl);
+ cprintf("To: %s%s", mptr, nl);
else if (i == 'T') {
xtime = atol(mptr);
- omprintf("Date: %s", asctime(localtime(&xtime)));
+ cprintf("Date: %s", asctime(localtime(&xtime)));
}
}
}
if (!strcasecmp(snode, NODENAME)) {
strcpy(snode, FQDN);
}
- omprintf("Message-ID: <%s@%s>%s", mid, snode, nl);
+ cprintf("Message-ID: <%s@%s>%s", mid, snode, nl);
PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG);
- omprintf("From: %s@%s (%s)%s", suser, snode, luser, nl);
- omprintf("Organization: %s%s", lnode, nl);
+ cprintf("From: %s@%s (%s)%s", suser, snode, luser, nl);
+ cprintf("Organization: %s%s", lnode, nl);
}
/* end header processing loop ... at this point, we're in the text */
*/
while (ch=*(mptr++), ch!=0) {
if (ch==13) ;
- else if (ch==10) omprintf("%s", nl);
- else omprintf("%c", ch);
+ else if (ch==10) cprintf("%s", nl);
+ else cprintf("%c", ch);
}
if (do_proto) cprintf("000\n");
CtdlFreeMessage(TheMessage);
if (mode == MT_CITADEL)
if (do_proto) cprintf("text\n");
if (mode == MT_RFC822) {
- omprintf("%s", nl);
+ cprintf("%s", nl);
}
/* If the format type on disk is 1 (fixed-format), then we want
if (ch == 13)
ch = 10;
if ((ch == 10) || (strlen(buf) > 250)) {
- omprintf("%s%s", buf, nl);
+ cprintf("%s%s", buf, nl);
strcpy(buf, "");
} else {
buf[strlen(buf) + 1] = 0;
}
}
if (strlen(buf) > 0)
- omprintf("%s%s", buf, nl);
+ cprintf("%s%s", buf, nl);
}
/* If the message on disk is format 0 (Citadel vari-format), we
* message to the reader's screen width.
*/
if (TheMessage->cm_format_type == FMT_CITADEL) {
- omfmout(mptr);
+ memfmout(80, mptr, 0);
}
/* If the message on disk is format 4 (MIME), we've gotta hand it
msgid = extract_long(cmdbuf, 0);
headers_only = extract_int(cmdbuf, 1);
- CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, NULL, -1, 0);
+ CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, 0);
return;
}
msgid = extract_long(cmdbuf, 0);
headers_only = extract_int(cmdbuf, 1);
- CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, NULL, -1, 1);
+ CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, 1);
}
long msgid;
msgid = extract_long(cmdbuf, 0);
- CtdlOutputMsg(msgid, MT_MIME, 0, 1, NULL, -1, 0);
+ CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0);
}
/*
msgid = extract_long(cmdbuf, 0);
extract(desired_section, cmdbuf, 1);
- CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, NULL, -1, 1);
+ CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1);
}
FILE *fp, *tempfp;
char filename[PATH_MAX];
char cmdbuf[256];
- int ch;
+ char ch;
struct quickroom qrbuf;
char roomname[ROOMNAMELEN];
struct CtdlMessage *msg;
int mode, /* how would you like that message? */
int headers_only, /* eschew the message body? */
int do_proto, /* do Citadel protocol responses? */
- FILE *outfp,
- int outsock,
int crlf);
POP3->msgs[POP3->num_msgs-1].deleted = 0;
fp = tmpfile();
POP3->msgs[POP3->num_msgs-1].temp = fp;
- CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, fp, 0, 1);
+
+ CtdlRedirectOutput(fp, -1);
+ CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
+ CtdlRedirectOutput(NULL, -1);
+
POP3->msgs[POP3->num_msgs-1].rfc822_length = ftell(fp);
}
#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"
}
/* If we reach this point, the server is expecting data */
- CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, NULL, sock, 1);
+
+ CtdlRedirectOutput(NULL, sock);
+ CtdlOutputMsg(msgnum, MT_RFC822, 0, 0, 1);
+ CtdlRedirectOutput(NULL, -1);
+
sock_puts(sock, ".");
if (sock_gets(sock, buf) < 0) {
*status = 4;
+/*
+ * 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) {
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'] = 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);
+
+
+ /*
+ * 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);
+ }
+
}
}
-/**** FIX temporary hack to run the queue *****/
-void cmd_qqqq(char *argbuf) {
- smtp_do_queue();
- cprintf("%d ok\n", OK);
-}
-
-
/*****************************************************************************/
/* MODULE INITIALIZATION STUFF */
CtdlRegisterServiceHook(SMTP_PORT,
smtp_greeting,
smtp_command_loop);
-
- /**** FIX ... temporary hack to run the queue ******/
- CtdlRegisterProtoHook(cmd_qqqq, "QQQQ", "run the queue");
-
create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0);
+ CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
return "$Id$";
}
char dl_is_net;
char upload_type;
+ /* Redirect this session's output to somewhere else? */
+ FILE *redirect_fp;
+ int redirect_sock;
+
+ /* A linked list of all express messages sent to us. */
struct ExpressMessage *FirstExpressMessage;
+ /* Masquerade... */
char fake_username[32]; /* Fake username <bc> */
char fake_postname[32]; /* Fake postname <bc> */
char fake_hostname[25]; /* Name of the fake hostname <bc> */
char fake_roomname[ROOMNAMELEN]; /* Name of the fake room <bc> */
- struct CtdlSessData *FirstSessData; /* Allocated session data */
+ /* Dynamically allocated session data */
+ struct CtdlSessData *FirstSessData;
};
typedef struct CitContext t_context;
#define EVT_RWHO 7 /* An RWHO command is being executed */
-
-
+#define EVT_TIMER 50 /* Timer events are called once per minute
+ and are not tied to any session */
/*
* UserFunctionHook extensions are used for any type of hook which implements
fd_set masterfds; /* Master sockets etc. */
int masterhighest;
+time_t last_timer = 0L; /* Last timer hook processing */
+
/*
* lprintf() ... Write logging information
{
int bytes_written = 0;
int retval;
+ int sock;
+
+ if (CC->redirect_fp != NULL) {
+ fwrite(buf, nbytes, 1, CC->redirect_fp);
+ return;
+ }
+
+ if (CC->redirect_sock > 0) {
+ sock = CC->redirect_sock; /* and continue below... */
+ }
+ else {
+ sock = CC->client_socket;
+ }
+
while (bytes_written < nbytes) {
- retval = write(CC->client_socket, &buf[bytes_written],
+ retval = write(sock, &buf[bytes_written],
nbytes - bytes_written);
if (retval < 1) {
lprintf(2, "client_write() failed: %s\n",
strerror(errno));
- CC->kill_me = 1;
+ if (sock == CC->client_socket) CC->kill_me = 1;
return;
}
bytes_written = bytes_written + retval;
}
-
+
+
+
+/*
+ * Redirect a session's output to a file or socket.
+ * This function may be called with a file handle *or* a socket (but not
+ * both). Call with neither to return output to its normal client socket.
+ */
+void CtdlRedirectOutput(FILE *fp, int sock) {
+
+ if (fp != NULL) CC->redirect_fp = fp;
+ else CC->redirect_fp = NULL;
+
+ if (sock > 0) CC->redirect_sock = sock;
+ else CC->redirect_sock = (-1);
+
+}
+
+
+/*
+ * masterCC is the context we use when not attached to a session. This
+ * function initializes it.
+ */
+void InitializeMasterCC(void) {
+ memset(&masterCC, 0, sizeof(struct CitContext));
+ masterCC.internal_pgm = 1;
+}
+
+
/*
* Here's where it all begins.
/* specify default port name and trace file */
strcpy(tracefile, "");
+ /* initialize the master context */
+ InitializeMasterCC();
+
/* parse command-line arguments */
for (a=1; a<argc; ++a) {
struct sockaddr_in fsin; /* Data for master socket */
int alen; /* Data for master socket */
int ssock; /* Descriptor for client socket */
+ struct timeval tv;
++num_threads;
+
+ tv.tv_sec = 60; /* wake up every minute if no input */
+ tv.tv_usec = 0;
+
while (!time_to_die) {
/*
}
end_critical_section(S_SESSION_TABLE);
- retval = select(highest + 1, &readfds, NULL, NULL, NULL);
+ retval = select(highest + 1, &readfds, NULL, NULL, &tv);
/* Now figure out who made this select() unblock.
* First, check for an error or exit condition.
}
dead_session_purge();
+ if ((time(NULL) - last_timer) > 60L) {
+ last_timer = time(NULL);
+ PerformSessionHooks(EVT_TIMER);
+ }
}
/* If control reaches this point, the server is shutting down */
int convert_login (char *NameToConvert);
void worker_thread (void);
inline void become_session(struct CitContext *which_con);
+void CtdlRedirectOutput(FILE *fp, int sock);
extern int num_sessions;