aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--imap.c374
1 files changed, 231 insertions, 143 deletions
diff --git a/imap.c b/imap.c
index 0e061152..eeb115f0 100644
--- a/imap.c
+++ b/imap.c
@@ -29,14 +29,12 @@
#include "socket.h"
#include "popclient.h"
-#define IMAP_PORT 143
-
#ifdef HAVE_PROTOTYPES
/* prototypes for internal functions */
-int IMAP_OK (char *buf, int socket);
-void IMAP_send ();
-int IMAP_cmd ();
-int IMAP_readmsg (int socket, int mboxfd, int len,
+int generic_ok (char *buf, int socket);
+void generic_send ();
+int transact ();
+int generic_readmsg (int socket, int mboxfd, int len,
char *host, int topipe, int rewrite);
#endif
@@ -45,9 +43,178 @@ static char tag[TAGLEN];
static int tagnum;
#define GENSYM (sprintf(tag, "a%04d", ++tagnum), tag)
-static int exists;
-static int unseen;
-static int recent;
+static int count, first;
+
+typedef struct
+{
+ char *name; /* protocol name */
+ int port; /* service port */
+ int tagged; /* if true, generate & expect command tags */
+ int (*parse_response)(); /* response_parsing function */
+ int (*getauth)(); /* authorization fetcher */
+ int (*getrange)(); /* get message range to fetch */
+ int (*fetch)(); /* fetch a given message */
+ int (*trail)(); /* eat trailer of a message */
+ char *delete_cmd; /* delete command */
+ char *expunge_cmd; /* expunge command */
+ char *exit_cmd; /* exit command */
+}
+method;
+
+/*********************************************************************
+
+ Method declarations for IMAP
+
+ *********************************************************************/
+
+static int exists, unseen, recent;
+
+int imap_ok (argbuf,socket)
+/* parse command response */
+char *argbuf;
+int socket;
+{
+ int ok;
+ char buf [POPBUFSIZE];
+ char *bufp;
+ int n;
+
+ do {
+ if (SockGets(socket, buf, sizeof(buf)) < 0)
+ return(PS_SOCKET);
+
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"%s\n",buf);
+
+ /* interpret untagged status responses */
+ if (strstr(buf, "EXISTS"))
+ exists = atoi(buf+2);
+ if (strstr(buf, "RECENT"))
+ recent = atoi(buf+2);
+ if (sscanf(buf + 2, "OK [UNSEEN %d]", &n) == 1)
+ unseen = n;
+
+ } while
+ (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
+
+ if (tag[0] == '\0')
+ return(0);
+ else {
+ if (strncmp(buf + TAGLEN + 1, "OK", 2) == 0) {
+ strcpy(argbuf, buf + TAGLEN);
+ return(0);
+ }
+ else if (strncmp(buf + TAGLEN + 1, "BAD", 2) == 0)
+ return(PS_ERROR);
+ else
+ return(PS_PROTOCOL);
+ }
+}
+
+int imap_getauth(socket, queryctl)
+/* apply for connection authorization */
+int socket;
+struct hostrec *queryctl;
+{
+ /* try to get authorized */
+ return(transact(socket,
+ "LOGIN %s %s",
+ queryctl->remotename, queryctl->password));
+}
+
+static imap_getrange(socket, queryctl, countp, firstp)
+/* get range of messages to be fetched */
+int socket;
+struct hostrec *queryctl;
+int *countp;
+int *firstp;
+{
+ int ok;
+
+ /* find out how many messages are waiting */
+ exists = unseen = recent = -1;
+ ok = transact(socket,
+ "SELECT %s",
+ queryctl->remotefolder[0] ? queryctl->remotefolder : "INBOX");
+ if (ok != 0)
+ return(ok);
+
+ /* compute size of message run */
+ *countp = exists;
+ if (queryctl->fetchall)
+ *firstp = 1;
+ else {
+ if (exists > 0 && unseen == -1) {
+ fprintf(stderr,
+ "no UNSEEN response; assuming all %d RECENT messages are unseen\n",
+ recent);
+ *firstp = exists - recent + 1;
+ } else {
+ *firstp = unseen;
+ }
+ }
+
+ return(0);
+}
+
+static int imap_fetch(socket, number, limit, lenp)
+/* request nth message */
+int socket;
+int number;
+int limit;
+int *lenp;
+{
+ char buf [POPBUFSIZE];
+ int num;
+
+ if (limit)
+ generic_send(socket,
+ "PARTIAL %d RFC822 0 %d",
+ number, limit);
+ else
+ generic_send(socket,
+ "FETCH %d RFC822",
+ number);
+
+ /* looking for FETCH response */
+ do {
+ if (SockGets(socket, buf,sizeof(buf)) < 0)
+ return(PS_SOCKET);
+ } while
+ (sscanf(buf+2, "%d FETCH (RFC822 {%d}", &num, lenp) != 2);
+
+ if (num != number)
+ return(PS_ERROR);
+ else
+ return(0);
+}
+
+static imap_trail(socket)
+/* discard tail of FETCH response */
+int socket;
+{
+ char buf [POPBUFSIZE];
+
+ if (SockGets(socket, buf,sizeof(buf)) < 0)
+ return(PS_SOCKET);
+}
+
+static method imap =
+{
+ "IMAP", /* Internet Message Access Protocol */
+ 143, /* standard IMAP3bis/IMAP4 port */
+ 1, /* this is a tagged protocol */
+ imap_ok, /* parse command response */
+ imap_getauth, /* get authorization */
+ imap_getrange, /* query range of messages */
+ imap_fetch, /* request given message */
+ imap_trail, /* eat message trailer */
+ "STORE %d +FLAGS (\\Deleted)", /* set IMAP delete flag */
+ "EXPUNGE", /* the IMAP expunge command */
+ "LOGOUT", /* the IMAP exit command */
+};
+
+static method *protocol;
/*********************************************************************
function: doIMAP
@@ -67,7 +234,20 @@ static int recent;
int doIMAP (queryctl)
struct hostrec *queryctl;
{
- int ok, num, len;
+ protocol = &imap;
+ return(do_protocol(queryctl));
+}
+
+/*********************************************************************
+
+ Everything below here is generic to all protocols.
+
+ *********************************************************************/
+
+int do_protocol(queryctl)
+struct hostrec *queryctl;
+{
+ int ok, len;
int mboxfd;
char buf [POPBUFSIZE];
int socket;
@@ -81,57 +261,36 @@ struct hostrec *queryctl;
return(PS_IOERR);
/* open the socket */
- if ((socket = Socket(queryctl->servername,IMAP_PORT)) < 0) {
- perror("doIMAP: 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);
+ ok = imap_ok(buf,socket);
if (ok != 0) {
if (ok != PS_SOCKET)
- IMAP_cmd(socket, "LOGOUT");
+ transact(socket, protocol->exit_cmd);
close(socket);
goto closeUp;
}
/* print the greeting */
if (outlevel > O_SILENT && outlevel < O_VERBOSE)
- fprintf(stderr,"IMAP greeting: %s\n",buf);
+ fprintf(stderr,"%s greeting: %s\n", protocol->name, buf);
- /* try to get authorized */
- ok = IMAP_cmd(socket,
- "LOGIN %s %s",
- queryctl->remotename, queryctl->password);
+ /* try to get authorized to fetch mail */
+ ok = (protocol->getauth)(socket, queryctl);
if (ok == PS_ERROR)
ok = PS_AUTHFAIL;
if (ok != 0)
goto cleanUp;
- /* find out how many messages are waiting */
- exists = unseen = recent = -1;
- ok = IMAP_cmd(socket,
- "SELECT %s",
- queryctl->remotefolder[0] ? queryctl->remotefolder : "INBOX");
- if (ok != 0)
+ /* compute count and first */
+ if ((*protocol->getrange)(socket, queryctl, &count, &first) != 0)
goto cleanUp;
- /* compute size of message run */
- count = exists;
- if (queryctl->fetchall)
- first = 1;
- else {
- if (exists > 0 && unseen == -1) {
- fprintf(stderr,
- "no UNSEEN response; assuming all %d RECENT messages are unseen\n",
- recent);
- first = exists - recent + 1;
- } else {
- first = unseen;
- }
- }
-
/* show them how many messages we'll be downloading */
if (outlevel > O_SILENT && outlevel < O_VERBOSE)
if (first > 1)
@@ -155,37 +314,20 @@ struct hostrec *queryctl;
if (queryctl->flush && number < first && !queryctl->fetchall)
ok = 0; /* no command to send here, will delete message below */
- else if (linelimit)
- IMAP_send(socket,
- "PARTIAL %d RFC822 0 %d",
- number, linelimit);
- else
- IMAP_send(socket,
- "FETCH %d RFC822",
- number);
-
- if (number >= first || queryctl->fetchall) {
- /* looking for FETCH response */
- do {
- if (SockGets(socket, buf,sizeof(buf)) < 0)
- return(PS_SOCKET);
- } while
- (sscanf(buf+2, "%d FETCH (RFC822 {%d}", &num, &len)!=2);
+ else
+ {
+ (*protocol->fetch)(socket, number, linelimit, &len);
if (outlevel == O_VERBOSE)
- fprintf(stderr,"fetching message %d (%d bytes)\n",num,len);
- ok = IMAP_readmsg(socket,mboxfd, len,
+ fprintf(stderr,"fetching message %d (%d bytes)\n",number,len);
+ ok = generic_readmsg(socket,mboxfd,len,
queryctl->servername,
queryctl->output == TO_MDA,
queryctl->rewrite);
- /* discard tail of FETCH response */
- if (SockGets(socket, buf,sizeof(buf)) < 0)
- return(PS_SOCKET);
+ if (protocol->trail)
+ (*protocol->trail)(socket);
+ if (ok != 0)
+ goto cleanUp;
}
- else
- ok = 0;
-
- if (ok != 0)
- goto cleanUp;
/* maybe we delete this message now? */
if ((number < first && queryctl->flush) || !queryctl->keep) {
@@ -193,9 +335,7 @@ struct hostrec *queryctl;
fprintf(stderr,"flushing message %d\n", number);
else
;
- ok = IMAP_cmd(socket,
- "STORE %d +FLAGS (\\Deleted)",
- number);
+ ok = transact(socket, protocol->delete_cmd, number);
if (ok != 0)
goto cleanUp;
}
@@ -210,21 +350,21 @@ struct hostrec *queryctl;
}
/* remove all messages flagged for deletion */
- if (!queryctl->keep)
+ if (!queryctl->keep && protocol->expunge_cmd)
{
- ok = IMAP_cmd(socket, "EXPUNGE");
+ ok = transact(socket, protocol->expunge_cmd);
if (ok != 0)
goto cleanUp;
}
- ok = IMAP_cmd(socket, "LOGOUT");
+ ok = transact(socket, protocol->exit_cmd);
if (ok == 0)
ok = PS_SUCCESS;
close(socket);
goto closeUp;
}
else {
- ok = IMAP_cmd(socket, "LOGOUT");
+ ok = transact(socket, protocol->exit_cmd);
if (ok == 0)
ok = PS_NOMAIL;
close(socket);
@@ -233,7 +373,7 @@ struct hostrec *queryctl;
cleanUp:
if (ok != 0 && ok != PS_SOCKET)
- IMAP_cmd(socket, "LOGOUT");
+ transact(socket, protocol->exit_cmd);
closeUp:
if (queryctl->output == TO_FOLDER)
@@ -241,67 +381,13 @@ closeUp:
ok = PS_IOERR;
if (ok == PS_IOERR || ok == PS_SOCKET)
- perror("doIMAP: cleanUp");
+ perror("do_protocol: cleanUp");
return(ok);
}
/*********************************************************************
- function: IMAP_OK
- description: get the server's response to a command, and return
- the extra arguments sent with the response.
- arguments:
- argbuf buffer to receive the argument string.
- socket socket to which the server is connected.
-
- return value: zero if okay, else return code.
- calls: SockGets
- globals: reads outlevel.
- *********************************************************************/
-
-int IMAP_OK (argbuf,socket)
-char *argbuf;
-int socket;
-{
- int ok;
- char buf [POPBUFSIZE];
- char *bufp;
- int n;
-
- do {
- if (SockGets(socket, buf, sizeof(buf)) < 0)
- return(PS_SOCKET);
-
- if (outlevel == O_VERBOSE)
- fprintf(stderr,"%s\n",buf);
-
- /* interpret untagged status responses */
- if (strstr(buf, "EXISTS"))
- exists = atoi(buf+2);
- if (strstr(buf, "RECENT"))
- recent = atoi(buf+2);
- if (sscanf(buf + 2, "OK [UNSEEN %d]", &n) == 1)
- unseen = n;
-
- } while
- (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
-
- if (tag[0] == '\0')
- return(0);
- else {
- if (strncmp(buf + TAGLEN + 1, "OK", 2) == 0) {
- strcpy(argbuf, buf + TAGLEN);
- return(0);
- }
- else if (strncmp(buf + TAGLEN + 1, "BAD", 2) == 0)
- return(PS_ERROR);
- else
- return(PS_PROTOCOL);
- }
-}
-
-/*********************************************************************
- function: IMAP_send
+ function: generic_send
description: Assemble command in print style and send to the server
arguments:
@@ -313,7 +399,7 @@ int socket;
globals: reads outlevel.
*********************************************************************/
-void IMAP_send(socket, fmt, va_alist)
+void generic_send(socket, fmt, va_alist)
int socket;
const char *fmt;
va_dcl {
@@ -321,7 +407,8 @@ va_dcl {
char buf [POPBUFSIZE];
va_list ap;
- (void) sprintf(buf, "%s ", GENSYM);
+ if (protocol->tagged)
+ (void) sprintf(buf, "%s ", GENSYM);
va_start(ap);
vsprintf(buf + strlen(buf), fmt, ap);
@@ -334,19 +421,20 @@ va_dcl {
}
/*********************************************************************
- function: IMAP_cmd
- description: Assemble command in print style and send to the server
+ function: 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.
+ calls: SockPuts, imap_ok.
globals: reads outlevel.
*********************************************************************/
-int IMAP_cmd(socket, fmt, va_alist)
+int transact(socket, fmt, va_alist)
int socket;
const char *fmt;
va_dcl {
@@ -366,7 +454,7 @@ va_dcl {
if (outlevel == O_VERBOSE)
fprintf(stderr,"> %s\n", buf);
- ok = IMAP_OK(buf,socket);
+ ok = (protocol->parse_response)(buf,socket);
if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
fprintf(stderr,"%s\n",buf);
@@ -374,7 +462,7 @@ va_dcl {
}
/*********************************************************************
- function: IMAP_readmsg
+ function: generic_readmsg
description: Read the message content
as described in RFC 1225.
@@ -391,7 +479,7 @@ va_dcl {
globals: reads outlevel.
*********************************************************************/
-int IMAP_readmsg (socket,mboxfd,len,pophost,topipe,rewrite)
+int generic_readmsg (socket,mboxfd,len,pophost,topipe,rewrite)
int socket;
int mboxfd;
int len;
@@ -457,7 +545,7 @@ int rewrite;
now = time(NULL);
sprintf(fromBuf,"From POPmail %s",ctime(&now));
if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) {
- perror("IMAP_readmsg: write");
+ perror("generic_readmsg: write");
return(PS_IOERR);
}
}
@@ -471,7 +559,7 @@ int rewrite;
/* write this line to the file */
if (write(mboxfd,bufp,strlen(bufp)) < 0) {
- perror("IMAP_readmsg: write");
+ perror("generic_readmsg: write");
return(PS_IOERR);
}
@@ -488,7 +576,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("IMAP_readmsg: write");
+ perror("generic_readmsg: write");
return(PS_IOERR);
}
}
@@ -497,7 +585,7 @@ int rewrite;
it has been defined */
#ifdef BINMAIL_TERM
if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
- perror("IMAP_readmsg: write");
+ perror("generic_readmsg: write");
return(PS_IOERR);
}
#endif