Review: thread.c
[s-mailx.git] / pop3.c
blob345b9b2ede82d090a40af312d01426a5b699ae48
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ POP3 (RFCs 1939, 2595) client.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2002
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 EMPTY_FILE(pop3)
45 #ifdef HAVE_POP3
47 #define POP3_ANSWER(RV,ACTIONSTOP) \
48 do if (((RV) = pop3_answer(mp)) == STOP) {\
49 ACTIONSTOP;\
50 } while (0)
52 #define POP3_OUT(RV,X,Y,ACTIONSTOP) \
53 do {\
54 if (((RV) = pop3_finish(mp)) == STOP) {\
55 ACTIONSTOP;\
57 if (options & OPT_VERBOSE)\
58 fprintf(stderr, ">>> %s", X);\
59 mp->mb_active |= Y;\
60 if (((RV) = swrite(&mp->mb_sock, X)) == STOP) {\
61 ACTIONSTOP;\
63 } while (0)
65 static char *_pop3_buf;
66 static size_t _pop3_bufsize;
67 static sigjmp_buf _pop3_jmp;
68 static sighandler_type _pop3_savealrm;
69 static int _pop3_keepalive;
70 static int volatile _pop3_lock;
72 /* Perform entire login handshake */
73 static enum okay _pop3_login(struct mailbox *mp, char *xuser, char *pass,
74 char const *uhp, char const *xserver);
76 /* APOP: get greeting credential or NULL */
77 #ifdef HAVE_MD5
78 static char * _pop3_lookup_apop_timestamp(char const *bp);
79 #endif
81 static bool_t _pop3_use_starttls(char const *uhp);
83 /* APOP: shall we use it (for uhp)? */
84 static bool_t _pop3_no_apop(char const *uhp);
86 /* Several authentication methods */
87 #ifdef HAVE_MD5
88 static enum okay _pop3_auth_apop(struct mailbox *mp, char *xuser, char *pass,
89 char const *ts);
90 #endif
91 static enum okay _pop3_auth_plain(struct mailbox *mp, char *xuser, char *pass);
93 static void pop3_timer_off(void);
94 static enum okay pop3_answer(struct mailbox *mp);
95 static enum okay pop3_finish(struct mailbox *mp);
96 static void pop3catch(int s);
97 static void _pop3_maincatch(int s);
98 static enum okay pop3_noop1(struct mailbox *mp);
99 static void pop3alarm(int s);
100 static enum okay pop3_stat(struct mailbox *mp, off_t *size, int *cnt);
101 static enum okay pop3_list(struct mailbox *mp, int n, size_t *size);
102 static void pop3_init(struct mailbox *mp, int n);
103 static void pop3_dates(struct mailbox *mp);
104 static void pop3_setptr(struct mailbox *mp);
105 static enum okay pop3_get(struct mailbox *mp, struct message *m,
106 enum needspec need);
107 static enum okay pop3_exit(struct mailbox *mp);
108 static enum okay pop3_delete(struct mailbox *mp, int n);
109 static enum okay pop3_update(struct mailbox *mp);
111 static enum okay
112 _pop3_login(struct mailbox *mp, char *xuser, char *pass, char const *uhp,
113 char const *xserver)
115 #ifdef HAVE_MD5
116 char *ts;
117 #endif
118 char *cp;
119 enum okay rv;
120 NYD_ENTER;
122 /* Get the greeting, check wether APOP is advertised */
123 POP3_ANSWER(rv, goto jleave);
124 #ifdef HAVE_MD5
125 ts = _pop3_lookup_apop_timestamp(_pop3_buf);
126 #endif
128 if ((cp = strchr(xserver, ':')) != NULL) { /* TODO GENERIC URI PARSE! */
129 size_t l = PTR2SIZE(cp - xserver);
130 char *x = salloc(l +1);
131 memcpy(x, xserver, l);
132 x[l] = '\0';
133 xserver = x;
136 /* If not yet secured, can we upgrade to TLS? */
137 #ifdef HAVE_SSL
138 if (mp->mb_sock.s_use_ssl == 0 && _pop3_use_starttls(uhp)) {
139 POP3_OUT(rv, "STLS\r\n", MB_COMD, goto jleave);
140 POP3_ANSWER(rv, goto jleave);
141 if ((rv = ssl_open(xserver, &mp->mb_sock, uhp)) != OKAY)
142 goto jleave;
144 #else
145 if (_pop3_use_starttls(uhp)) {
146 fprintf(stderr, "No SSL support compiled in.\n");
147 rv = STOP;
148 goto jleave;
150 #endif
152 /* Use the APOP single roundtrip? */
153 if (!_pop3_no_apop(uhp)) {
154 #ifdef HAVE_MD5
155 if (ts != NULL) {
156 if ((rv = _pop3_auth_apop(mp, xuser, pass, ts)) != OKAY)
157 fprintf(stderr, tr(276,
158 "POP3 `APOP' authentication failed, "
159 "maybe try setting *pop3-no-apop*\n"));
160 goto jleave;
161 } else
162 #endif
163 if (options & OPT_VERBOSE)
164 fprintf(stderr, tr(204, "No POP3 `APOP' support "
165 "available, sending password in clear text\n"));
167 rv = _pop3_auth_plain(mp, xuser, pass);
168 jleave:
169 NYD_LEAVE;
170 return rv;
173 #ifdef HAVE_MD5
174 static char *
175 _pop3_lookup_apop_timestamp(char const *bp)
177 /* RFC 1939:
178 * A POP3 server which implements the APOP command will include
179 * a timestamp in its banner greeting. The syntax of the timestamp
180 * corresponds to the `msg-id' in [RFC822]
181 * RFC 822:
182 * msg-id = "<" addr-spec ">"
183 * addr-spec = local-part "@" domain */
184 char const *cp, *ep;
185 size_t tl;
186 char *rp = NULL;
187 bool_t hadat = FAL0;
188 NYD_ENTER;
190 if ((cp = strchr(bp, '<')) == NULL)
191 goto jleave;
193 /* xxx What about malformed APOP timestamp (<@>) here? */
194 for (ep = cp; *ep != '\0'; ++ep) {
195 if (spacechar(*ep))
196 goto jleave;
197 else if (*ep == '@')
198 hadat = TRU1;
199 else if (*ep == '>') {
200 if (!hadat)
201 goto jleave;
202 break;
205 if (*ep != '>')
206 goto jleave;
208 tl = PTR2SIZE(++ep - cp);
209 rp = salloc(tl +1);
210 memcpy(rp, cp, tl);
211 rp[tl] = '\0';
212 jleave:
213 NYD_LEAVE;
214 return rp;
216 #endif
218 static bool_t
219 _pop3_use_starttls(char const *uhp)
221 bool_t rv;
222 NYD_ENTER;
224 if (!(rv = ok_blook(pop3_use_starttls)))
225 rv = vok_blook(savecat("pop3-use-starttls-", uhp));
226 NYD_LEAVE;
227 return rv;
230 static bool_t
231 _pop3_no_apop(char const *uhp)
233 bool_t rv;
234 NYD_ENTER;
236 if (!(rv = ok_blook(pop3_no_apop)))
237 rv = vok_blook(savecat("pop3-no-apop-", uhp));
238 NYD_LEAVE;
239 return rv;
242 #ifdef HAVE_MD5
243 static enum okay
244 _pop3_auth_apop(struct mailbox *mp, char *xuser, char *pass, char const *ts)
246 unsigned char digest[16];
247 char hex[MD5TOHEX_SIZE];
248 md5_ctx ctx;
249 size_t tl, i;
250 char *user, *cp;
251 enum okay rv = STOP;
252 NYD_ENTER;
254 for (tl = strlen(ts);;) {
255 user = xuser;
256 if (!getcredentials(&user, &pass))
257 break;
259 md5_init(&ctx);
260 md5_update(&ctx, (unsigned char*)UNCONST(ts), tl);
261 md5_update(&ctx, (unsigned char*)pass, strlen(pass));
262 md5_final(digest, &ctx);
263 md5tohex(hex, digest);
265 i = strlen(user);
266 cp = ac_alloc(5 + i + 1 + MD5TOHEX_SIZE + 2 +1);
268 memcpy(cp, "APOP ", 5);
269 memcpy(cp + 5, user, i);
270 i += 5;
271 cp[i++] = ' ';
272 memcpy(cp + i, hex, MD5TOHEX_SIZE);
273 i += MD5TOHEX_SIZE;
274 memcpy(cp + i, "\r\n\0", 3);
275 POP3_OUT(rv, cp, MB_COMD, goto jcont);
276 POP3_ANSWER(rv, goto jcont);
277 rv = OKAY;
278 jcont:
279 ac_free(cp);
280 if (rv == OKAY)
281 break;
282 pass = NULL;
284 NYD_LEAVE;
285 return rv;
287 #endif /* HAVE_MD5 */
289 static enum okay
290 _pop3_auth_plain(struct mailbox *mp, char *xuser, char *pass)
292 char *user, *cp;
293 size_t ul, pl;
294 enum okay rv = STOP;
295 NYD_ENTER;
297 /* The USER/PASS plain text version */
298 for (;;) {
299 user = xuser;
300 if (!getcredentials(&user, &pass))
301 break;
303 ul = strlen(user);
304 pl = strlen(pass);
305 cp = ac_alloc(MAX(ul, pl) + 5 + 2 +1);
307 memcpy(cp, "USER ", 5);
308 memcpy(cp + 5, user, ul);
309 memcpy(cp + 5 + ul, "\r\n\0", 3);
310 POP3_OUT(rv, cp, MB_COMD, goto jcont);
311 POP3_ANSWER(rv, goto jcont);
313 memcpy(cp, "PASS ", 5);
314 memcpy(cp + 5, pass, pl);
315 memcpy(cp + 5 + pl, "\r\n\0", 3);
316 POP3_OUT(rv, cp, MB_COMD, goto jcont);
317 POP3_ANSWER(rv, goto jcont);
318 rv = OKAY;
319 jcont:
320 ac_free(cp);
321 if (rv == OKAY)
322 break;
323 pass = NULL;
325 NYD_LEAVE;
326 return rv;
329 static void
330 pop3_timer_off(void)
332 NYD_ENTER;
333 if (_pop3_keepalive > 0) {
334 alarm(0);
335 safe_signal(SIGALRM, _pop3_savealrm);
337 NYD_LEAVE;
340 static enum okay
341 pop3_answer(struct mailbox *mp)
343 int sz;
344 enum okay rv = STOP;
345 NYD_ENTER;
347 jretry:
348 if ((sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock)) > 0) {
349 if ((mp->mb_active & (MB_COMD | MB_MULT)) == MB_MULT)
350 goto jmultiline;
351 if (options & OPT_VERBOSE)
352 fputs(_pop3_buf, stderr);
353 switch (*_pop3_buf) {
354 case '+':
355 rv = OKAY;
356 mp->mb_active &= ~MB_COMD;
357 break;
358 case '-':
359 rv = STOP;
360 mp->mb_active = MB_NONE;
361 fprintf(stderr, tr(218, "POP3 error: %s"), _pop3_buf);
362 break;
363 default:
364 /* If the answer starts neither with '+' nor with '-', it must be part
365 * of a multiline response. Get lines until a single dot appears */
366 jmultiline:
367 while (_pop3_buf[0] != '.' || _pop3_buf[1] != '\r' ||
368 _pop3_buf[2] != '\n' || _pop3_buf[3] != '\0') {
369 sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock);
370 if (sz <= 0)
371 goto jeof;
373 mp->mb_active &= ~MB_MULT;
374 if (mp->mb_active != MB_NONE)
375 goto jretry;
377 } else {
378 jeof:
379 rv = STOP;
380 mp->mb_active = MB_NONE;
382 NYD_LEAVE;
383 return rv;
386 static enum okay
387 pop3_finish(struct mailbox *mp)
389 NYD_ENTER;
390 while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
391 pop3_answer(mp);
392 NYD_LEAVE;
393 return OKAY;
396 static void
397 pop3catch(int s)
399 NYD_X; /* Signal handler */
400 termios_state_reset();
401 switch (s) {
402 case SIGINT:
403 fprintf(stderr, tr(102, "Interrupt\n"));
404 siglongjmp(_pop3_jmp, 1);
405 break;
406 case SIGPIPE:
407 fprintf(stderr, "Received SIGPIPE during POP3 operation\n");
408 break;
412 static void
413 _pop3_maincatch(int s)
415 NYD_X; /* Signal handler */
416 UNUSED(s);
418 if (interrupts++ == 0)
419 fprintf(stderr, tr(102, "Interrupt\n"));
420 else
421 onintr(0);
424 static enum okay
425 pop3_noop1(struct mailbox *mp)
427 enum okay rv;
428 NYD_ENTER;
430 POP3_OUT(rv, "NOOP\r\n", MB_COMD, goto jleave);
431 POP3_ANSWER(rv, goto jleave);
432 jleave:
433 NYD_LEAVE;
434 return rv;
437 static void
438 pop3alarm(int s)
440 sighandler_type volatile saveint, savepipe;
441 NYD_X; /* Signal handler */
442 UNUSED(s);
444 if (_pop3_lock++ == 0) {
445 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
446 safe_signal(SIGINT, &_pop3_maincatch);
447 savepipe = safe_signal(SIGPIPE, SIG_IGN);
448 if (sigsetjmp(_pop3_jmp, 1)) {
449 safe_signal(SIGINT, saveint);
450 safe_signal(SIGPIPE, savepipe);
451 goto jbrk;
453 if (savepipe != SIG_IGN)
454 safe_signal(SIGPIPE, pop3catch);
455 if (pop3_noop1(&mb) != OKAY) {
456 safe_signal(SIGINT, saveint);
457 safe_signal(SIGPIPE, savepipe);
458 goto jleave;
460 safe_signal(SIGINT, saveint);
461 safe_signal(SIGPIPE, savepipe);
463 jbrk:
464 alarm(_pop3_keepalive);
465 jleave:
466 --_pop3_lock;
469 static enum okay
470 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
472 char *cp;
473 enum okay rv;
474 NYD_ENTER;
476 POP3_OUT(rv, "STAT\r\n", MB_COMD, goto jleave);
477 POP3_ANSWER(rv, goto jleave);
479 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
481 while (*cp != '\0' && spacechar(*cp))
482 ++cp;
484 if (*cp != '\0') {
485 *cnt = (int)strtol(cp, NULL, 10);
486 while (*cp != '\0' && !spacechar(*cp))
487 ++cp;
488 while (*cp != '\0' && spacechar(*cp))
489 ++cp;
490 if (*cp != '\0')
491 *size = (int)strtol(cp, NULL, 10);
492 else
493 rv = STOP;
494 } else
495 rv = STOP;
497 if (rv == STOP)
498 fprintf(stderr, tr(260, "invalid POP3 STAT response: %s\n"), _pop3_buf);
499 jleave:
500 NYD_LEAVE;
501 return rv;
504 static enum okay
505 pop3_list(struct mailbox *mp, int n, size_t *size)
507 char o[LINESIZE], *cp;
508 enum okay rv;
509 NYD_ENTER;
511 snprintf(o, sizeof o, "LIST %u\r\n", n);
512 POP3_OUT(rv, o, MB_COMD, goto jleave);
513 POP3_ANSWER(rv, goto jleave);
515 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
517 while (*cp != '\0' && spacechar(*cp))
518 ++cp;
519 while (*cp != '\0' && !spacechar(*cp))
520 ++cp;
521 while (*cp != '\0' && spacechar(*cp))
522 ++cp;
523 if (*cp != '\0')
524 *size = (size_t)strtol(cp, NULL, 10);
525 else
526 *size = 0;
527 jleave:
528 NYD_LEAVE;
529 return rv;
532 static void
533 pop3_init(struct mailbox *mp, int n)
535 struct message *m;
536 char *cp;
537 NYD_ENTER;
539 m = message + n;
540 m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
541 m->m_block = 0;
542 m->m_offset = 0;
543 pop3_list(mp, PTR2SIZE(m - message + 1), &m->m_xsize);
545 if ((cp = hfield1("status", m)) != NULL) {
546 while (*cp != '\0') {
547 if (*cp == 'R')
548 m->m_flag |= MREAD;
549 else if (*cp == 'O')
550 m->m_flag &= ~MNEW;
551 ++cp;
554 NYD_LEAVE;
557 static void
558 pop3_dates(struct mailbox *mp)
560 size_t i;
561 NYD_ENTER;
562 UNUSED(mp);
564 for (i = 0; UICMP(z, i, <, msgCount); ++i)
565 substdate(message + i);
566 NYD_LEAVE;
569 static void
570 pop3_setptr(struct mailbox *mp)
572 size_t i;
573 NYD_ENTER;
575 message = scalloc(msgCount + 1, sizeof *message);
576 for (i = 0; UICMP(z, i, <, msgCount); ++i)
577 pop3_init(mp, i);
579 setdot(message);
580 message[msgCount].m_size = 0;
581 message[msgCount].m_lines = 0;
582 pop3_dates(mp);
583 NYD_LEAVE;
586 static enum okay
587 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
589 char o[LINESIZE], *line, *lp;
590 sighandler_type volatile saveint, savepipe;
591 size_t linesize, linelen, size;
592 int number, emptyline, lines;
593 off_t offset;
594 enum okay rv;
595 NYD_ENTER;
597 line = NULL; /* TODO line pool */
598 saveint = savepipe = SIG_IGN;
599 linesize = 0;
600 number = (int)PTR2SIZE(m - message + 1);
601 emptyline = 0;
602 rv = STOP;
604 if (mp->mb_sock.s_fd < 0) {
605 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
606 ++_pop3_lock;
607 goto jleave;
610 if (_pop3_lock++ == 0) {
611 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
612 safe_signal(SIGINT, &_pop3_maincatch);
613 savepipe = safe_signal(SIGPIPE, SIG_IGN);
614 if (sigsetjmp(_pop3_jmp, 1)) {
615 safe_signal(SIGINT, saveint);
616 safe_signal(SIGPIPE, savepipe);
617 --_pop3_lock;
618 goto jleave;
620 if (savepipe != SIG_IGN)
621 safe_signal(SIGPIPE, pop3catch);
624 fseek(mp->mb_otf, 0L, SEEK_END);
625 offset = ftell(mp->mb_otf);
626 jretry:
627 switch (need) {
628 case NEED_HEADER:
629 snprintf(o, sizeof o, "TOP %u 0\r\n", number);
630 break;
631 case NEED_BODY:
632 snprintf(o, sizeof o, "RETR %u\r\n", number);
633 break;
634 case NEED_UNSPEC:
635 abort(); /* XXX */
637 POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
639 if (pop3_answer(mp) == STOP) {
640 if (need == NEED_HEADER) {
641 /* The TOP POP3 command is optional, so retry with entire message */
642 need = NEED_BODY;
643 goto jretry;
645 goto jleave;
648 size = 0;
649 lines = 0;
650 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
651 if (line[0] == '.' && line[1] == '\r' && line[2] == '\n' &&
652 line[3] == '\0') {
653 mp->mb_active &= ~MB_MULT;
654 break;
656 if (line[0] == '.') {
657 lp = line + 1;
658 --linelen;
659 } else
660 lp = line;
661 /* TODO >>
662 * Need to mask 'From ' lines. This cannot be done properly
663 * since some servers pass them as 'From ' and others as
664 * '>From '. Although one could identify the first kind of
665 * server in principle, it is not possible to identify the
666 * second as '>From ' may also come from a server of the
667 * first type as actual data. So do what is absolutely
668 * necessary only - mask 'From '.
670 * If the line is the first line of the message header, it
671 * is likely a real 'From ' line. In this case, it is just
672 * ignored since it violates all standards.
673 * TODO i have *never* seen the latter?!?!?
674 * TODO <<
676 /* Since we simply copy over data without doing any transfer
677 * encoding reclassification/adjustment we *have* to perform
678 * RFC 4155 compliant From_ quoting here */
679 if (is_head(lp, linelen)) {
680 if (lines == 0)
681 continue;
682 fputc('>', mp->mb_otf);
683 ++size;
685 lines++;
686 if (lp[linelen-1] == '\n' && (linelen == 1 ||
687 lp[linelen-2] == '\r')) {
688 emptyline = linelen <= 2;
689 if (linelen > 2)
690 fwrite(lp, 1, linelen - 2, mp->mb_otf);
691 fputc('\n', mp->mb_otf);
692 size += linelen - 1;
693 } else {
694 emptyline = 0;
695 fwrite(lp, 1, linelen, mp->mb_otf);
696 size += linelen;
699 if (!emptyline) {
700 /* This is very ugly; but some POP3 daemons don't end a
701 * message with \r\n\r\n, and we need \n\n for mbox format */
702 fputc('\n', mp->mb_otf);
703 ++lines;
704 ++size;
706 m->m_size = size;
707 m->m_lines = lines;
708 m->m_block = mailx_blockof(offset);
709 m->m_offset = mailx_offsetof(offset);
710 fflush(mp->mb_otf);
712 switch (need) {
713 case NEED_HEADER:
714 m->m_have |= HAVE_HEADER;
715 break;
716 case NEED_BODY:
717 m->m_have |= HAVE_HEADER | HAVE_BODY;
718 m->m_xlines = m->m_lines;
719 m->m_xsize = m->m_size;
720 break;
721 case NEED_UNSPEC:
722 break;
725 rv = OKAY;
726 jleave:
727 if (line != NULL)
728 free(line);
729 if (saveint != SIG_IGN)
730 safe_signal(SIGINT, saveint);
731 if (savepipe != SIG_IGN)
732 safe_signal(SIGPIPE, savepipe);
733 --_pop3_lock;
734 NYD_LEAVE;
735 if (interrupts)
736 onintr(0);
737 return rv;
740 static enum okay
741 pop3_exit(struct mailbox *mp)
743 enum okay rv;
744 NYD_ENTER;
746 POP3_OUT(rv, "QUIT\r\n", MB_COMD, goto jleave);
747 POP3_ANSWER(rv, goto jleave);
748 jleave:
749 NYD_LEAVE;
750 return rv;
753 static enum okay
754 pop3_delete(struct mailbox *mp, int n)
756 char o[LINESIZE];
757 enum okay rv;
758 NYD_ENTER;
760 snprintf(o, sizeof o, "DELE %u\r\n", n);
761 POP3_OUT(rv, o, MB_COMD, goto jleave);
762 POP3_ANSWER(rv, goto jleave);
763 jleave:
764 NYD_LEAVE;
765 return rv;
768 static enum okay
769 pop3_update(struct mailbox *mp)
771 struct message *m;
772 int dodel, c, gotcha, held;
773 NYD_ENTER;
775 if (!edit) {
776 holdbits();
777 c = 0;
778 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
779 if (m->m_flag & MBOX)
780 ++c;
781 if (c > 0)
782 makembox();
785 gotcha = held = 0;
786 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
787 if (edit)
788 dodel = m->m_flag & MDELETED;
789 else
790 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
791 if (dodel) {
792 pop3_delete(mp, PTR2SIZE(m - message + 1));
793 ++gotcha;
794 } else
795 ++held;
797 if (gotcha && edit) {
798 printf(tr(168, "\"%s\" "), displayname);
799 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
800 ? tr(170, "complete\n") : tr(212, "updated.\n"));
801 } else if (held && !edit) {
802 if (held == 1)
803 printf(tr(155, "Held 1 message in %s\n"), displayname);
804 else
805 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
807 fflush(stdout);
808 NYD_LEAVE;
809 return OKAY;
812 FL enum okay
813 pop3_noop(void)
815 sighandler_type volatile saveint, savepipe;
816 enum okay rv = STOP;
817 NYD_ENTER;
819 _pop3_lock = 1;
820 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
821 safe_signal(SIGINT, &_pop3_maincatch);
822 savepipe = safe_signal(SIGPIPE, SIG_IGN);
823 if (sigsetjmp(_pop3_jmp, 1) == 0) {
824 if (savepipe != SIG_IGN)
825 safe_signal(SIGPIPE, pop3catch);
826 rv = pop3_noop1(&mb);
828 safe_signal(SIGINT, saveint);
829 safe_signal(SIGPIPE, savepipe);
830 _pop3_lock = 0;
831 NYD_LEAVE;
832 return rv;
835 FL int
836 pop3_setfile(char const *server, int nmail, int isedit)
838 struct sock so;
839 sighandler_type saveint, savepipe;
840 char * volatile user, * volatile pass;
841 char const *cp, *uhp, * volatile sp = server;
842 int use_ssl = 0, rv = 1;
843 NYD_ENTER;
845 if (nmail)
846 goto jleave;
848 if (!strncmp(sp, "pop3://", 7)) {
849 sp = sp + 7;
850 use_ssl = 0;
851 #ifdef HAVE_SSL
852 } else if (!strncmp(sp, "pop3s://", 8)) {
853 sp = sp + 8;
854 use_ssl = 1;
855 #endif
857 uhp = sp;
858 pass = lookup_password_for_token(uhp);
860 if ((cp = last_at_before_slash(sp)) != NULL) {
861 user = salloc(cp - sp +1);
862 memcpy(user, sp, cp - sp);
863 user[cp - sp] = '\0';
864 sp = cp + 1;
865 user = urlxdec(user);
866 } else
867 user = NULL;
869 if (sopen(sp, &so, use_ssl, uhp, (use_ssl ? "pop3s" : "pop3")) != OKAY) {
870 rv = -1;
871 goto jleave;
873 quit();
874 edit = (isedit != 0);
875 if (mb.mb_sock.s_fd >= 0)
876 sclose(&mb.mb_sock);
877 if (mb.mb_itf) {
878 fclose(mb.mb_itf);
879 mb.mb_itf = NULL;
881 if (mb.mb_otf) {
882 fclose(mb.mb_otf);
883 mb.mb_otf = NULL;
885 initbox(server);
886 mb.mb_type = MB_VOID;
887 _pop3_lock = 1;
888 mb.mb_sock = so;
890 saveint = safe_signal(SIGINT, SIG_IGN);
891 savepipe = safe_signal(SIGPIPE, SIG_IGN);
892 if (sigsetjmp(_pop3_jmp, 1)) {
893 sclose(&mb.mb_sock);
894 safe_signal(SIGINT, saveint);
895 safe_signal(SIGPIPE, savepipe);
896 _pop3_lock = 0;
897 goto jleave;
899 if (saveint != SIG_IGN)
900 safe_signal(SIGINT, pop3catch);
901 if (savepipe != SIG_IGN)
902 safe_signal(SIGPIPE, pop3catch);
904 if ((cp = ok_vlook(pop3_keepalive)) != NULL) {
905 if ((_pop3_keepalive = (int)strtol(cp, NULL, 10)) > 0) {
906 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
907 alarm(_pop3_keepalive);
911 mb.mb_sock.s_desc = "POP3";
912 mb.mb_sock.s_onclose = pop3_timer_off;
913 if (_pop3_login(&mb, user, pass, uhp, sp) != OKAY ||
914 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
915 sclose(&mb.mb_sock);
916 pop3_timer_off();
917 safe_signal(SIGINT, saveint);
918 safe_signal(SIGPIPE, savepipe);
919 _pop3_lock = 0;
920 goto jleave;
922 mb.mb_type = MB_POP3;
923 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
924 pop3_setptr(&mb);
925 setmsize(msgCount);
926 sawcom = FAL0;
928 safe_signal(SIGINT, saveint);
929 safe_signal(SIGPIPE, savepipe);
930 _pop3_lock = 0;
931 if (!edit && msgCount == 0) {
932 if (mb.mb_type == MB_POP3 && !ok_blook(emptystart))
933 fprintf(stderr, tr(258, "No mail at %s\n"), server);
934 goto jleave;
936 rv = 0;
937 jleave:
938 NYD_LEAVE;
939 return rv;
942 FL enum okay
943 pop3_header(struct message *m)
945 enum okay rv;
946 NYD_ENTER;
948 rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
949 NYD_LEAVE;
950 return rv;
953 FL enum okay
954 pop3_body(struct message *m)
956 enum okay rv;
957 NYD_ENTER;
959 rv = pop3_get(&mb, m, NEED_BODY);
960 NYD_LEAVE;
961 return rv;
964 FL void
965 pop3_quit(void)
967 sighandler_type volatile saveint, savepipe;
968 NYD_ENTER;
970 if (mb.mb_sock.s_fd < 0) {
971 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
972 goto jleave;
975 _pop3_lock = 1;
976 saveint = safe_signal(SIGINT, SIG_IGN);
977 savepipe = safe_signal(SIGPIPE, SIG_IGN);
978 if (sigsetjmp(_pop3_jmp, 1)) {
979 safe_signal(SIGINT, saveint);
980 safe_signal(SIGPIPE, saveint);
981 _pop3_lock = 0;
982 goto jleave;
984 if (saveint != SIG_IGN)
985 safe_signal(SIGINT, pop3catch);
986 if (savepipe != SIG_IGN)
987 safe_signal(SIGPIPE, pop3catch);
988 pop3_update(&mb);
989 pop3_exit(&mb);
990 sclose(&mb.mb_sock);
991 safe_signal(SIGINT, saveint);
992 safe_signal(SIGPIPE, savepipe);
993 _pop3_lock = 0;
994 jleave:
995 NYD_LEAVE;
997 #endif /* HAVE_POP3 */
999 /* vim:set fenc=utf-8:s-it-mode */