INSTALL: update for v14.6
[s-mailx.git] / pop3.c
blob5863dcd55e6b87cdcf5119fc5392b54514a62d31
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 #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; 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
365 * '-', it must be part of a multiline response,
366 * e. g. because the user interrupted a file
367 * download. Get lines until a single dot appears */
368 jmultiline:
369 while (_pop3_buf[0] != '.' || _pop3_buf[1] != '\r' ||
370 _pop3_buf[2] != '\n' || _pop3_buf[3] != '\0') {
371 sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock);
372 if (sz <= 0)
373 goto jeof;
375 mp->mb_active &= ~MB_MULT;
376 if (mp->mb_active != MB_NONE)
377 goto jretry;
379 } else {
380 jeof:
381 rv = STOP;
382 mp->mb_active = MB_NONE;
384 NYD_LEAVE;
385 return rv;
388 static enum okay
389 pop3_finish(struct mailbox *mp)
391 NYD_ENTER;
392 while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
393 pop3_answer(mp);
394 NYD_LEAVE;
395 return OKAY;
398 static void
399 pop3catch(int s)
401 NYD_X; /* Signal handler */
402 termios_state_reset();
403 switch (s) {
404 case SIGINT:
405 fprintf(stderr, tr(102, "Interrupt\n"));
406 siglongjmp(_pop3_jmp, 1);
407 break;
408 case SIGPIPE:
409 fprintf(stderr, "Received SIGPIPE during POP3 operation\n");
410 break;
414 static void
415 _pop3_maincatch(int s)
417 NYD_X; /* Signal handler */
418 UNUSED(s);
420 if (interrupts++ == 0)
421 fprintf(stderr, tr(102, "Interrupt\n"));
422 else
423 onintr(0);
426 static enum okay
427 pop3_noop1(struct mailbox *mp)
429 enum okay rv;
430 NYD_ENTER;
432 POP3_OUT(rv, "NOOP\r\n", MB_COMD, goto jleave);
433 POP3_ANSWER(rv, goto jleave);
434 jleave:
435 NYD_LEAVE;
436 return rv;
439 static void
440 pop3alarm(int s)
442 sighandler_type volatile saveint, savepipe;
443 NYD_X; /* Signal handler */
444 UNUSED(s);
446 if (_pop3_lock++ == 0) {
447 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
448 safe_signal(SIGINT, &_pop3_maincatch);
449 savepipe = safe_signal(SIGPIPE, SIG_IGN);
450 if (sigsetjmp(_pop3_jmp, 1)) {
451 safe_signal(SIGINT, saveint);
452 safe_signal(SIGPIPE, savepipe);
453 goto jbrk;
455 if (savepipe != SIG_IGN)
456 safe_signal(SIGPIPE, pop3catch);
457 if (pop3_noop1(&mb) != OKAY) {
458 safe_signal(SIGINT, saveint);
459 safe_signal(SIGPIPE, savepipe);
460 goto jleave;
462 safe_signal(SIGINT, saveint);
463 safe_signal(SIGPIPE, savepipe);
465 jbrk:
466 alarm(_pop3_keepalive);
467 jleave:
468 --_pop3_lock;
471 static enum okay
472 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
474 char *cp;
475 enum okay rv;
476 NYD_ENTER;
478 POP3_OUT(rv, "STAT\r\n", MB_COMD, goto jleave);
479 POP3_ANSWER(rv, goto jleave);
481 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
483 while (*cp != '\0' && spacechar(*cp))
484 ++cp;
486 if (*cp != '\0') {
487 *cnt = (int)strtol(cp, NULL, 10);
488 while (*cp != '\0' && !spacechar(*cp))
489 ++cp;
490 while (*cp != '\0' && spacechar(*cp))
491 ++cp;
492 if (*cp != '\0')
493 *size = (int)strtol(cp, NULL, 10);
494 else
495 rv = STOP;
496 } else
497 rv = STOP;
499 if (rv == STOP)
500 fprintf(stderr, tr(260, "invalid POP3 STAT response: %s\n"),
501 _pop3_buf);
502 jleave:
503 NYD_LEAVE;
504 return rv;
507 static enum okay
508 pop3_list(struct mailbox *mp, int n, size_t *size)
510 char o[LINESIZE], *cp;
511 enum okay rv;
512 NYD_ENTER;
514 snprintf(o, sizeof o, "LIST %u\r\n", n);
515 POP3_OUT(rv, o, MB_COMD, goto jleave);
516 POP3_ANSWER(rv, goto jleave);
518 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
520 while (*cp != '\0' && spacechar(*cp))
521 ++cp;
522 while (*cp != '\0' && !spacechar(*cp))
523 ++cp;
524 while (*cp != '\0' && spacechar(*cp))
525 ++cp;
526 if (*cp != '\0')
527 *size = (size_t)strtol(cp, NULL, 10);
528 else
529 *size = 0;
530 jleave:
531 NYD_LEAVE;
532 return rv;
535 static void
536 pop3_init(struct mailbox *mp, int n)
538 struct message *m;
539 char *cp;
540 NYD_ENTER;
542 m = message + n;
543 m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
544 m->m_block = 0;
545 m->m_offset = 0;
546 pop3_list(mp, PTR2SIZE(m - message + 1), &m->m_xsize);
548 if ((cp = hfield1("status", m)) != NULL) {
549 while (*cp != '\0') {
550 if (*cp == 'R')
551 m->m_flag |= MREAD;
552 else if (*cp == 'O')
553 m->m_flag &= ~MNEW;
554 ++cp;
557 NYD_LEAVE;
560 static void
561 pop3_dates(struct mailbox *mp)
563 size_t i;
564 NYD_ENTER;
565 UNUSED(mp);
567 for (i = 0; UICMP(z, i, <, msgCount); ++i)
568 substdate(message + i);
569 NYD_LEAVE;
572 static void
573 pop3_setptr(struct mailbox *mp)
575 size_t i;
576 NYD_ENTER;
578 message = scalloc(msgCount + 1, sizeof *message);
579 for (i = 0; UICMP(z, i, <, msgCount); ++i)
580 pop3_init(mp, i);
582 setdot(message);
583 message[msgCount].m_size = 0;
584 message[msgCount].m_lines = 0;
585 pop3_dates(mp);
586 NYD_LEAVE;
589 static enum okay
590 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
592 char o[LINESIZE], *line, *lp;
593 sighandler_type volatile saveint, savepipe;
594 size_t linesize, linelen, size;
595 int number, emptyline, lines;
596 off_t offset;
597 enum okay rv;
598 NYD_ENTER;
600 line = NULL;
601 saveint = savepipe = SIG_IGN;
602 linesize = 0;
603 number = (int)PTR2SIZE(m - message + 1);
604 emptyline = 0;
605 rv = STOP;
607 if (mp->mb_sock.s_fd < 0) {
608 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
609 ++_pop3_lock;
610 goto jleave;
613 if (_pop3_lock++ == 0) {
614 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
615 safe_signal(SIGINT, &_pop3_maincatch);
616 savepipe = safe_signal(SIGPIPE, SIG_IGN);
617 if (sigsetjmp(_pop3_jmp, 1)) {
618 safe_signal(SIGINT, saveint);
619 safe_signal(SIGPIPE, savepipe);
620 --_pop3_lock;
621 goto jleave;
623 if (savepipe != SIG_IGN)
624 safe_signal(SIGPIPE, pop3catch);
627 fseek(mp->mb_otf, 0L, SEEK_END);
628 offset = ftell(mp->mb_otf);
629 jretry:
630 switch (need) {
631 case NEED_HEADER:
632 snprintf(o, sizeof o, "TOP %u 0\r\n", number);
633 break;
634 case NEED_BODY:
635 snprintf(o, sizeof o, "RETR %u\r\n", number);
636 break;
637 case NEED_UNSPEC:
638 abort(); /* XXX */
640 POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
642 if (pop3_answer(mp) == STOP) {
643 if (need == NEED_HEADER) {
644 /* The TOP POP3 command is optional, so retry with entire message */
645 need = NEED_BODY;
646 goto jretry;
648 goto jleave;
651 size = 0;
652 lines = 0;
653 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
654 if (line[0] == '.' && line[1] == '\r' && line[2] == '\n' &&
655 line[3] == '\0') {
656 mp->mb_active &= ~MB_MULT;
657 break;
659 if (line[0] == '.') {
660 lp = &line[1];
661 linelen--;
662 } else
663 lp = line;
664 /* TODO >>
665 * Need to mask 'From ' lines. This cannot be done properly
666 * since some servers pass them as 'From ' and others as
667 * '>From '. Although one could identify the first kind of
668 * server in principle, it is not possible to identify the
669 * second as '>From ' may also come from a server of the
670 * first type as actual data. So do what is absolutely
671 * necessary only - mask 'From '.
673 * If the line is the first line of the message header, it
674 * is likely a real 'From ' line. In this case, it is just
675 * ignored since it violates all standards.
676 * TODO i have *never* seen the latter?!?!?
677 * TODO <<
679 /* Since we simply copy over data without doing any transfer
680 * encoding reclassification/adjustment we *have* to perform
681 * RFC 4155 compliant From_ quoting here */
682 if (is_head(lp, linelen)) {
683 if (lines == 0)
684 continue;
685 fputc('>', mp->mb_otf);
686 ++size;
688 lines++;
689 if (lp[linelen-1] == '\n' && (linelen == 1 ||
690 lp[linelen-2] == '\r')) {
691 emptyline = linelen <= 2;
692 if (linelen > 2)
693 fwrite(lp, 1, linelen - 2, mp->mb_otf);
694 fputc('\n', mp->mb_otf);
695 size += linelen - 1;
696 } else {
697 emptyline = 0;
698 fwrite(lp, 1, linelen, mp->mb_otf);
699 size += linelen;
702 if (!emptyline) {
703 /* This is very ugly; but some POP3 daemons don't end a
704 * message with \r\n\r\n, and we need \n\n for mbox format */
705 fputc('\n', mp->mb_otf);
706 lines++;
707 size++;
709 m->m_size = size;
710 m->m_lines = lines;
711 m->m_block = mailx_blockof(offset);
712 m->m_offset = mailx_offsetof(offset);
713 fflush(mp->mb_otf);
715 switch (need) {
716 case NEED_HEADER:
717 m->m_have |= HAVE_HEADER;
718 break;
719 case NEED_BODY:
720 m->m_have |= HAVE_HEADER | HAVE_BODY;
721 m->m_xlines = m->m_lines;
722 m->m_xsize = m->m_size;
723 break;
724 case NEED_UNSPEC:
725 break;
728 rv = OKAY;
729 jleave:
730 if (line != NULL)
731 free(line);
732 if (saveint != SIG_IGN)
733 safe_signal(SIGINT, saveint);
734 if (savepipe != SIG_IGN)
735 safe_signal(SIGPIPE, savepipe);
736 --_pop3_lock;
737 NYD_LEAVE;
738 if (interrupts)
739 onintr(0);
740 return rv;
743 static enum okay
744 pop3_exit(struct mailbox *mp)
746 enum okay rv;
747 NYD_ENTER;
749 POP3_OUT(rv, "QUIT\r\n", MB_COMD, goto jleave);
750 POP3_ANSWER(rv, goto jleave);
751 jleave:
752 NYD_LEAVE;
753 return rv;
756 static enum okay
757 pop3_delete(struct mailbox *mp, int n)
759 char o[LINESIZE];
760 enum okay rv;
761 NYD_ENTER;
763 snprintf(o, sizeof o, "DELE %u\r\n", n);
764 POP3_OUT(rv, o, MB_COMD, goto jleave);
765 POP3_ANSWER(rv, goto jleave);
766 jleave:
767 NYD_LEAVE;
768 return rv;
771 static enum okay
772 pop3_update(struct mailbox *mp)
774 struct message *m;
775 int dodel, c, gotcha, held;
776 NYD_ENTER;
778 if (!edit) {
779 holdbits();
780 c = 0;
781 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
782 if (m->m_flag & MBOX)
783 ++c;
784 if (c > 0)
785 makembox();
788 gotcha = held = 0;
789 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
790 if (edit)
791 dodel = m->m_flag & MDELETED;
792 else
793 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
794 if (dodel) {
795 pop3_delete(mp, PTR2SIZE(m - message + 1));
796 ++gotcha;
797 } else
798 ++held;
800 if (gotcha && edit) {
801 printf(tr(168, "\"%s\" "), displayname);
802 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
803 ? tr(170, "complete\n") : tr(212, "updated.\n"));
804 } else if (held && !edit) {
805 if (held == 1)
806 printf(tr(155, "Held 1 message in %s\n"), displayname);
807 else
808 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
810 fflush(stdout);
811 NYD_LEAVE;
812 return OKAY;
815 FL enum okay
816 pop3_noop(void)
818 sighandler_type volatile saveint, savepipe;
819 enum okay rv = STOP;
820 NYD_ENTER;
822 _pop3_lock = 1;
823 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
824 safe_signal(SIGINT, &_pop3_maincatch);
825 savepipe = safe_signal(SIGPIPE, SIG_IGN);
826 if (sigsetjmp(_pop3_jmp, 1) == 0) {
827 if (savepipe != SIG_IGN)
828 safe_signal(SIGPIPE, pop3catch);
829 rv = pop3_noop1(&mb);
831 safe_signal(SIGINT, saveint);
832 safe_signal(SIGPIPE, savepipe);
833 _pop3_lock = 0;
834 NYD_LEAVE;
835 return rv;
838 FL int
839 pop3_setfile(char const *server, int nmail, int isedit)
841 struct sock so;
842 sighandler_type saveint, savepipe;
843 char * volatile user, * volatile pass;
844 char const *cp, *uhp, * volatile sp = server;
845 int use_ssl = 0, rv = 1;
846 NYD_ENTER;
848 if (nmail)
849 goto jleave;
851 if (!strncmp(sp, "pop3://", 7)) {
852 sp = &sp[7];
853 use_ssl = 0;
854 #ifdef HAVE_SSL
855 } else if (!strncmp(sp, "pop3s://", 8)) {
856 sp = &sp[8];
857 use_ssl = 1;
858 #endif
860 uhp = sp;
861 pass = lookup_password_for_token(uhp);
863 if ((cp = last_at_before_slash(sp)) != NULL) {
864 user = salloc(cp - sp +1);
865 memcpy(user, sp, cp - sp);
866 user[cp - sp] = '\0';
867 sp = &cp[1];
868 user = urlxdec(user);
869 } else
870 user = NULL;
872 if (sopen(sp, &so, use_ssl, uhp, (use_ssl ? "pop3s" : "pop3")) != OKAY) {
873 rv = -1;
874 goto jleave;
876 quit();
877 edit = (isedit != 0);
878 if (mb.mb_sock.s_fd >= 0)
879 sclose(&mb.mb_sock);
880 if (mb.mb_itf) {
881 fclose(mb.mb_itf);
882 mb.mb_itf = NULL;
884 if (mb.mb_otf) {
885 fclose(mb.mb_otf);
886 mb.mb_otf = NULL;
888 initbox(server);
889 mb.mb_type = MB_VOID;
890 _pop3_lock = 1;
891 mb.mb_sock = so;
893 saveint = safe_signal(SIGINT, SIG_IGN);
894 savepipe = safe_signal(SIGPIPE, SIG_IGN);
895 if (sigsetjmp(_pop3_jmp, 1)) {
896 sclose(&mb.mb_sock);
897 safe_signal(SIGINT, saveint);
898 safe_signal(SIGPIPE, savepipe);
899 _pop3_lock = 0;
900 goto jleave;
902 if (saveint != SIG_IGN)
903 safe_signal(SIGINT, pop3catch);
904 if (savepipe != SIG_IGN)
905 safe_signal(SIGPIPE, pop3catch);
907 if ((cp = ok_vlook(pop3_keepalive)) != NULL) {
908 if ((_pop3_keepalive = (int)strtol(cp, NULL, 10)) > 0) {
909 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
910 alarm(_pop3_keepalive);
914 mb.mb_sock.s_desc = "POP3";
915 mb.mb_sock.s_onclose = pop3_timer_off;
916 if (_pop3_login(&mb, user, pass, uhp, sp) != OKAY ||
917 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
918 sclose(&mb.mb_sock);
919 pop3_timer_off();
920 safe_signal(SIGINT, saveint);
921 safe_signal(SIGPIPE, savepipe);
922 _pop3_lock = 0;
923 goto jleave;
925 mb.mb_type = MB_POP3;
926 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
927 pop3_setptr(&mb);
928 setmsize(msgCount);
929 sawcom = FAL0;
931 safe_signal(SIGINT, saveint);
932 safe_signal(SIGPIPE, savepipe);
933 _pop3_lock = 0;
934 if (!edit && msgCount == 0) {
935 if (mb.mb_type == MB_POP3 && !ok_blook(emptystart))
936 fprintf(stderr, tr(258, "No mail at %s\n"), server);
937 goto jleave;
939 rv = 0;
940 jleave:
941 NYD_LEAVE;
942 return rv;
945 FL enum okay
946 pop3_header(struct message *m)
948 enum okay rv;
949 NYD_ENTER;
951 rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
952 NYD_LEAVE;
953 return rv;
956 FL enum okay
957 pop3_body(struct message *m)
959 enum okay rv;
960 NYD_ENTER;
962 rv = pop3_get(&mb, m, NEED_BODY);
963 NYD_LEAVE;
964 return rv;
967 FL void
968 pop3_quit(void)
970 sighandler_type volatile saveint, savepipe;
971 NYD_ENTER;
973 if (mb.mb_sock.s_fd < 0) {
974 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
975 goto jleave;
978 _pop3_lock = 1;
979 saveint = safe_signal(SIGINT, SIG_IGN);
980 savepipe = safe_signal(SIGPIPE, SIG_IGN);
981 if (sigsetjmp(_pop3_jmp, 1)) {
982 safe_signal(SIGINT, saveint);
983 safe_signal(SIGPIPE, saveint);
984 _pop3_lock = 0;
985 goto jleave;
987 if (saveint != SIG_IGN)
988 safe_signal(SIGINT, pop3catch);
989 if (savepipe != SIG_IGN)
990 safe_signal(SIGPIPE, pop3catch);
991 pop3_update(&mb);
992 pop3_exit(&mb);
993 sclose(&mb.mb_sock);
994 safe_signal(SIGINT, saveint);
995 safe_signal(SIGPIPE, savepipe);
996 _pop3_lock = 0;
997 jleave:
998 NYD_LEAVE;
1000 #endif /* HAVE_POP3 */
1002 /* vim:set fenc=utf-8:s-it-mode */