1 /* Copyright 1993-95 by Carl Harris, Jr. Copyright 1996 by Eric S. Raymond
3 * For license terms, see the file COPYING in this directory.
6 /***********************************************************************
9 programmer: Carl Harris, ceharris@mal.com
10 Hacks and bug fixes by esr.
11 description: POP3 client code.
13 ***********************************************************************/
18 #if defined(STDC_HEADERS)
21 #if defined(HAVE_UNISTD_H)
27 #include "fetchmail.h"
29 #ifdef HAVE_PROTOTYPES
30 /* prototypes for internal functions */
31 int POP3_sendSTAT (int *msgcount, int socket);
32 int POP3_sendLAST (int *last, int socket);
33 int POP3_sendUIDL (int num, int socket, char **cp);
34 int POP3_BuildDigest (char *buf, struct hostrec *options);
38 /*********************************************************************
40 Method declarations for POP3
42 *********************************************************************/
44 int pop3_ok (argbuf,socket)
45 /* parse command response */
50 char buf [POPBUFSIZE+1];
53 if (SockGets(socket, buf, sizeof(buf)) >= 0) {
54 if (outlevel == O_VERBOSE)
55 fprintf(stderr,"%s\n",buf);
58 if (*bufp == '+' || *bufp == '-')
63 while (isalpha(*bufp))
67 if (strcmp(buf,"+OK") == 0)
69 else if (strcmp(buf,"-ERR") == 0)
83 int pop3_getauth(socket, queryctl, greeting)
84 /* apply for connection authorization */
86 struct hostrec *queryctl;
89 char buf [POPBUFSIZE+1];
91 #if defined(HAVE_APOP_SUPPORT)
92 /* build MD5 digest from greeting timestamp + password */
93 if (queryctl->protocol == P_APOP)
94 if (POP3_BuildDigest(greeting,queryctl) != 0) {
97 #endif /* HAVE_APOP_SUPPORT */
99 switch (queryctl->protocol) {
101 SockPrintf(socket,"USER %s\r\n",queryctl->remotename);
102 if (outlevel == O_VERBOSE)
103 fprintf(stderr,"> USER %s\n",queryctl->remotename);
104 if (pop3_ok(buf,socket) != 0)
107 if (queryctl->rpopid[0])
109 SockPrintf(socket, "RPOP %s\r\n", queryctl->rpopid);
110 if (outlevel == O_VERBOSE)
111 fprintf(stderr,"> RPOP %s %s\n",queryctl->rpopid);
115 SockPrintf(socket,"PASS %s\r\n",queryctl->password);
116 if (outlevel == O_VERBOSE)
117 fprintf(stderr,"> PASS password\n");
119 if (pop3_ok(buf,socket) != 0)
123 #if defined(HAVE_APOP_SUPPORT)
125 SockPrintf(socket,"APOP %s %s\r\n",
126 queryctl->remotename, queryctl->digest);
127 if (outlevel == O_VERBOSE)
128 fprintf(stderr,"> APOP %s %s\n",queryctl->remotename, queryctl->digest);
129 if (pop3_ok(buf,socket) != 0)
132 #endif /* HAVE_APOP_SUPPORT */
135 fprintf(stderr,"Undefined protocol request in POP3_auth\n");
144 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
145 fprintf(stderr,"%s\n",buf);
154 static pop3_getrange(socket, queryctl, countp, firstp)
155 /* get range of messages to be fetched */
157 struct hostrec *queryctl;
163 ok = POP3_sendSTAT(countp,socket);
169 * Ask for number of last message retrieved.
170 * Newer, RFC-1760-conformant POP servers may not have the LAST command.
171 * Therefore we don't croak if we get a nonzero return. Instead, send
172 * UIDL and try to find the last received ID stored for this host in
173 * the list we get back.
177 if (!queryctl->fetchall) {
178 char buf [POPBUFSIZE+1];
183 ok = POP3_sendLAST(firstp, socket);
184 use_uidl = (ok != 0);
186 /* otherwise, if we have a stored last ID for this host,
187 * send UIDL and search the returned list for it
189 if (use_uidl && queryctl->lastid[0]) {
190 if ((ok = POP3_sendUIDL(-1, socket, 0)) == 0) {
191 while (SockGets(socket, buf, sizeof(buf)) >= 0) {
192 if (outlevel == O_VERBOSE)
193 fprintf(stderr,"%s\n",buf);
194 if (strcmp(buf, ".\n") == 0) {
197 if (sscanf(buf, "%d %s\n", &num, id) == 2)
198 if (strcmp(id, queryctl->lastid) == 0)
211 static int pop3_fetch(socket, number, limit, lenp)
212 /* request nth message */
220 return(gen_transact(socket, "TOP %d %d", number, limit));
222 return(gen_transact(socket, "RETR %d", number));
225 static pop3_trail(socket, queryctl, number)
226 /* update the last-seen field for this host */
228 struct hostrec *queryctl;
234 if (use_uidl && (ok = POP3_sendUIDL(number, socket, &cp)) == 0)
235 (void) strcpy(queryctl->lastid, cp);
239 static struct method pop3 =
241 "POP3", /* Post Office Protocol v3 */
242 110, /* standard POP3 port */
243 0, /* this is not a tagged protocol */
244 1, /* this uses a message delimiter */
245 pop3_ok, /* parse command response */
246 pop3_getauth, /* get authorization */
247 pop3_getrange, /* query range of messages */
248 pop3_fetch, /* request given message */
249 pop3_trail, /* eat message trailer */
250 "DELE %d", /* set POP3 delete flag */
251 NULL, /* the POP3 expunge command */
252 "QUIT", /* the POP3 exit command */
255 int doPOP3 (queryctl)
256 /* retrieve messages using POP3 */
257 struct hostrec *queryctl;
259 if (queryctl->remotefolder[0]) {
260 fprintf(stderr,"Option --remote is not supported with POP3\n");
264 return(do_protocol(queryctl, &pop3));
267 /*********************************************************************
268 function: POP3_sendSTAT
269 description: send the STAT command to the POP3 server to find
270 out how many messages are waiting.
272 count pointer to an integer to receive the message count.
273 socket socket to which the POP3 server is connected.
275 return value: return code from POP3_OK.
276 calls: POP3_OK, SockPrintf
277 globals: reads outlevel.
278 *********************************************************************/
280 int POP3_sendSTAT (msgcount,socket)
285 char buf [POPBUFSIZE+1];
288 SockPrintf(socket,"STAT\r\n");
289 if (outlevel == O_VERBOSE)
290 fprintf(stderr,"> STAT\n");
292 ok = pop3_ok(buf,socket);
294 sscanf(buf,"%d %d",msgcount,&totalsize);
295 else if (outlevel > O_SILENT && outlevel < O_VERBOSE)
296 fprintf(stderr,"%s\n",buf);
301 /******************************************************************
302 function: POP3_sendLAST
303 description: send the LAST command to the server, which should
304 return the number of the last message number retrieved
307 last integer buffer to receive last message#
309 ret. value: zero if success, else status code.
310 globals: SockPrintf, pop3_ok.
311 calls: reads outlevel.
312 *****************************************************************/
314 int POP3_sendLAST (last, socket)
319 char buf [POPBUFSIZE];
321 SockPrintf(socket,"LAST\r\n");
322 if (outlevel == O_VERBOSE)
323 fprintf(stderr,"> LAST\n");
325 ok = pop3_ok(buf,socket);
326 if (ok == 0 && sscanf(buf,"%d",last) == 0)
329 if (ok != 0 && outlevel > O_SILENT)
330 fprintf(stderr,"Server says '%s' to LAST command.\n",buf);
335 /******************************************************************
336 function: POP3_sendUIDL
337 description: send the UIDL command to the server,
340 num number of message to query (may be -1)
342 ret. value: zero if success, else status code.
343 globals: SockPrintf, pop3_ok.
344 calls: reads outlevel.
345 *****************************************************************/
347 int POP3_sendUIDL (num, socket, cp)
353 char buf [POPBUFSIZE];
354 static char id[IDLEN];
356 (void) strcpy(buf, "UIDL\r\n");
358 (void) sprintf(buf, "UIDL %d\r\n", num);
360 SockPrintf(socket, buf);
361 if (outlevel == O_VERBOSE)
362 fprintf(stderr,"> %s", buf);
364 ok = pop3_ok(buf,socket);
365 if (ok != 0 && outlevel > O_SILENT)
366 fprintf(stderr,"Server says '%s' to UIDL command.\n",buf);
369 sscanf(buf, "%*d %s\n", id);
376 /******************************************************************
377 function: POP3_BuildDigest
378 description: Construct the MD5 digest for the current session,
379 using the user-specified password, and the time
380 stamp in the POP3 greeting.
383 queryctl merged options record.
385 ret. value: zero on success, nonzero if no timestamp found in
389 *****************************************************************/
391 #if defined(HAVE_APOP_SUPPORT)
392 POP3_BuildDigest (buf,queryctl)
394 struct hostrec *queryctl;
399 /* find start of timestamp */
400 for (start = buf; *start != 0 && *start != '<'; start++)
403 fprintf(stderr,"Required APOP timestamp not found in greeting\n");
407 /* find end of timestamp */
408 for (end = start; *end != 0 && *end != '>'; end++)
410 if (*end == 0 || (end - start - 1) == 1) {
411 fprintf(stderr,"Timestamp syntax error in greeting\n");
415 /* copy timestamp and password into digestion buffer */
416 msg = (char *) malloc((end-start-1) + strlen(queryctl->password) + 1);
419 strcat(msg,queryctl->password);
421 strcpy(queryctl->digest, MD5Digest(msg));
425 #endif /* HAVE_APOP_SUPPORT */