aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>1996-08-27 14:54:36 +0000
committerEric S. Raymond <esr@thyrsus.com>1996-08-27 14:54:36 +0000
commit46170e9ee247b92505c9e3270449b5f05705d176 (patch)
tree9d02e38029f43f71a7d91e3f9f3baa867011bcf9
parentad31c35c7c40b4897881bdf4376bd8d89dc09630 (diff)
downloadfetchmail-46170e9ee247b92505c9e3270449b5f05705d176.tar.gz
fetchmail-46170e9ee247b92505c9e3270449b5f05705d176.tar.bz2
fetchmail-46170e9ee247b92505c9e3270449b5f05705d176.zip
Genericized version of POP3 works.
svn path=/trunk/; revision=70
-rw-r--r--imap.c359
1 files changed, 303 insertions, 56 deletions
diff --git a/imap.c b/imap.c
index eeb115f0..5e4688f3 100644
--- a/imap.c
+++ b/imap.c
@@ -31,13 +31,15 @@
#ifdef HAVE_PROTOTYPES
/* prototypes for internal functions */
-int generic_ok (char *buf, int socket);
-void generic_send ();
-int transact ();
-int generic_readmsg (int socket, int mboxfd, int len,
+int gen_ok (char *buf, int socket);
+void gen_send ();
+int gen_transact ();
+int gen_readmsg (int socket, int mboxfd, long len, int delimited,
char *host, int topipe, int rewrite);
#endif
+#define DELIMITED 99999L
+
#define TAGLEN 5
static char tag[TAGLEN];
static int tagnum;
@@ -45,11 +47,12 @@ static int tagnum;
static int count, first;
-typedef struct
+struct method
{
char *name; /* protocol name */
int port; /* service port */
int tagged; /* if true, generate & expect command tags */
+ int delimited; /* if true, accept "." message delimiter */
int (*parse_response)(); /* response_parsing function */
int (*getauth)(); /* authorization fetcher */
int (*getrange)(); /* get message range to fetch */
@@ -58,8 +61,233 @@ typedef struct
char *delete_cmd; /* delete command */
char *expunge_cmd; /* expunge command */
char *exit_cmd; /* exit command */
+};
+
+/*********************************************************************
+
+ Method declarations for POP3
+
+ *********************************************************************/
+
+int pop3_ok (argbuf,socket)
+/* parse command response */
+char *argbuf;
+int socket;
+{
+ int ok;
+ char buf [POPBUFSIZE];
+ char *bufp;
+
+ if (SockGets(socket, buf, sizeof(buf)) >= 0) {
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"%s\n",buf);
+
+ bufp = buf;
+ if (*bufp == '+' || *bufp == '-')
+ bufp++;
+ else
+ return(PS_PROTOCOL);
+
+ while (isalpha(*bufp))
+ bufp++;
+ *(bufp++) = '\0';
+
+ if (strcmp(buf,"+OK") == 0)
+ ok = 0;
+ else if (strcmp(buf,"-ERR") == 0)
+ ok = PS_ERROR;
+ else
+ ok = PS_PROTOCOL;
+
+ if (argbuf != NULL)
+ strcpy(argbuf,bufp);
+ }
+ else
+ ok = PS_SOCKET;
+
+ return(ok);
+}
+
+int pop3_getauth(socket, queryctl, greeting)
+/* apply for connection authorization */
+int socket;
+struct hostrec *queryctl;
+char *greeting;
+{
+ char buf [POPBUFSIZE];
+
+#if defined(HAVE_APOP_SUPPORT)
+ /* build MD5 digest from greeting timestamp + password */
+ if (queryctl->whichpop == P_APOP)
+ if (POP3_BuildDigest(greeting,queryctl) != 0) {
+ return(PS_AUTHFAIL);
+ }
+#endif
+
+ switch (queryctl->protocol) {
+ case P_POP3:
+ SockPrintf(socket,"USER %s\r\n",queryctl->remotename);
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"> USER %s\n",queryctl->remotename);
+ if (POP3_OK(buf,socket) != 0)
+ goto badAuth;
+
+ SockPrintf(socket,"PASS %s\r\n",queryctl->password);
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"> PASS password\n");
+ if (POP3_OK(buf,socket) != 0)
+ goto badAuth;
+
+ break;
+
+#if defined(HAVE_APOP_SUPPORT)
+ case P_APOP:
+ SockPrintf(socket,"APOP %s %s\r\n",
+ queryctl->remotename, queryctl->digest);
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"> APOP %s %s\n",queryctl->remotename, queryctl->digest);
+ if (POP3_OK(buf,socket) != 0)
+ goto badAuth;
+ break;
+#endif /* HAVE_APOP_SUPPORT */
+
+#if defined(HAVE_RPOP_SUPPORT)
+ case P_RPOP:
+ SockPrintf(socket, "RPOP %s\r\n", queryctl->remotename);
+ if (POP3_OK(buf,socket) != 0)
+ goto badAuth;
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"> RPOP %s %s\n",queryctl->remotename);
+ break;
+#endif /* HAVE_RPOP_SUPPORT */
+
+ default:
+ fprintf(stderr,"Undefined protocol request in POP3_auth\n");
+ }
+
+ /* we're approved */
+ return(0);
+
+ /*NOTREACHED*/
+
+badAuth:
+ if (outlevel > O_SILENT && outlevel < O_VERBOSE)
+ fprintf(stderr,"%s\n",buf);
+ else
+ ; /* say nothing */
+
+ return(PS_ERROR);
+}
+
+static int use_uidl;
+
+static pop3_getrange(socket, queryctl, countp, firstp)
+/* get range of messages to be fetched */
+int socket;
+struct hostrec *queryctl;
+int *countp;
+int *firstp;
+{
+ int ok;
+
+ ok = POP3_sendSTAT(countp,socket);
+ if (ok != 0) {
+ return(ok);
+ }
+
+ /*
+ * Ask for number of last message retrieved.
+ * Newer, RFC-1760-conformant POP servers may not have the LAST command.
+ * Therefore we don't croak if we get a nonzero return. Instead, send
+ * UIDL and try to find the last received ID stored for this host in
+ * the list we get back.
+ */
+ *firstp = 1;
+ use_uidl = 0;
+ if (!queryctl->fetchall) {
+ char buf [POPBUFSIZE];
+ char id [IDLEN];
+ int num;
+
+ /* try LAST first */
+ ok = POP3_sendLAST(firstp, socket);
+ use_uidl = (ok != 0);
+
+ /* otherwise, if we have a stored last ID for this host,
+ * send UIDL and search the returned list for it
+ */
+ if (use_uidl && queryctl->lastid[0]) {
+ if ((ok = POP3_sendUIDL(-1, socket, 0)) == 0) {
+ while (SockGets(socket, buf, sizeof(buf)) >= 0) {
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"%s\n",buf);
+ if (strcmp(buf, ".\n") == 0) {
+ break;
+ }
+ if (sscanf(buf, "%d %s\n", &num, id) == 2)
+ if (strcmp(id, queryctl->lastid) == 0)
+ *firstp = num;
+ }
+ }
+ }
+
+ if (ok == 0)
+ (*firstp)++;
+ }
+
+ return(0);
+}
+
+static int pop3_fetch(socket, number, limit, lenp)
+/* request nth message */
+int socket;
+int number;
+int limit;
+int *lenp;
+{
+ *lenp = DELIMITED;
+ if (limit)
+ return(POP3_sendTOP(number, limit, socket));
+ else
+ return(POP3_sendRETR(number, socket));
+}
+
+static pop3_trail(socket, queryctl, number)
+/* update the last-seen field for this host */
+int socket;
+struct hostrec *queryctl;
+int number;
+{
+ char *cp;
+ int ok = 0;
+
+ if (use_uidl && (ok = POP3_sendUIDL(number, socket, &cp)) == 0)
+ (void) strcpy(queryctl->lastid, cp);
+ return(ok);
+}
+
+static struct method pop3 =
+{
+ "POP3", /* Post Office Protocol v3 */
+ 110, /* standard POP3 port */
+ 0, /* this is not a tagged protocol */
+ 1, /* this uses a message delimiter */
+ pop3_ok, /* parse command response */
+ pop3_getauth, /* get authorization */
+ pop3_getrange, /* query range of messages */
+ pop3_fetch, /* request given message */
+ pop3_trail, /* eat message trailer */
+ "DELE %d", /* set POP3 delete flag */
+ NULL, /* the POP3 expunge command */
+ "QUIT", /* the POP3 exit command */
+};
+
+int doPOP3bis (queryctl)
+/* retrieve messages using POP3 */
+struct hostrec *queryctl;
+{
+ return(do_protocol(queryctl, &pop3));
}
-method;
/*********************************************************************
@@ -111,13 +339,14 @@ int socket;
}
}
-int imap_getauth(socket, queryctl)
+int imap_getauth(socket, queryctl, buf)
/* apply for connection authorization */
int socket;
struct hostrec *queryctl;
+char *buf;
{
/* try to get authorized */
- return(transact(socket,
+ return(gen_transact(socket,
"LOGIN %s %s",
queryctl->remotename, queryctl->password));
}
@@ -133,7 +362,7 @@ int *firstp;
/* find out how many messages are waiting */
exists = unseen = recent = -1;
- ok = transact(socket,
+ ok = gen_transact(socket,
"SELECT %s",
queryctl->remotefolder[0] ? queryctl->remotefolder : "INBOX");
if (ok != 0)
@@ -168,11 +397,11 @@ int *lenp;
int num;
if (limit)
- generic_send(socket,
+ gen_send(socket,
"PARTIAL %d RFC822 0 %d",
number, limit);
else
- generic_send(socket,
+ gen_send(socket,
"FETCH %d RFC822",
number);
@@ -189,21 +418,26 @@ int *lenp;
return(0);
}
-static imap_trail(socket)
+static imap_trail(socket, queryctl, number)
/* discard tail of FETCH response */
int socket;
+struct hostrec *queryctl;
+int number;
{
char buf [POPBUFSIZE];
if (SockGets(socket, buf,sizeof(buf)) < 0)
return(PS_SOCKET);
+ else
+ return(0);
}
-static method imap =
+static struct method imap =
{
"IMAP", /* Internet Message Access Protocol */
143, /* standard IMAP3bis/IMAP4 port */
1, /* this is a tagged protocol */
+ 0, /* no message delimiter */
imap_ok, /* parse command response */
imap_getauth, /* get authorization */
imap_getrange, /* query range of messages */
@@ -214,16 +448,30 @@ static method imap =
"LOGOUT", /* the IMAP exit command */
};
-static method *protocol;
+int doIMAP (queryctl)
+/* retrieve messages using IMAP Version 2bis or Version 4 */
+struct hostrec *queryctl;
+{
+ return(do_protocol(queryctl, &imap));
+}
+
+/*********************************************************************
+
+ Everything below here is generic to all protocols.
+
+ *********************************************************************/
+
+static struct method *protocol;
/*********************************************************************
- function: doIMAP
+ function: do_protocol
description: retrieve messages from the specified mail server
- using IMAP Version 2bis or Version 4.
+ using a given set of mrthods
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
@@ -231,21 +479,9 @@ static method *protocol;
globals: reads outlevel.
*********************************************************************/
-int doIMAP (queryctl)
-struct hostrec *queryctl;
-{
- protocol = &imap;
- return(do_protocol(queryctl));
-}
-
-/*********************************************************************
-
- Everything below here is generic to all protocols.
-
- *********************************************************************/
-
-int do_protocol(queryctl)
+int do_protocol(queryctl, proto)
struct hostrec *queryctl;
+struct method *proto;
{
int ok, len;
int mboxfd;
@@ -254,6 +490,7 @@ struct hostrec *queryctl;
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)
@@ -271,7 +508,7 @@ struct hostrec *queryctl;
ok = imap_ok(buf,socket);
if (ok != 0) {
if (ok != PS_SOCKET)
- transact(socket, protocol->exit_cmd);
+ gen_transact(socket, protocol->exit_cmd);
close(socket);
goto closeUp;
}
@@ -281,7 +518,7 @@ struct hostrec *queryctl;
fprintf(stderr,"%s greeting: %s\n", protocol->name, buf);
/* try to get authorized to fetch mail */
- ok = (protocol->getauth)(socket, queryctl);
+ ok = (protocol->getauth)(socket, queryctl, buf);
if (ok == PS_ERROR)
ok = PS_AUTHFAIL;
if (ok != 0)
@@ -318,13 +555,16 @@ struct hostrec *queryctl;
{
(*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 = generic_readmsg(socket,mboxfd,len,
+ ok = gen_readmsg(socket,mboxfd,len,protocol->delimited,
queryctl->servername,
queryctl->output == TO_MDA,
queryctl->rewrite);
if (protocol->trail)
- (*protocol->trail)(socket);
+ (*protocol->trail)(socket, queryctl, number);
if (ok != 0)
goto cleanUp;
}
@@ -335,7 +575,7 @@ struct hostrec *queryctl;
fprintf(stderr,"flushing message %d\n", number);
else
;
- ok = transact(socket, protocol->delete_cmd, number);
+ ok = gen_transact(socket, protocol->delete_cmd, number);
if (ok != 0)
goto cleanUp;
}
@@ -352,19 +592,19 @@ struct hostrec *queryctl;
/* remove all messages flagged for deletion */
if (!queryctl->keep && protocol->expunge_cmd)
{
- ok = transact(socket, protocol->expunge_cmd);
+ ok = gen_transact(socket, protocol->expunge_cmd);
if (ok != 0)
goto cleanUp;
}
- ok = transact(socket, protocol->exit_cmd);
+ ok = gen_transact(socket, protocol->exit_cmd);
if (ok == 0)
ok = PS_SUCCESS;
close(socket);
goto closeUp;
}
else {
- ok = transact(socket, protocol->exit_cmd);
+ ok = gen_transact(socket, protocol->exit_cmd);
if (ok == 0)
ok = PS_NOMAIL;
close(socket);
@@ -373,7 +613,7 @@ struct hostrec *queryctl;
cleanUp:
if (ok != 0 && ok != PS_SOCKET)
- transact(socket, protocol->exit_cmd);
+ gen_transact(socket, protocol->exit_cmd);
closeUp:
if (queryctl->output == TO_FOLDER)
@@ -387,7 +627,7 @@ closeUp:
}
/*********************************************************************
- function: generic_send
+ function: gen_send
description: Assemble command in print style and send to the server
arguments:
@@ -399,7 +639,7 @@ closeUp:
globals: reads outlevel.
*********************************************************************/
-void generic_send(socket, fmt, va_alist)
+void gen_send(socket, fmt, va_alist)
int socket;
const char *fmt;
va_dcl {
@@ -409,6 +649,8 @@ va_dcl {
if (protocol->tagged)
(void) sprintf(buf, "%s ", GENSYM);
+ else
+ buf[0] = '\0';
va_start(ap);
vsprintf(buf + strlen(buf), fmt, ap);
@@ -421,7 +663,7 @@ va_dcl {
}
/*********************************************************************
- function: transact
+ function: gen_transact
description: Assemble command in print style and send to the server.
then accept a protocol-dependent response.
@@ -434,7 +676,7 @@ va_dcl {
globals: reads outlevel.
*********************************************************************/
-int transact(socket, fmt, va_alist)
+int gen_transact(socket, fmt, va_alist)
int socket;
const char *fmt;
va_dcl {
@@ -443,7 +685,10 @@ va_dcl {
char buf [POPBUFSIZE];
va_list ap;
- (void) sprintf(buf, "%s ", GENSYM);
+ if (protocol->tagged)
+ (void) sprintf(buf, "%s ", GENSYM);
+ else
+ buf[0] = '\0';
va_start(ap);
vsprintf(buf + strlen(buf), fmt, ap);
@@ -462,7 +707,7 @@ va_dcl {
}
/*********************************************************************
- function: generic_readmsg
+ function: gen_readmsg
description: Read the message content
as described in RFC 1225.
@@ -470,7 +715,7 @@ va_dcl {
socket ... to which the server is connected.
mboxfd open file descriptor to which the retrieved message will
be written.
- len lrength of text
+ len length of text
pophost name of the POP host
topipe true if we're writing to the system mailbox pipe.
@@ -479,10 +724,11 @@ va_dcl {
globals: reads outlevel.
*********************************************************************/
-int generic_readmsg (socket,mboxfd,len,pophost,topipe,rewrite)
+int gen_readmsg (socket,mboxfd,len,delimited,pophost,topipe,rewrite)
int socket;
int mboxfd;
-int len;
+long len;
+int delimited;
char *pophost;
int topipe;
int rewrite;
@@ -515,15 +761,19 @@ int rewrite;
inheaders = 1;
lines = 0;
sizeticker = MSGBUFSIZE;
- while (len > 0) {
+ while (delimited || len > 0) {
if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
return(PS_SOCKET);
len -= n;
+ if (outlevel == O_VERBOSE)
+ (void) fprintf(stderr, "%s\n", buf);
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");
@@ -545,7 +795,7 @@ int rewrite;
now = time(NULL);
sprintf(fromBuf,"From POPmail %s",ctime(&now));
if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) {
- perror("generic_readmsg: write");
+ perror("gen_readmsg: write");
return(PS_IOERR);
}
}
@@ -559,7 +809,7 @@ int rewrite;
/* write this line to the file */
if (write(mboxfd,bufp,strlen(bufp)) < 0) {
- perror("generic_readmsg: write");
+ perror("gen_readmsg: write");
return(PS_IOERR);
}
@@ -576,7 +826,7 @@ int rewrite;
/* 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("generic_readmsg: write");
+ perror("gen_readmsg: write");
return(PS_IOERR);
}
}
@@ -585,7 +835,7 @@ int rewrite;
it has been defined */
#ifdef BINMAIL_TERM
if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
- perror("generic_readmsg: write");
+ perror("gen_readmsg: write");
return(PS_IOERR);
}
#endif
@@ -600,6 +850,3 @@ int rewrite;
;
return(0);
}
-
-
-