1 |
ben |
1 |
/* this is objects.c, part of the libspopc library sources |
2 |
|
|
* copyright © 2002- Benoit Rouits <brouits@free.fr> |
3 |
|
|
* released under the terms of the GNU Lesser General Public Licence. |
4 |
|
|
* |
5 |
|
|
* libspopc offers simple API for a pop3 client. |
6 |
|
|
* See RFC 1725 for pop3 specifications. |
7 |
ben |
2 |
* more information on http://herewe.servebeer.com/libspopc/ |
8 |
ben |
1 |
* |
9 |
|
|
* This library is free software; you can redistribute it and/or |
10 |
|
|
* modify it under the terms of the GNU Lesser General Public |
11 |
|
|
* License as published by the Free Software Foundation; either |
12 |
|
|
* version 2.1 of the License, or (at your option) any later version. |
13 |
|
|
* |
14 |
|
|
* This library is distributed in the hope that it will be useful, |
15 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 |
|
|
* Lesser General Public License for more details. |
18 |
|
|
* |
19 |
|
|
* You should have received a copy of the GNU Lesser General Public |
20 |
|
|
* License along with this library; if not, write to the Free Software |
21 |
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 |
|
|
*/ |
23 |
|
|
|
24 |
|
|
#include <stdlib.h> |
25 |
|
|
#include <string.h> |
26 |
|
|
#include <sys/types.h> |
27 |
|
|
|
28 |
|
|
#ifdef WIN32 |
29 |
|
|
#include <winsock.h> |
30 |
|
|
#else |
31 |
|
|
#include <sys/socket.h> |
32 |
|
|
#include <netdb.h> |
33 |
|
|
#endif |
34 |
|
|
|
35 |
|
|
#include "libspopc.h" |
36 |
|
|
|
37 |
|
|
/*************************************** |
38 |
|
|
* high-level methods for a simple mua * |
39 |
|
|
***************************************/ |
40 |
|
|
|
41 |
|
|
char* popbegin(const char* servername, const char* user, const char* pass, popsession** sp){ |
42 |
|
|
/* prepares, connect and get lists of messages stored on pop server */ |
43 |
|
|
/* you must give a valid servername, user and pass */ |
44 |
|
|
/* returns an error message if a problem occurs, else NULL */ |
45 |
|
|
char* resp=NULL; |
46 |
|
|
char* err=NULL; |
47 |
|
|
char *hostname, *ptr_port; |
48 |
|
|
int nport; |
49 |
|
|
popsession* s = NULL; |
50 |
|
|
|
51 |
|
|
if(!(servername && user && pass)){ |
52 |
|
|
err=strdup("popbegin: some NULL args !"); |
53 |
|
|
goto error; |
54 |
|
|
} |
55 |
|
|
s=(popsession*)malloc(sizeof(popsession)); |
56 |
|
|
if(!s){ |
57 |
|
|
err=strdup("popbegin.malloc: failed\n"); |
58 |
|
|
goto error; |
59 |
|
|
} |
60 |
|
|
/* basic default construction */ |
61 |
|
|
s->sock=BAD_SOCK; |
62 |
|
|
s->connection=NULL; |
63 |
|
|
s->server=NULL; |
64 |
|
|
s->list=NULL; |
65 |
|
|
s->uidl=NULL; |
66 |
|
|
s->bytes=-1; |
67 |
|
|
s->last=-1; |
68 |
|
|
s->num=-1; |
69 |
|
|
s->del=0; /* no deletion (by default) at this time */ |
70 |
|
|
s->sync=1; /* this is sync'ed at this time (no cnx yet) */ |
71 |
|
|
|
72 |
|
|
s->server=(struct hostent*)malloc(sizeof(struct hostent)); |
73 |
|
|
if(!(s->server)){ |
74 |
|
|
err=strdup("popbegin.malloc: failed\n"); |
75 |
|
|
goto error; |
76 |
|
|
} |
77 |
|
|
s->connection=(struct sockaddr_in*)malloc(sizeof(struct sockaddr_in)); |
78 |
|
|
if(!(s->connection)){ |
79 |
|
|
err=strdup("popbegin.malloc: failed\n"); |
80 |
|
|
goto error; |
81 |
|
|
} |
82 |
|
|
hostname = strdup (servername); |
83 |
|
|
if (!hostname) { |
84 |
|
|
err=strdup("popbegin.strdup: failed\n"); /* ahem... */ |
85 |
|
|
goto error; |
86 |
|
|
} |
87 |
|
|
ptr_port = strchr (hostname, ':'); |
88 |
|
|
if (!ptr_port) |
89 |
|
|
nport = 110; |
90 |
|
|
else { |
91 |
|
|
*ptr_port = 0; |
92 |
|
|
nport = (int) strtoul (++ptr_port, NULL, 10); |
93 |
|
|
if (!nport) |
94 |
|
|
nport = 110; |
95 |
|
|
} |
96 |
|
|
s->sock=pop3_prepare(hostname,nport,s->connection,s->server); |
97 |
|
|
free (hostname); |
98 |
|
|
if(s->sock==BAD_SOCK){ |
99 |
|
|
err=strdup("popbegin.pop3_prepare: failed\n"); |
100 |
|
|
goto error; |
101 |
|
|
} |
102 |
|
|
resp=pop3_connect(s->sock,s->connection); |
103 |
|
|
if(!resp){ |
104 |
|
|
err=strdup("popbegin.pop3_connect: failed\n"); |
105 |
|
|
goto error; |
106 |
|
|
} |
107 |
|
|
free(resp); |
108 |
|
|
resp=pop3_user(s->sock,user); |
109 |
|
|
if((!resp) || pop3_error(resp)){ |
110 |
|
|
err=resp?resp:strdup("popbegin.pop3_user: failed\n"); |
111 |
|
|
goto error; |
112 |
|
|
} |
113 |
|
|
free(resp); |
114 |
|
|
resp=pop3_pass(s->sock,pass); |
115 |
|
|
if((!resp) || pop3_error(resp)){ |
116 |
|
|
err=resp?resp:strdup("popbegin.pop3_pass: failed\n"); |
117 |
|
|
goto error; |
118 |
|
|
} |
119 |
|
|
free(resp); |
120 |
|
|
resp=pop3_stat(s->sock); |
121 |
|
|
if((!resp) || pop3_error(resp)){ |
122 |
|
|
err=resp?resp:strdup("popbegin.pop3_stat: failed\n"); |
123 |
|
|
goto error; |
124 |
|
|
} |
125 |
|
|
s->bytes=stat2bytes(resp); |
126 |
|
|
s->num=stat2num(resp); |
127 |
|
|
s->last=stat2num(resp); /* safe here: we did not delete anything */ |
128 |
|
|
free(resp); |
129 |
|
|
resp=pop3_list(s->sock,0); |
130 |
|
|
if((!resp) || pop3_error(resp)){ |
131 |
|
|
err=resp?resp:strdup("popbegin.pop3_list: failed\n"); |
132 |
|
|
goto error; |
133 |
|
|
} |
134 |
|
|
s->list=list2array(resp); |
135 |
|
|
free(resp); |
136 |
|
|
resp=pop3_uidl(s->sock,0); |
137 |
|
|
if((!resp) || pop3_error(resp)){ |
138 |
|
|
err=resp?resp:strdup("popbegin.pop3_uidl: failed\n"); |
139 |
|
|
goto error; |
140 |
|
|
} |
141 |
|
|
s->uidl=uidl2array(resp); |
142 |
|
|
s->del=0; |
143 |
|
|
(*sp)=s; |
144 |
|
|
free(resp); |
145 |
|
|
return (NULL); |
146 |
|
|
|
147 |
|
|
error: |
148 |
|
|
if (s) { |
149 |
|
|
if (s->sock != BAD_SOCK) |
150 |
|
|
{ |
151 |
|
|
pop3_disconnect (s->sock, s->server); |
152 |
|
|
free(s->server); |
153 |
|
|
} |
154 |
|
|
free (s->connection); |
155 |
|
|
free(s); |
156 |
|
|
} |
157 |
|
|
return (err); |
158 |
|
|
} |
159 |
|
|
|
160 |
|
|
char* popgethead(popsession* session, int id){ |
161 |
|
|
/* returns the header of a message id between 1 and last or NULL if bad id or error */ |
162 |
|
|
char* resp; |
163 |
|
|
char* msg; |
164 |
|
|
if(!session){ |
165 |
|
|
return(NULL); |
166 |
|
|
} |
167 |
|
|
if((id > session->last) || (id < 1)){ |
168 |
|
|
return(NULL); |
169 |
|
|
} |
170 |
|
|
resp=pop3_top(session->sock,id,0); /* 0 means only header */ |
171 |
|
|
if((!resp) || pop3_error(resp)){ |
172 |
|
|
if(resp){ |
173 |
|
|
free(resp); |
174 |
|
|
} |
175 |
|
|
return(NULL); |
176 |
|
|
} |
177 |
|
|
msg=retr2msg(resp); |
178 |
|
|
if(!msg){ |
179 |
|
|
msg=resp; |
180 |
|
|
}else{ |
181 |
|
|
free(resp); |
182 |
|
|
} |
183 |
|
|
return(msg); |
184 |
|
|
} |
185 |
|
|
|
186 |
|
|
char* popgetmsg(popsession* session, int id){ |
187 |
|
|
/* returns a message id between 1 to last or NULL if bad id or error */ |
188 |
|
|
char* resp=NULL; |
189 |
|
|
char* msg=NULL; |
190 |
|
|
|
191 |
|
|
if(!session){ |
192 |
|
|
return(NULL); |
193 |
|
|
} |
194 |
|
|
if((id > session->last) || (id < 1)){ |
195 |
|
|
return(NULL); |
196 |
|
|
} |
197 |
|
|
resp=pop3_retr(session->sock,id); |
198 |
|
|
if((!resp) || pop3_error(resp)){ |
199 |
|
|
free(resp); |
200 |
|
|
return(NULL); |
201 |
|
|
} |
202 |
|
|
msg=retr2msg(resp); |
203 |
|
|
if(!msg){ |
204 |
|
|
msg=resp; |
205 |
|
|
}else{ |
206 |
|
|
free(resp); |
207 |
|
|
} |
208 |
|
|
if(session->del){ |
209 |
|
|
popdelmsg(session, id); |
210 |
|
|
} |
211 |
|
|
return(msg); |
212 |
|
|
} |
213 |
|
|
|
214 |
|
|
int popdelmsg(popsession* session, int id){ |
215 |
|
|
/* deletes a message 'id' on pop server */ |
216 |
|
|
/* returns -1 if no deletion (server error), 0 else */ |
217 |
|
|
/* sets session->sync to 0 if last id unsync-ed , 1 if OK */ |
218 |
|
|
char* resp; |
219 |
|
|
int ret; |
220 |
|
|
if(!session){ |
221 |
|
|
return -1; |
222 |
|
|
} |
223 |
|
|
if((id > session->last) || (id < 1)){ |
224 |
|
|
return -1; |
225 |
|
|
} |
226 |
|
|
/* actualy delete the email */ |
227 |
|
|
resp=pop3_dele(session->sock,id); |
228 |
|
|
if((!resp) || pop3_error(resp)){ |
229 |
|
|
free(resp); |
230 |
|
|
return -1; |
231 |
|
|
} |
232 |
|
|
free(resp); |
233 |
|
|
resp=pop3_stat(session->sock); |
234 |
|
|
if((!resp) || pop3_error(resp)){ |
235 |
|
|
session->sync=0; |
236 |
|
|
return -1; |
237 |
|
|
} |
238 |
|
|
ret = stat2bytes(resp); |
239 |
|
|
if (ret < 0) |
240 |
|
|
session->sync=0; |
241 |
|
|
|
242 |
|
|
else |
243 |
|
|
session->bytes=ret; |
244 |
|
|
ret = stat2num(resp); |
245 |
|
|
if (ret < 0) |
246 |
|
|
session->sync=0; |
247 |
|
|
else |
248 |
|
|
session->num=ret; |
249 |
|
|
free(resp); |
250 |
|
|
ret=poplast(session); /* check actual last id */ |
251 |
|
|
if (ret < 0){ |
252 |
|
|
session->sync=0; |
253 |
|
|
return -1; |
254 |
|
|
} |
255 |
|
|
session->last=ret; |
256 |
|
|
|
257 |
|
|
/* no more message of this id*/ |
258 |
|
|
session->list[id]=0; |
259 |
|
|
free(session->uidl[id]); |
260 |
|
|
session->uidl[id]=NULL; |
261 |
|
|
session->sync=1; |
262 |
|
|
return 0; |
263 |
|
|
} |
264 |
|
|
|
265 |
|
|
int popcancel(popsession* session){ |
266 |
|
|
/* cancel all previous deletions on pop server */ |
267 |
|
|
/* returns -1 if server error, 0 else */ |
268 |
|
|
char* resp; |
269 |
|
|
int ret; |
270 |
|
|
|
271 |
|
|
if(!session){ |
272 |
|
|
return(-1); |
273 |
|
|
} |
274 |
|
|
resp=pop3_rset(session->sock); |
275 |
|
|
if((!resp) || pop3_error(resp)){ |
276 |
|
|
free(resp); |
277 |
|
|
return(-1); |
278 |
|
|
} |
279 |
|
|
free(resp); |
280 |
|
|
resp=pop3_stat(session->sock); |
281 |
|
|
if((!resp) || pop3_error(resp)){ |
282 |
|
|
session->sync=0; |
283 |
|
|
return(-1); |
284 |
|
|
} |
285 |
|
|
/* sync number of bytes */ |
286 |
|
|
ret = stat2bytes(resp); |
287 |
|
|
if (ret < 0) |
288 |
|
|
session->sync=0; |
289 |
|
|
else |
290 |
|
|
session->bytes=ret; |
291 |
|
|
/* sync number of messages */ |
292 |
|
|
ret = stat2num(resp); |
293 |
|
|
if (ret < 0) |
294 |
|
|
session->sync=0; |
295 |
|
|
else |
296 |
|
|
session->num=ret; |
297 |
|
|
/* sync last mail id */ |
298 |
|
|
/* safe to use stat2num here since we cancel */ |
299 |
|
|
ret = stat2num(resp); |
300 |
|
|
if (ret < 0) |
301 |
|
|
session->sync=0; |
302 |
|
|
else |
303 |
|
|
session->last=ret; |
304 |
|
|
free(resp); |
305 |
|
|
resp=pop3_list(session->sock,0); |
306 |
|
|
if((!resp) || pop3_error(resp)){ |
307 |
|
|
session->sync=0; |
308 |
|
|
return -1; |
309 |
|
|
} |
310 |
|
|
freelistarray(session->list); |
311 |
|
|
session->list=list2array(resp); |
312 |
|
|
free(resp); |
313 |
|
|
resp=pop3_uidl(session->sock,0); |
314 |
|
|
if((!resp) || pop3_error(resp)){ |
315 |
|
|
session->sync=0; |
316 |
|
|
return -1; |
317 |
|
|
} |
318 |
|
|
freeuidlarray(session->uidl); |
319 |
|
|
session->uidl=uidl2array(resp); |
320 |
|
|
free(resp); |
321 |
|
|
session->sync=1; |
322 |
|
|
return 0; |
323 |
|
|
} |
324 |
|
|
|
325 |
|
|
void popend(popsession* session){ |
326 |
|
|
/* quit and destroys pop session */ |
327 |
|
|
int i; |
328 |
|
|
char* resp; |
329 |
|
|
|
330 |
|
|
if(!session) |
331 |
|
|
return; |
332 |
|
|
resp=pop3_quit(session->sock); |
333 |
|
|
free(resp); |
334 |
|
|
pop3_disconnect(session->sock, session->server); |
335 |
|
|
free(session->server); |
336 |
|
|
free(session->connection); |
337 |
|
|
free(session->list); |
338 |
|
|
for(i=0;i<=session->last;i++){ |
339 |
|
|
free(session->uidl[i]); |
340 |
|
|
} |
341 |
|
|
free(session->uidl); |
342 |
|
|
free(session); |
343 |
|
|
return; |
344 |
|
|
} |
345 |
|
|
|
346 |
|
|
int popnum(popsession* session){ |
347 |
|
|
/* returns the number of current (non-deleted) messages */ |
348 |
|
|
char* r=NULL; |
349 |
|
|
int n; |
350 |
|
|
|
351 |
|
|
if(!session) |
352 |
|
|
return -1; |
353 |
|
|
r=pop3_stat(session->sock); |
354 |
|
|
if(pop3_error(r)) { |
355 |
|
|
free(r); |
356 |
|
|
return -1; /* error (timeout, etc..) */ |
357 |
|
|
} |
358 |
|
|
n=stat2num(r); |
359 |
|
|
free(r); |
360 |
|
|
return(n); |
361 |
|
|
} |
362 |
|
|
|
363 |
|
|
int poplast(popsession* session){ |
364 |
|
|
/* return the id of the last downloadable (non-deleted) message */ |
365 |
|
|
/* thanks to Francesco Gennai <francesco.gennai@isti.cnr.it> */ |
366 |
|
|
int i=0; |
367 |
|
|
char* lines=NULL; |
368 |
|
|
char* p=NULL; |
369 |
|
|
|
370 |
|
|
if(!session) |
371 |
|
|
return -1; |
372 |
|
|
lines=pop3_list(session->sock, 0); /* 0 means 'all' */ |
373 |
|
|
if(pop3_error(lines)) { |
374 |
|
|
free(lines); |
375 |
|
|
return -1; /* error (timeout, etc..) */ |
376 |
|
|
} |
377 |
|
|
p=lines; |
378 |
|
|
p=nextline(p); /* skip +OK */ |
379 |
|
|
while (p[0]!='.'){ /* dot means list terminated */ |
380 |
|
|
i = atoi(p); /* first number is the id */ |
381 |
|
|
p=nextline(p); |
382 |
|
|
} |
383 |
|
|
/* i is now the greatest id */ |
384 |
|
|
free(lines); |
385 |
|
|
return(i); |
386 |
|
|
} |
387 |
|
|
|
388 |
|
|
int popchkmsg(popsession* session, int id) { |
389 |
|
|
/* check if the message 'id' is accessible in the current session */ |
390 |
|
|
/* thanks to Francesco Gennai <francesco.gennai@isti.cnr.it> */ |
391 |
|
|
if (popmsguid(session, id)) return 1; /* anything but 0 */ |
392 |
|
|
return 0; |
393 |
|
|
} |
394 |
|
|
|
395 |
|
|
/* re-synchronize the session object from the server */ |
396 |
|
|
int popsync(popsession* session) { |
397 |
|
|
char* resp; |
398 |
|
|
int ret; |
399 |
|
|
|
400 |
|
|
if(!session){ |
401 |
|
|
return(-1); |
402 |
|
|
} |
403 |
|
|
resp=pop3_stat(session->sock); |
404 |
|
|
if((!resp) || pop3_error(resp)){ |
405 |
|
|
session->sync=0; |
406 |
|
|
return(-1); |
407 |
|
|
} |
408 |
|
|
session->bytes=stat2bytes(resp); |
409 |
|
|
session->num=stat2num(resp); |
410 |
|
|
ret=poplast(session); /* check actual last id */ |
411 |
|
|
if (ret < 0){ |
412 |
|
|
session->sync=0; |
413 |
|
|
return(-1); |
414 |
|
|
} |
415 |
|
|
session->last=ret; |
416 |
|
|
free(resp); |
417 |
|
|
resp=pop3_list(session->sock,0); |
418 |
|
|
if((!resp) || pop3_error(resp)){ |
419 |
|
|
session->sync=0; |
420 |
|
|
return(-1); |
421 |
|
|
} |
422 |
|
|
freelistarray(session->list); |
423 |
|
|
session->list=list2array(resp); |
424 |
|
|
free(resp); |
425 |
|
|
resp=pop3_uidl(session->sock,0); |
426 |
|
|
if((!resp) || pop3_error(resp)){ |
427 |
|
|
session->sync=0; |
428 |
|
|
return(-1); |
429 |
|
|
} |
430 |
|
|
freeuidlarray(session->uidl); |
431 |
|
|
session->uidl=uidl2array(resp); |
432 |
|
|
free(resp); |
433 |
|
|
session->sync=1; |
434 |
|
|
return(0); |
435 |
|
|
} |
436 |
|
|
|