aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--driver.c422
1 files changed, 422 insertions, 0 deletions
diff --git a/driver.c b/driver.c
new file mode 100644
index 00000000..a2288654
--- /dev/null
+++ b/driver.c
@@ -0,0 +1,422 @@
+/* Copyright 1996 by Eric S. Raymond
+ * All rights reserved.
+ * For license terms, see the file COPYING in this directory.
+ */
+
+/***********************************************************************
+ module: driver.c
+ project: popclient
+ programmer: Eric S. Raymond
+ description: Generic driver for mail fetch method protocols
+
+ ***********************************************************************/
+
+#include <config.h>
+#include <varargs.h>
+
+#include <stdio.h>
+#if defined(STDC_HEADERS)
+#include <string.h>
+#endif
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#include <sys/time.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "socket.h"
+#include "popclient.h"
+
+static struct method *protocol;
+
+char tag[TAGLEN];
+static int tagnum;
+#define GENSYM (sprintf(tag, "a%04d", ++tagnum), tag)
+
+/*********************************************************************
+ function: do_protocol
+ description: retrieve messages from the specified mail server
+ using a given set of methods
+
+ arguments:
+ queryctl fully-specified options (i.e. parsed, defaults invoked,
+ etc).
+ proto protocol method pointer
+
+ return value: exit code from the set of PS_.* constants defined in
+ popclient.h
+ calls:
+ globals: reads outlevel.
+ *********************************************************************/
+
+int do_protocol(queryctl, proto)
+struct hostrec *queryctl;
+struct method *proto;
+{
+ int ok, len;
+ int mboxfd;
+ char buf [POPBUFSIZE];
+ int socket;
+ int first,number,count;
+
+ tagnum = 0;
+ protocol = proto;
+
+ /* open stdout or the mailbox, locking it if it is a folder */
+ if (queryctl->output == TO_FOLDER || queryctl->output == TO_STDOUT)
+ if ((mboxfd = openuserfolder(queryctl)) < 0)
+ return(PS_IOERR);
+
+ /* open the socket */
+ if ((socket = Socket(queryctl->servername,protocol->port)) < 0) {
+ perror("do_protocol: socket");
+ ok = PS_SOCKET;
+ goto closeUp;
+ }
+
+ /* accept greeting message from IMAP server */
+ ok = imap_ok(buf,socket);
+ if (ok != 0) {
+ if (ok != PS_SOCKET)
+ gen_transact(socket, protocol->exit_cmd);
+ close(socket);
+ goto closeUp;
+ }
+
+ /* print the greeting */
+ if (outlevel > O_SILENT && outlevel < O_VERBOSE)
+ fprintf(stderr,"%s greeting: %s\n", protocol->name, buf);
+
+ /* try to get authorized to fetch mail */
+ ok = (protocol->getauth)(socket, queryctl, buf);
+ if (ok == PS_ERROR)
+ ok = PS_AUTHFAIL;
+ if (ok != 0)
+ goto cleanUp;
+
+ /* compute count and first */
+ if ((*protocol->getrange)(socket, queryctl, &count, &first) != 0)
+ goto cleanUp;
+
+ /* show them how many messages we'll be downloading */
+ if (outlevel > O_SILENT && outlevel < O_VERBOSE)
+ if (first > 1)
+ fprintf(stderr,"%d messages in folder, %d new messages.\n",
+ count, count - first + 1);
+ else
+ fprintf(stderr,"%d %smessages in folder.\n", count, ok ? "" : "new ");
+
+ if (count > 0) {
+ for (number = queryctl->flush ? 1 : first; number<=count; number++) {
+
+ char *cp;
+
+ /* open the mail pipe if we're using an MDA */
+ if (queryctl->output == TO_MDA
+ && (queryctl->fetchall || number >= first)) {
+ ok = (mboxfd = openmailpipe(queryctl)) < 0 ? -1 : 0;
+ if (ok != 0)
+ goto cleanUp;
+ }
+
+ if (queryctl->flush && number < first && !queryctl->fetchall)
+ ok = 0; /* no command to send here, will delete message below */
+ else
+ {
+ (*protocol->fetch)(socket, number, linelimit, &len);
+ if (outlevel == O_VERBOSE)
+ if (protocol->delimited)
+ fprintf(stderr,"fetching message %d (delimited)\n",number);
+ else
+ fprintf(stderr,"fetching message %d (%d bytes)\n",number,len);
+ ok = gen_readmsg(socket,mboxfd,len,protocol->delimited,
+ queryctl->servername,
+ queryctl->output == TO_MDA,
+ queryctl->rewrite);
+ if (protocol->trail)
+ (*protocol->trail)(socket, queryctl, number);
+ if (ok != 0)
+ goto cleanUp;
+ }
+
+ /* maybe we delete this message now? */
+ if ((number < first && queryctl->flush) || !queryctl->keep) {
+ if (outlevel > O_SILENT && outlevel < O_VERBOSE)
+ fprintf(stderr,"flushing message %d\n", number);
+ else
+ ;
+ ok = gen_transact(socket, protocol->delete_cmd, number);
+ if (ok != 0)
+ goto cleanUp;
+ }
+
+ /* close the mail pipe, we'll reopen before next message */
+ if (queryctl->output == TO_MDA
+ && (queryctl->fetchall || number >= first)) {
+ ok = closemailpipe(mboxfd);
+ if (ok != 0)
+ goto cleanUp;
+ }
+ }
+
+ /* remove all messages flagged for deletion */
+ if (!queryctl->keep && protocol->expunge_cmd)
+ {
+ ok = gen_transact(socket, protocol->expunge_cmd);
+ if (ok != 0)
+ goto cleanUp;
+ }
+
+ ok = gen_transact(socket, protocol->exit_cmd);
+ if (ok == 0)
+ ok = PS_SUCCESS;
+ close(socket);
+ goto closeUp;
+ }
+ else {
+ ok = gen_transact(socket, protocol->exit_cmd);
+ if (ok == 0)
+ ok = PS_NOMAIL;
+ close(socket);
+ goto closeUp;
+ }
+
+cleanUp:
+ if (ok != 0 && ok != PS_SOCKET)
+ gen_transact(socket, protocol->exit_cmd);
+
+closeUp:
+ if (queryctl->output == TO_FOLDER)
+ if (closeuserfolder(mboxfd) < 0 && ok == 0)
+ ok = PS_IOERR;
+
+ if (ok == PS_IOERR || ok == PS_SOCKET)
+ perror("do_protocol: cleanUp");
+
+ return(ok);
+}
+
+/*********************************************************************
+ function: gen_send
+ description: Assemble command in print style and send to the server
+
+ arguments:
+ socket socket to which the server is connected.
+ fmt printf-style format
+
+ return value: none.
+ calls: SockPuts.
+ globals: reads outlevel.
+ *********************************************************************/
+
+void gen_send(socket, fmt, va_alist)
+int socket;
+const char *fmt;
+va_dcl {
+
+ char buf [POPBUFSIZE];
+ va_list ap;
+
+ if (protocol->tagged)
+ (void) sprintf(buf, "%s ", GENSYM);
+ else
+ buf[0] = '\0';
+
+ va_start(ap);
+ vsprintf(buf + strlen(buf), fmt, ap);
+ va_end(ap);
+
+ SockPuts(socket, buf);
+
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"> %s\n", buf);
+}
+
+/*********************************************************************
+ function: gen_transact
+ description: Assemble command in print style and send to the server.
+ then accept a protocol-dependent response.
+
+ arguments:
+ socket socket to which the server is connected.
+ fmt printf-style format
+
+ return value: none.
+ calls: SockPuts, imap_ok.
+ globals: reads outlevel.
+ *********************************************************************/
+
+int gen_transact(socket, fmt, va_alist)
+int socket;
+const char *fmt;
+va_dcl {
+
+ int ok;
+ char buf [POPBUFSIZE];
+ va_list ap;
+
+ if (protocol->tagged)
+ (void) sprintf(buf, "%s ", GENSYM);
+ else
+ buf[0] = '\0';
+
+ va_start(ap);
+ vsprintf(buf + strlen(buf), fmt, ap);
+ va_end(ap);
+
+ SockPuts(socket, buf);
+
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"> %s\n", buf);
+
+ ok = (protocol->parse_response)(buf,socket);
+ if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
+ fprintf(stderr,"%s\n",buf);
+
+ return(ok);
+}
+
+/*********************************************************************
+ function: gen_readmsg
+ description: Read the message content
+
+ as described in RFC 1225.
+ arguments:
+ socket ... to which the server is connected.
+ mboxfd open file descriptor to which the retrieved message will
+ be written.
+ len length of text
+ pophost name of the POP host
+ topipe true if we're writing to the system mailbox pipe.
+
+ return value: zero if success else PS_* return code.
+ calls: SockGets.
+ globals: reads outlevel.
+ *********************************************************************/
+
+int gen_readmsg (socket,mboxfd,len,delimited,pophost,topipe,rewrite)
+int socket;
+int mboxfd;
+long len;
+int delimited;
+char *pophost;
+int topipe;
+int rewrite;
+{
+ char buf [MSGBUFSIZE];
+ char *bufp;
+ char savec;
+ char fromBuf[MSGBUFSIZE];
+ int n;
+ int needFrom;
+ int inheaders;
+ int lines,sizeticker;
+ time_t now;
+ /* This keeps the retrieved message count for display purposes */
+ static int msgnum = 0;
+
+ /* set up for status message if outlevel allows it */
+ if (outlevel > O_SILENT && outlevel < O_VERBOSE) {
+ fprintf(stderr,"reading message %d",++msgnum);
+ /* won't do the '...' if retrieved messages are being sent to stdout */
+ if (mboxfd == 1)
+ fputs(".\n",stderr);
+ else
+ ;
+ }
+ else
+ ;
+
+ /* read the message content from the server */
+ inheaders = 1;
+ lines = 0;
+ sizeticker = MSGBUFSIZE;
+ while (delimited || len > 0) {
+ if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
+ return(PS_SOCKET);
+ len -= n;
+ bufp = buf;
+ if (buf[0] == '\r' || buf[0] == '\n')
+ inheaders = 0;
+ if (*bufp == '.') {
+ bufp++;
+ if (delimited && *bufp == 0)
+ break; /* end of message */
+ }
+ strcat(bufp,"\n");
+
+ /* Check for Unix 'From' header, and add a bogus one if it's not
+ present -- only if not using an MDA.
+ XXX -- should probably parse real From: header and use its
+ address field instead of bogus 'POPmail' string.
+ */
+ if (!topipe && lines == 0) {
+ if (strlen(bufp) >= strlen("From ")) {
+ savec = *(bufp + 5);
+ *(bufp + 5) = 0;
+ needFrom = strcmp(bufp,"From ") != 0;
+ *(bufp + 5) = savec;
+ }
+ else
+ needFrom = 1;
+ if (needFrom) {
+ now = time(NULL);
+ sprintf(fromBuf,"From POPmail %s",ctime(&now));
+ if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) {
+ perror("gen_readmsg: write");
+ return(PS_IOERR);
+ }
+ }
+ }
+
+ /*
+ * Edit some headers so that replies will work properly.
+ */
+ if (inheaders && rewrite)
+ reply_hack(bufp, pophost);
+
+ /* write this line to the file */
+ if (write(mboxfd,bufp,strlen(bufp)) < 0) {
+ perror("gen_readmsg: write");
+ return(PS_IOERR);
+ }
+
+ sizeticker -= strlen(bufp);
+ if (sizeticker <= 0) {
+ if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
+ fputc('.',stderr);
+ sizeticker = MSGBUFSIZE;
+ }
+ lines++;
+ }
+
+ if (!topipe) {
+ /* The server may not write the extra newline required by the Unix
+ mail folder format, so we write one here just in case */
+ if (write(mboxfd,"\n",1) < 0) {
+ perror("gen_readmsg: write");
+ return(PS_IOERR);
+ }
+ }
+ else {
+ /* The mail delivery agent may require a terminator. Write it if
+ it has been defined */
+#ifdef BINMAIL_TERM
+ if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
+ perror("gen_readmsg: write");
+ return(PS_IOERR);
+ }
+#endif
+ }
+
+ /* finish up display output */
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"(%d lines of message content)\n",lines);
+ else if (outlevel > O_SILENT && mboxfd != 1)
+ fputs(".\n",stderr);
+ else
+ ;
+ return(0);
+}