nail.h: struct message.m_spamscore: ui_it -> ui32_t
[s-mailx.git] / pop3.c
blobe6c23c7d2765d96851c27e073bcf4e34513d91a8
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 = (size_t)(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 static char *
174 _pop3_lookup_apop_timestamp(char const *bp)
176 /* RFC 1939:
177 * A POP3 server which implements the APOP command will include
178 * a timestamp in its banner greeting. The syntax of the timestamp
179 * corresponds to the `msg-id' in [RFC822]
180 * RFC 822:
181 * msg-id = "<" addr-spec ">"
182 * addr-spec = local-part "@" domain */
183 char const *cp, *ep;
184 size_t tl;
185 char *rp = NULL;
186 bool_t hadat = FAL0;
187 NYD_ENTER;
189 if ((cp = strchr(bp, '<')) == NULL)
190 goto jleave;
192 /* xxx What about malformed APOP timestamp (<@>) here? */
193 for (ep = cp; *ep; ep++) {
194 if (spacechar(*ep))
195 goto jleave;
196 else if (*ep == '@')
197 hadat = TRU1;
198 else if (*ep == '>') {
199 if (!hadat)
200 goto jleave;
201 break;
204 if (*ep != '>')
205 goto jleave;
207 tl = PTR2SIZE(++ep - cp);
208 rp = salloc(tl +1);
209 memcpy(rp, cp, tl);
210 rp[tl] = '\0';
211 jleave:
212 NYD_LEAVE;
213 return rp;
216 static bool_t
217 _pop3_use_starttls(char const *uhp)
219 bool_t rv;
220 NYD_ENTER;
222 if (!(rv = ok_blook(pop3_use_starttls)))
223 rv = vok_blook(savecat("pop3-use-starttls-", uhp));
224 NYD_LEAVE;
225 return rv;
228 static bool_t
229 _pop3_no_apop(char const *uhp)
231 bool_t rv;
232 NYD_ENTER;
234 if (!(rv = ok_blook(pop3_no_apop)))
235 rv = vok_blook(savecat("pop3-no-apop-", uhp));
236 NYD_LEAVE;
237 return rv;
240 #ifdef HAVE_MD5
241 static enum okay
242 _pop3_auth_apop(struct mailbox *mp, char *xuser, char *pass, char const *ts)
244 unsigned char digest[16];
245 char hex[MD5TOHEX_SIZE];
246 md5_ctx ctx;
247 size_t tl, i;
248 char *user, *cp;
249 enum okay rv = STOP;
250 NYD_ENTER;
252 for (tl = strlen(ts);;) {
253 user = xuser;
254 if (!getcredentials(&user, &pass))
255 break;
257 md5_init(&ctx);
258 md5_update(&ctx, (unsigned char*)UNCONST(ts), tl);
259 md5_update(&ctx, (unsigned char*)pass, strlen(pass));
260 md5_final(digest, &ctx);
261 md5tohex(hex, digest);
263 i = strlen(user);
264 cp = ac_alloc(5 + i + 1 + MD5TOHEX_SIZE + 2 +1);
266 memcpy(cp, "APOP ", 5);
267 memcpy(cp + 5, user, i);
268 i += 5;
269 cp[i++] = ' ';
270 memcpy(cp + i, hex, MD5TOHEX_SIZE);
271 i += MD5TOHEX_SIZE;
272 memcpy(cp + i, "\r\n\0", 3);
273 POP3_OUT(rv, cp, MB_COMD, goto jcont);
274 POP3_ANSWER(rv, goto jcont);
275 rv = OKAY;
276 jcont:
277 ac_free(cp);
278 if (rv == OKAY)
279 break;
280 pass = NULL;
282 NYD_LEAVE;
283 return rv;
285 #endif /* HAVE_MD5 */
287 static enum okay
288 _pop3_auth_plain(struct mailbox *mp, char *xuser, char *pass)
290 char *user, *cp;
291 size_t ul, pl;
292 enum okay rv = STOP;
293 NYD_ENTER;
295 /* The USER/PASS plain text version */
296 for (;;) {
297 user = xuser;
298 if (!getcredentials(&user, &pass))
299 break;
301 ul = strlen(user);
302 pl = strlen(pass);
303 cp = ac_alloc(MAX(ul, pl) + 5 + 2 +1);
305 memcpy(cp, "USER ", 5);
306 memcpy(cp + 5, user, ul);
307 memcpy(cp + 5 + ul, "\r\n\0", 3);
308 POP3_OUT(rv, cp, MB_COMD, goto jcont);
309 POP3_ANSWER(rv, goto jcont);
311 memcpy(cp, "PASS ", 5);
312 memcpy(cp + 5, pass, pl);
313 memcpy(cp + 5 + pl, "\r\n\0", 3);
314 POP3_OUT(rv, cp, MB_COMD, goto jcont);
315 POP3_ANSWER(rv, goto jcont);
316 rv = OKAY;
317 jcont:
318 ac_free(cp);
319 if (rv == OKAY)
320 break;
321 pass = NULL;
323 NYD_LEAVE;
324 return rv;
327 static void
328 pop3_timer_off(void)
330 NYD_ENTER;
331 if (_pop3_keepalive > 0) {
332 alarm(0);
333 safe_signal(SIGALRM, _pop3_savealrm);
335 NYD_LEAVE;
338 static enum okay
339 pop3_answer(struct mailbox *mp)
341 int sz;
342 enum okay rv = STOP;
343 NYD_ENTER;
345 jretry:
346 if ((sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock)) > 0) {
347 if ((mp->mb_active & (MB_COMD | MB_MULT)) == MB_MULT)
348 goto jmultiline;
349 if (options & OPT_VERBOSE)
350 fputs(_pop3_buf, stderr);
351 switch (*_pop3_buf) {
352 case '+':
353 rv = OKAY;
354 mp->mb_active &= ~MB_COMD;
355 break;
356 case '-':
357 rv = STOP;
358 mp->mb_active = MB_NONE;
359 fprintf(stderr, tr(218, "POP3 error: %s"), _pop3_buf);
360 break;
361 default:
362 /* If the answer starts neither with '+' nor with
363 * '-', it must be part of a multiline response,
364 * e. g. because the user interrupted a file
365 * download. 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"),
499 _pop3_buf);
500 jleave:
501 NYD_LEAVE;
502 return rv;
505 static enum okay
506 pop3_list(struct mailbox *mp, int n, size_t *size)
508 char o[LINESIZE], *cp;
509 enum okay rv;
510 NYD_ENTER;
512 snprintf(o, sizeof o, "LIST %u\r\n", n);
513 POP3_OUT(rv, o, MB_COMD, goto jleave);
514 POP3_ANSWER(rv, goto jleave);
516 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
518 while (*cp != '\0' && spacechar(*cp))
519 ++cp;
520 while (*cp != '\0' && !spacechar(*cp))
521 ++cp;
522 while (*cp != '\0' && spacechar(*cp))
523 ++cp;
524 if (*cp != '\0')
525 *size = (size_t)strtol(cp, NULL, 10);
526 else
527 *size = 0;
528 jleave:
529 NYD_LEAVE;
530 return rv;
533 static void
534 pop3_init(struct mailbox *mp, int n)
536 struct message *m;
537 char *cp;
538 NYD_ENTER;
540 m = message + n;
541 m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
542 m->m_block = 0;
543 m->m_offset = 0;
544 pop3_list(mp, PTR2SIZE(m - message + 1), &m->m_xsize);
546 if ((cp = hfield1("status", m)) != NULL) {
547 while (*cp != '\0') {
548 if (*cp == 'R')
549 m->m_flag |= MREAD;
550 else if (*cp == 'O')
551 m->m_flag &= ~MNEW;
552 ++cp;
555 NYD_LEAVE;
558 static void
559 pop3_dates(struct mailbox *mp)
561 size_t i;
562 NYD_ENTER;
563 UNUSED(mp);
565 for (i = 0; UICMP(z, i, <, msgCount); ++i)
566 substdate(message + i);
567 NYD_LEAVE;
570 static void
571 pop3_setptr(struct mailbox *mp)
573 size_t i;
574 NYD_ENTER;
576 message = scalloc(msgCount + 1, sizeof *message);
577 for (i = 0; UICMP(z, i, <, msgCount); ++i)
578 pop3_init(mp, i);
580 setdot(message);
581 message[msgCount].m_size = 0;
582 message[msgCount].m_lines = 0;
583 pop3_dates(mp);
584 NYD_LEAVE;
587 static enum okay
588 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
590 char o[LINESIZE], *line = NULL, *lp;
591 sighandler_type volatile saveint = SIG_IGN, savepipe = SIG_IGN;
592 size_t linesize = 0, linelen, size;
593 int number = m - message + 1, emptyline = 0, lines;
594 off_t offset;
595 enum okay rv = STOP;
596 NYD_ENTER;
598 if (mp->mb_sock.s_fd < 0) {
599 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
600 ++_pop3_lock;
601 goto jleave;
604 if (_pop3_lock++ == 0) {
605 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
606 safe_signal(SIGINT, &_pop3_maincatch);
607 savepipe = safe_signal(SIGPIPE, SIG_IGN);
608 if (sigsetjmp(_pop3_jmp, 1)) {
609 safe_signal(SIGINT, saveint);
610 safe_signal(SIGPIPE, savepipe);
611 --_pop3_lock;
612 goto jleave;
614 if (savepipe != SIG_IGN)
615 safe_signal(SIGPIPE, pop3catch);
618 fseek(mp->mb_otf, 0L, SEEK_END);
619 offset = ftell(mp->mb_otf);
620 jretry:
621 switch (need) {
622 case NEED_HEADER:
623 snprintf(o, sizeof o, "TOP %u 0\r\n", number);
624 break;
625 case NEED_BODY:
626 snprintf(o, sizeof o, "RETR %u\r\n", number);
627 break;
628 case NEED_UNSPEC:
629 abort(); /* XXX */
631 POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
633 if (pop3_answer(mp) == STOP) {
634 if (need == NEED_HEADER) {
635 /* The TOP POP3 command is optional, so retry with entire message */
636 need = NEED_BODY;
637 goto jretry;
639 goto jleave;
642 size = 0;
643 lines = 0;
644 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
645 if (line[0] == '.' && line[1] == '\r' && line[2] == '\n' &&
646 line[3] == '\0') {
647 mp->mb_active &= ~MB_MULT;
648 break;
650 if (line[0] == '.') {
651 lp = &line[1];
652 linelen--;
653 } else
654 lp = line;
655 /* TODO >>
656 * Need to mask 'From ' lines. This cannot be done properly
657 * since some servers pass them as 'From ' and others as
658 * '>From '. Although one could identify the first kind of
659 * server in principle, it is not possible to identify the
660 * second as '>From ' may also come from a server of the
661 * first type as actual data. So do what is absolutely
662 * necessary only - mask 'From '.
664 * If the line is the first line of the message header, it
665 * is likely a real 'From ' line. In this case, it is just
666 * ignored since it violates all standards.
667 * TODO i have *never* seen the latter?!?!?
668 * TODO <<
670 /* Since we simply copy over data without doing any transfer
671 * encoding reclassification/adjustment we *have* to perform
672 * RFC 4155 compliant From_ quoting here */
673 if (is_head(lp, linelen)) {
674 if (lines == 0)
675 continue;
676 fputc('>', mp->mb_otf);
677 ++size;
679 lines++;
680 if (lp[linelen-1] == '\n' && (linelen == 1 ||
681 lp[linelen-2] == '\r')) {
682 emptyline = linelen <= 2;
683 if (linelen > 2)
684 fwrite(lp, 1, linelen - 2, mp->mb_otf);
685 fputc('\n', mp->mb_otf);
686 size += linelen - 1;
687 } else {
688 emptyline = 0;
689 fwrite(lp, 1, linelen, mp->mb_otf);
690 size += linelen;
693 if (!emptyline) {
694 /* This is very ugly; but some POP3 daemons don't end a
695 * message with \r\n\r\n, and we need \n\n for mbox format */
696 fputc('\n', mp->mb_otf);
697 lines++;
698 size++;
700 m->m_size = size;
701 m->m_lines = lines;
702 m->m_block = mailx_blockof(offset);
703 m->m_offset = mailx_offsetof(offset);
704 fflush(mp->mb_otf);
706 switch (need) {
707 case NEED_HEADER:
708 m->m_have |= HAVE_HEADER;
709 break;
710 case NEED_BODY:
711 m->m_have |= HAVE_HEADER | HAVE_BODY;
712 m->m_xlines = m->m_lines;
713 m->m_xsize = m->m_size;
714 break;
715 case NEED_UNSPEC:
716 break;
719 rv = OKAY;
720 jleave:
721 if (line != NULL)
722 free(line);
723 if (saveint != SIG_IGN)
724 safe_signal(SIGINT, saveint);
725 if (savepipe != SIG_IGN)
726 safe_signal(SIGPIPE, savepipe);
727 --_pop3_lock;
728 NYD_LEAVE;
729 if (interrupts)
730 onintr(0);
731 return rv;
734 static enum okay
735 pop3_exit(struct mailbox *mp)
737 enum okay rv;
738 NYD_ENTER;
740 POP3_OUT(rv, "QUIT\r\n", MB_COMD, goto jleave);
741 POP3_ANSWER(rv, goto jleave);
742 jleave:
743 NYD_LEAVE;
744 return rv;
747 static enum okay
748 pop3_delete(struct mailbox *mp, int n)
750 char o[LINESIZE];
751 enum okay rv;
752 NYD_ENTER;
754 snprintf(o, sizeof o, "DELE %u\r\n", n);
755 POP3_OUT(rv, o, MB_COMD, goto jleave);
756 POP3_ANSWER(rv, goto jleave);
757 jleave:
758 NYD_LEAVE;
759 return rv;
762 static enum okay
763 pop3_update(struct mailbox *mp)
765 struct message *m;
766 int dodel, c, gotcha, held;
767 NYD_ENTER;
769 if (!edit) {
770 holdbits();
771 c = 0;
772 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
773 if (m->m_flag & MBOX)
774 ++c;
775 if (c > 0)
776 makembox();
779 gotcha = held = 0;
780 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
781 if (edit)
782 dodel = m->m_flag & MDELETED;
783 else
784 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
785 if (dodel) {
786 pop3_delete(mp, PTR2SIZE(m - message + 1));
787 ++gotcha;
788 } else
789 ++held;
791 if (gotcha && edit) {
792 printf(tr(168, "\"%s\" "), displayname);
793 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
794 ? tr(170, "complete\n") : tr(212, "updated.\n"));
795 } else if (held && !edit) {
796 if (held == 1)
797 printf(tr(155, "Held 1 message in %s\n"), displayname);
798 else
799 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
801 fflush(stdout);
802 NYD_LEAVE;
803 return OKAY;
806 FL enum okay
807 pop3_noop(void)
809 sighandler_type volatile saveint, savepipe;
810 enum okay rv = STOP;
811 NYD_ENTER;
813 _pop3_lock = 1;
814 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
815 safe_signal(SIGINT, &_pop3_maincatch);
816 savepipe = safe_signal(SIGPIPE, SIG_IGN);
817 if (sigsetjmp(_pop3_jmp, 1) == 0) {
818 if (savepipe != SIG_IGN)
819 safe_signal(SIGPIPE, pop3catch);
820 rv = pop3_noop1(&mb);
822 safe_signal(SIGINT, saveint);
823 safe_signal(SIGPIPE, savepipe);
824 _pop3_lock = 0;
825 NYD_LEAVE;
826 return rv;
829 FL int
830 pop3_setfile(char const *server, int nmail, int isedit)
832 struct sock so;
833 sighandler_type saveint, savepipe;
834 char * volatile user, * volatile pass;
835 char const *cp, *uhp, * volatile sp = server;
836 int use_ssl = 0, rv = 1;
837 NYD_ENTER;
839 if (nmail)
840 goto jleave;
842 if (strncmp(sp, "pop3://", 7) == 0) {
843 sp = &sp[7];
844 use_ssl = 0;
845 #ifdef HAVE_SSL
846 } else if (strncmp(sp, "pop3s://", 8) == 0) {
847 sp = &sp[8];
848 use_ssl = 1;
849 #endif
851 uhp = sp;
852 pass = lookup_password_for_token(uhp);
854 if ((cp = last_at_before_slash(sp)) != NULL) {
855 user = salloc(cp - sp +1);
856 memcpy(user, sp, cp - sp);
857 user[cp - sp] = '\0';
858 sp = &cp[1];
859 user = urlxdec(user);
860 } else
861 user = NULL;
863 if (sopen(sp, &so, use_ssl, uhp, (use_ssl ? "pop3s" : "pop3")) != OKAY) {
864 rv = -1;
865 goto jleave;
867 quit();
868 edit = (isedit != 0);
869 if (mb.mb_sock.s_fd >= 0)
870 sclose(&mb.mb_sock);
871 if (mb.mb_itf) {
872 fclose(mb.mb_itf);
873 mb.mb_itf = NULL;
875 if (mb.mb_otf) {
876 fclose(mb.mb_otf);
877 mb.mb_otf = NULL;
879 initbox(server);
880 mb.mb_type = MB_VOID;
881 _pop3_lock = 1;
882 mb.mb_sock = so;
884 saveint = safe_signal(SIGINT, SIG_IGN);
885 savepipe = safe_signal(SIGPIPE, SIG_IGN);
886 if (sigsetjmp(_pop3_jmp, 1)) {
887 sclose(&mb.mb_sock);
888 safe_signal(SIGINT, saveint);
889 safe_signal(SIGPIPE, savepipe);
890 _pop3_lock = 0;
891 goto jleave;
893 if (saveint != SIG_IGN)
894 safe_signal(SIGINT, pop3catch);
895 if (savepipe != SIG_IGN)
896 safe_signal(SIGPIPE, pop3catch);
898 if ((cp = ok_vlook(pop3_keepalive)) != NULL) {
899 if ((_pop3_keepalive = (int)strtol(cp, NULL, 10)) > 0) {
900 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
901 alarm(_pop3_keepalive);
905 mb.mb_sock.s_desc = "POP3";
906 mb.mb_sock.s_onclose = pop3_timer_off;
907 if (_pop3_login(&mb, user, pass, uhp, sp) != OKAY ||
908 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
909 sclose(&mb.mb_sock);
910 pop3_timer_off();
911 safe_signal(SIGINT, saveint);
912 safe_signal(SIGPIPE, savepipe);
913 _pop3_lock = 0;
914 goto jleave;
916 mb.mb_type = MB_POP3;
917 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
918 pop3_setptr(&mb);
919 setmsize(msgCount);
920 sawcom = FAL0;
922 safe_signal(SIGINT, saveint);
923 safe_signal(SIGPIPE, savepipe);
924 _pop3_lock = 0;
925 if (!edit && msgCount == 0) {
926 if (mb.mb_type == MB_POP3 && !ok_blook(emptystart))
927 fprintf(stderr, tr(258, "No mail at %s\n"), server);
928 goto jleave;
930 rv = 0;
931 jleave:
932 NYD_LEAVE;
933 return rv;
936 FL enum okay
937 pop3_header(struct message *m)
939 enum okay rv;
940 NYD_ENTER;
942 rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
943 NYD_LEAVE;
944 return rv;
947 FL enum okay
948 pop3_body(struct message *m)
950 enum okay rv;
951 NYD_ENTER;
953 rv = pop3_get(&mb, m, NEED_BODY);
954 NYD_LEAVE;
955 return rv;
958 FL void
959 pop3_quit(void)
961 sighandler_type volatile saveint, savepipe;
962 NYD_ENTER;
964 if (mb.mb_sock.s_fd < 0) {
965 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
966 goto jleave;
969 _pop3_lock = 1;
970 saveint = safe_signal(SIGINT, SIG_IGN);
971 savepipe = safe_signal(SIGPIPE, SIG_IGN);
972 if (sigsetjmp(_pop3_jmp, 1)) {
973 safe_signal(SIGINT, saveint);
974 safe_signal(SIGPIPE, saveint);
975 _pop3_lock = 0;
976 goto jleave;
978 if (saveint != SIG_IGN)
979 safe_signal(SIGINT, pop3catch);
980 if (savepipe != SIG_IGN)
981 safe_signal(SIGPIPE, pop3catch);
982 pop3_update(&mb);
983 pop3_exit(&mb);
984 sclose(&mb.mb_sock);
985 safe_signal(SIGINT, saveint);
986 safe_signal(SIGPIPE, savepipe);
987 _pop3_lock = 0;
988 jleave:
989 NYD_LEAVE;
991 #endif /* HAVE_POP3 */
993 /* vim:set fenc=utf-8:s-it-mode */