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