]> Pileus Git - ~andy/fetchmail/blob - pop3.c
Better octet-message parsing.
[~andy/fetchmail] / pop3.c
1 /*
2  * pop3.c -- POP3 protocol methods
3  *
4  * For license terms, see the file COPYING in this directory.
5  */
6
7 #include  <config.h>
8 #include  <stdio.h>
9 #include  <string.h>
10  
11 #include  "socket.h"
12 #include  "fetchmail.h"
13
14 static int last;
15
16 int pop3_ok (socket, argbuf)
17 /* parse command response */
18 int socket;
19 char *argbuf;
20 {
21   int ok;
22   char buf [POPBUFSIZE+1];
23   char *bufp;
24
25   if (SockGets(socket, buf, sizeof(buf)) >= 0) {
26     if (outlevel == O_VERBOSE)
27       fprintf(stderr,"%s\n",buf);
28
29     bufp = buf;
30     if (*bufp == '+' || *bufp == '-')
31       bufp++;
32     else
33       return(PS_PROTOCOL);
34
35     while (isalpha(*bufp))
36       bufp++;
37     *(bufp++) = '\0';
38
39     if (strcmp(buf,"+OK") == 0)
40       ok = 0;
41     else if (strcmp(buf,"-ERR") == 0)
42       ok = PS_ERROR;
43     else
44       ok = PS_PROTOCOL;
45
46     if (argbuf != NULL)
47       strcpy(argbuf,bufp);
48   }
49   else 
50     ok = PS_SOCKET;
51
52   return(ok);
53 }
54
55 int pop3_getauth(socket, queryctl, greeting)
56 /* apply for connection authorization */
57 int socket;
58 struct hostrec *queryctl;
59 char *greeting;
60 {
61     char buf [POPBUFSIZE+1];
62
63     /* build MD5 digest from greeting timestamp + password */
64     if (queryctl->protocol == P_APOP) 
65     {
66         char *start,*end;
67         char *msg;
68
69         /* find start of timestamp */
70         for (start = greeting;  *start != 0 && *start != '<';  start++)
71             continue;
72         if (*start == 0) {
73             fprintf(stderr,"Required APOP timestamp not found in greeting\n");
74             return(PS_AUTHFAIL);
75         }
76
77         /* find end of timestamp */
78         for (end = start;  *end != 0  && *end != '>';  end++)
79             continue;
80         if (*end == 0 || (end - start - 1) == 1) {
81             fprintf(stderr,"Timestamp syntax error in greeting\n");
82             return(PS_AUTHFAIL);
83         }
84
85         /* copy timestamp and password into digestion buffer */
86         msg = (char *)xmalloc((end-start-1) + strlen(queryctl->password) + 1);
87         *(++end) = 0;
88         strcpy(msg,start);
89         strcat(msg,queryctl->password);
90
91         strcpy(queryctl->digest, MD5Digest(msg));
92         free(msg);
93     }
94
95     switch (queryctl->protocol) {
96     case P_POP3:
97         if ((gen_transact(socket,"USER %s", queryctl->remotename)) != 0)
98             return(PS_ERROR);
99
100         if ((gen_transact(socket, "PASS %s", queryctl->password)) != 0)
101             return(PS_ERROR);
102         break;
103
104     case P_APOP:
105         if ((gen_transact(socket, "APOP %s %s",
106                           queryctl->remotename, queryctl->digest)) != 0)
107             return(PS_ERROR);
108         break;
109
110     default:
111         fprintf(stderr,"Undefined protocol request in POP3_auth\n");
112     }
113
114     /* we're approved */
115     return(0);
116 }
117
118 static pop3_getrange(socket, queryctl, countp)
119 /* get range of messages to be fetched */
120 int socket;
121 struct hostrec *queryctl;
122 int *countp;
123 {
124     int ok;
125     char buf [POPBUFSIZE+1];
126
127     /* Ensure that the new list is properly empty */
128     queryctl->newsaved = (struct idlist *)NULL;
129
130     /* get the total message count */
131     gen_send(socket, "STAT");
132     ok = pop3_ok(socket, buf);
133     if (ok == 0)
134         sscanf(buf,"%d %*d", countp);
135     else
136         return(ok);
137
138     /*
139      * Newer, RFC-1725-conformant POP servers may not have the LAST command.
140      */
141     last = 0;
142     if (*countp > 0 && !queryctl->fetchall)
143     {
144         char id [IDLEN+1];
145
146         gen_send(socket,"LAST");
147         ok = pop3_ok(socket, buf);
148         if (ok == 0)
149         {
150             if (sscanf(buf, "%d", &last) == 0)
151                 return(PS_ERROR);
152         }
153         else
154         {
155             int num;
156
157             /* grab the mailbox's UID list */
158             gen_send(socket, "UIDL");
159             if ((ok = pop3_ok(socket, buf)) == 0) {
160                 while (SockGets(socket, buf, sizeof(buf)) >= 0) {
161                     if (outlevel == O_VERBOSE)
162                         fprintf(stderr,"%s\n",buf);
163                     if (strcmp(buf, ".") == 0) {
164                         break;
165                     }
166                     if (sscanf(buf, "%d %s", &num, id) == 2)
167                         save_uid(&queryctl->newsaved, num, id);
168                 }
169             }
170         }
171     }
172
173     return(0);
174 }
175
176 static int pop3_is_old(socket, queryctl, num)
177 int socket;
178 struct hostrec *queryctl;
179 int num;
180 {
181     if (!queryctl->oldsaved)
182         return (num <= last);
183     else
184         return (uid_in_list(&queryctl->oldsaved,
185                             uid_find (&queryctl->newsaved, num)));
186 }
187
188 static int pop3_fetch(socket, number, lenp)
189 /* request nth message */
190 int socket;
191 int number;
192 int *lenp; 
193 {
194     int ok;
195     char buf [POPBUFSIZE+1], *cp;
196
197     gen_send(socket, "RETR %d", number);
198     if ((ok = pop3_ok(socket, buf)) != 0)
199         return(ok);
200     /* look for "nnn octets" -- there may or may not be preceding cruft */
201     if ((cp = strstr(buf, " octets")) == (char *)NULL)
202         *lenp = 0;
203     else
204     {
205         while (isdigit(*--cp))
206             continue;
207         *lenp = atoi(++cp);
208     }
209     return(0);
210 }
211
212 static pop3_delete(socket, queryctl, number)
213 /* delete a given message */
214 int socket;
215 struct hostrec *queryctl;
216 int number;
217 {
218     return(gen_transact(socket, "DELE %d", number));
219 }
220
221 const static struct method pop3 =
222 {
223     "POP3",             /* Post Office Protocol v3 */
224     110,                /* standard POP3 port */
225     0,                  /* this is not a tagged protocol */
226     1,                  /* this uses a message delimiter */
227     pop3_ok,            /* parse command response */
228     pop3_getauth,       /* get authorization */
229     pop3_getrange,      /* query range of messages */
230     pop3_is_old,        /* how do we tell a message is old? */
231     pop3_fetch,         /* request given message */
232     NULL,               /* no message trailer */
233     pop3_delete,        /* how to delete a message */
234     NULL,               /* no POP3 expunge command */
235     "QUIT",             /* the POP3 exit command */
236 };
237
238 int doPOP3 (queryctl)
239 /* retrieve messages using POP3 */
240 struct hostrec *queryctl;
241 {
242     if (queryctl->mailbox[0]) {
243         fprintf(stderr,"Option --remote is not supported with POP3\n");
244         return(PS_SYNTAX);
245     }
246     return(do_protocol(queryctl, &pop3));
247 }
248
249 /* pop3.c ends here */