INSTALL: TOC, and talk on development guideline
[s-mailx.git] / pop3.c
blob6d95a0930ac384cdc77eb4bcce759b6fa34e46b3
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 switch (s) {
401 case SIGINT:
402 fprintf(stderr, tr(102, "Interrupt\n"));
403 siglongjmp(_pop3_jmp, 1);
404 break;
405 case SIGPIPE:
406 fprintf(stderr, "Received SIGPIPE during POP3 operation\n");
407 break;
411 static void
412 _pop3_maincatch(int s)
414 NYD_X; /* Signal handler */
415 UNUSED(s);
417 if (interrupts++ == 0)
418 fprintf(stderr, tr(102, "Interrupt\n"));
419 else
420 onintr(0);
423 static enum okay
424 pop3_noop1(struct mailbox *mp)
426 enum okay rv;
427 NYD_ENTER;
429 POP3_OUT(rv, "NOOP\r\n", MB_COMD, goto jleave);
430 POP3_ANSWER(rv, goto jleave);
431 jleave:
432 NYD_LEAVE;
433 return rv;
436 static void
437 pop3alarm(int s)
439 sighandler_type volatile saveint, savepipe;
440 NYD_X; /* Signal handler */
441 UNUSED(s);
443 if (_pop3_lock++ == 0) {
444 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
445 safe_signal(SIGINT, &_pop3_maincatch);
446 savepipe = safe_signal(SIGPIPE, SIG_IGN);
447 if (sigsetjmp(_pop3_jmp, 1)) {
448 safe_signal(SIGINT, saveint);
449 safe_signal(SIGPIPE, savepipe);
450 goto jbrk;
452 if (savepipe != SIG_IGN)
453 safe_signal(SIGPIPE, pop3catch);
454 if (pop3_noop1(&mb) != OKAY) {
455 safe_signal(SIGINT, saveint);
456 safe_signal(SIGPIPE, savepipe);
457 goto jleave;
459 safe_signal(SIGINT, saveint);
460 safe_signal(SIGPIPE, savepipe);
462 jbrk:
463 alarm(_pop3_keepalive);
464 jleave:
465 --_pop3_lock;
468 static enum okay
469 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
471 char *cp;
472 enum okay rv;
473 NYD_ENTER;
475 POP3_OUT(rv, "STAT\r\n", MB_COMD, goto jleave);
476 POP3_ANSWER(rv, goto jleave);
478 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
480 while (*cp != '\0' && spacechar(*cp))
481 ++cp;
483 if (*cp != '\0') {
484 *cnt = (int)strtol(cp, NULL, 10);
485 while (*cp != '\0' && !spacechar(*cp))
486 ++cp;
487 while (*cp != '\0' && spacechar(*cp))
488 ++cp;
489 if (*cp != '\0')
490 *size = (int)strtol(cp, NULL, 10);
491 else
492 rv = STOP;
493 } else
494 rv = STOP;
496 if (rv == STOP)
497 fprintf(stderr, tr(260, "invalid POP3 STAT response: %s\n"), _pop3_buf);
498 jleave:
499 NYD_LEAVE;
500 return rv;
503 static enum okay
504 pop3_list(struct mailbox *mp, int n, size_t *size)
506 char o[LINESIZE], *cp;
507 enum okay rv;
508 NYD_ENTER;
510 snprintf(o, sizeof o, "LIST %u\r\n", n);
511 POP3_OUT(rv, o, MB_COMD, goto jleave);
512 POP3_ANSWER(rv, goto jleave);
514 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
516 while (*cp != '\0' && spacechar(*cp))
517 ++cp;
518 while (*cp != '\0' && !spacechar(*cp))
519 ++cp;
520 while (*cp != '\0' && spacechar(*cp))
521 ++cp;
522 if (*cp != '\0')
523 *size = (size_t)strtol(cp, NULL, 10);
524 else
525 *size = 0;
526 jleave:
527 NYD_LEAVE;
528 return rv;
531 static void
532 pop3_init(struct mailbox *mp, int n)
534 struct message *m;
535 char *cp;
536 NYD_ENTER;
538 m = message + n;
539 m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
540 m->m_block = 0;
541 m->m_offset = 0;
542 pop3_list(mp, PTR2SIZE(m - message + 1), &m->m_xsize);
544 if ((cp = hfield1("status", m)) != NULL) {
545 while (*cp != '\0') {
546 if (*cp == 'R')
547 m->m_flag |= MREAD;
548 else if (*cp == 'O')
549 m->m_flag &= ~MNEW;
550 ++cp;
553 NYD_LEAVE;
556 static void
557 pop3_dates(struct mailbox *mp)
559 size_t i;
560 NYD_ENTER;
561 UNUSED(mp);
563 for (i = 0; UICMP(z, i, <, msgCount); ++i)
564 substdate(message + i);
565 NYD_LEAVE;
568 static void
569 pop3_setptr(struct mailbox *mp)
571 size_t i;
572 NYD_ENTER;
574 message = scalloc(msgCount + 1, sizeof *message);
575 for (i = 0; UICMP(z, i, <, msgCount); ++i)
576 pop3_init(mp, i);
578 setdot(message);
579 message[msgCount].m_size = 0;
580 message[msgCount].m_lines = 0;
581 pop3_dates(mp);
582 NYD_LEAVE;
585 static enum okay
586 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
588 char o[LINESIZE], *line, *lp;
589 sighandler_type volatile saveint, savepipe;
590 size_t linesize, linelen, size;
591 int number, emptyline, lines;
592 off_t offset;
593 enum okay rv;
594 NYD_ENTER;
596 line = NULL; /* TODO line pool */
597 saveint = savepipe = SIG_IGN;
598 linesize = 0;
599 number = (int)PTR2SIZE(m - message + 1);
600 emptyline = 0;
601 rv = STOP;
603 if (mp->mb_sock.s_fd < 0) {
604 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
605 ++_pop3_lock;
606 goto jleave;
609 if (_pop3_lock++ == 0) {
610 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
611 safe_signal(SIGINT, &_pop3_maincatch);
612 savepipe = safe_signal(SIGPIPE, SIG_IGN);
613 if (sigsetjmp(_pop3_jmp, 1)) {
614 safe_signal(SIGINT, saveint);
615 safe_signal(SIGPIPE, savepipe);
616 --_pop3_lock;
617 goto jleave;
619 if (savepipe != SIG_IGN)
620 safe_signal(SIGPIPE, pop3catch);
623 fseek(mp->mb_otf, 0L, SEEK_END);
624 offset = ftell(mp->mb_otf);
625 jretry:
626 switch (need) {
627 case NEED_HEADER:
628 snprintf(o, sizeof o, "TOP %u 0\r\n", number);
629 break;
630 case NEED_BODY:
631 snprintf(o, sizeof o, "RETR %u\r\n", number);
632 break;
633 case NEED_UNSPEC:
634 abort(); /* XXX */
636 POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
638 if (pop3_answer(mp) == STOP) {
639 if (need == NEED_HEADER) {
640 /* The TOP POP3 command is optional, so retry with entire message */
641 need = NEED_BODY;
642 goto jretry;
644 goto jleave;
647 size = 0;
648 lines = 0;
649 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
650 if (line[0] == '.' && line[1] == '\r' && line[2] == '\n' &&
651 line[3] == '\0') {
652 mp->mb_active &= ~MB_MULT;
653 break;
655 if (line[0] == '.') {
656 lp = line + 1;
657 --linelen;
658 } else
659 lp = line;
660 /* TODO >>
661 * Need to mask 'From ' lines. This cannot be done properly
662 * since some servers pass them as 'From ' and others as
663 * '>From '. Although one could identify the first kind of
664 * server in principle, it is not possible to identify the
665 * second as '>From ' may also come from a server of the
666 * first type as actual data. So do what is absolutely
667 * necessary only - mask 'From '.
669 * If the line is the first line of the message header, it
670 * is likely a real 'From ' line. In this case, it is just
671 * ignored since it violates all standards.
672 * TODO i have *never* seen the latter?!?!?
673 * TODO <<
675 /* Since we simply copy over data without doing any transfer
676 * encoding reclassification/adjustment we *have* to perform
677 * RFC 4155 compliant From_ quoting here */
678 if (is_head(lp, linelen)) {
679 if (lines == 0)
680 continue;
681 fputc('>', mp->mb_otf);
682 ++size;
684 lines++;
685 if (lp[linelen-1] == '\n' && (linelen == 1 ||
686 lp[linelen-2] == '\r')) {
687 emptyline = linelen <= 2;
688 if (linelen > 2)
689 fwrite(lp, 1, linelen - 2, mp->mb_otf);
690 fputc('\n', mp->mb_otf);
691 size += linelen - 1;
692 } else {
693 emptyline = 0;
694 fwrite(lp, 1, linelen, mp->mb_otf);
695 size += linelen;
698 if (!emptyline) {
699 /* This is very ugly; but some POP3 daemons don't end a
700 * message with \r\n\r\n, and we need \n\n for mbox format */
701 fputc('\n', mp->mb_otf);
702 ++lines;
703 ++size;
705 m->m_size = size;
706 m->m_lines = lines;
707 m->m_block = mailx_blockof(offset);
708 m->m_offset = mailx_offsetof(offset);
709 fflush(mp->mb_otf);
711 switch (need) {
712 case NEED_HEADER:
713 m->m_have |= HAVE_HEADER;
714 break;
715 case NEED_BODY:
716 m->m_have |= HAVE_HEADER | HAVE_BODY;
717 m->m_xlines = m->m_lines;
718 m->m_xsize = m->m_size;
719 break;
720 case NEED_UNSPEC:
721 break;
724 rv = OKAY;
725 jleave:
726 if (line != NULL)
727 free(line);
728 if (saveint != SIG_IGN)
729 safe_signal(SIGINT, saveint);
730 if (savepipe != SIG_IGN)
731 safe_signal(SIGPIPE, savepipe);
732 --_pop3_lock;
733 NYD_LEAVE;
734 if (interrupts)
735 onintr(0);
736 return rv;
739 static enum okay
740 pop3_exit(struct mailbox *mp)
742 enum okay rv;
743 NYD_ENTER;
745 POP3_OUT(rv, "QUIT\r\n", MB_COMD, goto jleave);
746 POP3_ANSWER(rv, goto jleave);
747 jleave:
748 NYD_LEAVE;
749 return rv;
752 static enum okay
753 pop3_delete(struct mailbox *mp, int n)
755 char o[LINESIZE];
756 enum okay rv;
757 NYD_ENTER;
759 snprintf(o, sizeof o, "DELE %u\r\n", n);
760 POP3_OUT(rv, o, MB_COMD, goto jleave);
761 POP3_ANSWER(rv, goto jleave);
762 jleave:
763 NYD_LEAVE;
764 return rv;
767 static enum okay
768 pop3_update(struct mailbox *mp)
770 struct message *m;
771 int dodel, c, gotcha, held;
772 NYD_ENTER;
774 if (!edit) {
775 holdbits();
776 c = 0;
777 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
778 if (m->m_flag & MBOX)
779 ++c;
780 if (c > 0)
781 makembox();
784 gotcha = held = 0;
785 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
786 if (edit)
787 dodel = m->m_flag & MDELETED;
788 else
789 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
790 if (dodel) {
791 pop3_delete(mp, PTR2SIZE(m - message + 1));
792 ++gotcha;
793 } else
794 ++held;
796 if (gotcha && edit) {
797 printf(tr(168, "\"%s\" "), displayname);
798 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
799 ? tr(170, "complete\n") : tr(212, "updated.\n"));
800 } else if (held && !edit) {
801 if (held == 1)
802 printf(tr(155, "Held 1 message in %s\n"), displayname);
803 else
804 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
806 fflush(stdout);
807 NYD_LEAVE;
808 return OKAY;
811 FL enum okay
812 pop3_noop(void)
814 sighandler_type volatile saveint, savepipe;
815 enum okay rv = STOP;
816 NYD_ENTER;
818 _pop3_lock = 1;
819 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
820 safe_signal(SIGINT, &_pop3_maincatch);
821 savepipe = safe_signal(SIGPIPE, SIG_IGN);
822 if (sigsetjmp(_pop3_jmp, 1) == 0) {
823 if (savepipe != SIG_IGN)
824 safe_signal(SIGPIPE, pop3catch);
825 rv = pop3_noop1(&mb);
827 safe_signal(SIGINT, saveint);
828 safe_signal(SIGPIPE, savepipe);
829 _pop3_lock = 0;
830 NYD_LEAVE;
831 return rv;
834 FL int
835 pop3_setfile(char const *server, int nmail, int isedit)
837 struct sock so;
838 sighandler_type saveint, savepipe;
839 char * volatile user, * volatile pass;
840 char const *cp, *uhp, * volatile sp = server;
841 int use_ssl = 0, rv = 1;
842 NYD_ENTER;
844 if (nmail)
845 goto jleave;
847 if (!strncmp(sp, "pop3://", 7)) {
848 sp = sp + 7;
849 use_ssl = 0;
850 #ifdef HAVE_SSL
851 } else if (!strncmp(sp, "pop3s://", 8)) {
852 sp = sp + 8;
853 use_ssl = 1;
854 #endif
856 uhp = sp;
857 pass = lookup_password_for_token(uhp);
859 if ((cp = last_at_before_slash(sp)) != NULL) {
860 user = salloc(cp - sp +1);
861 memcpy(user, sp, cp - sp);
862 user[cp - sp] = '\0';
863 sp = cp + 1;
864 user = urlxdec(user);
865 } else
866 user = NULL;
868 if (sopen(sp, &so, use_ssl, uhp, (use_ssl ? "pop3s" : "pop3")) != OKAY) {
869 rv = -1;
870 goto jleave;
872 quit();
873 edit = (isedit != 0);
874 if (mb.mb_sock.s_fd >= 0)
875 sclose(&mb.mb_sock);
876 if (mb.mb_itf) {
877 fclose(mb.mb_itf);
878 mb.mb_itf = NULL;
880 if (mb.mb_otf) {
881 fclose(mb.mb_otf);
882 mb.mb_otf = NULL;
884 initbox(server);
885 mb.mb_type = MB_VOID;
886 _pop3_lock = 1;
887 mb.mb_sock = so;
889 saveint = safe_signal(SIGINT, SIG_IGN);
890 savepipe = safe_signal(SIGPIPE, SIG_IGN);
891 if (sigsetjmp(_pop3_jmp, 1)) {
892 sclose(&mb.mb_sock);
893 safe_signal(SIGINT, saveint);
894 safe_signal(SIGPIPE, savepipe);
895 _pop3_lock = 0;
896 goto jleave;
898 if (saveint != SIG_IGN)
899 safe_signal(SIGINT, pop3catch);
900 if (savepipe != SIG_IGN)
901 safe_signal(SIGPIPE, pop3catch);
903 if ((cp = ok_vlook(pop3_keepalive)) != NULL) {
904 if ((_pop3_keepalive = (int)strtol(cp, NULL, 10)) > 0) {
905 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
906 alarm(_pop3_keepalive);
910 mb.mb_sock.s_desc = "POP3";
911 mb.mb_sock.s_onclose = pop3_timer_off;
912 if (_pop3_login(&mb, user, pass, uhp, sp) != OKAY ||
913 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
914 sclose(&mb.mb_sock);
915 pop3_timer_off();
916 safe_signal(SIGINT, saveint);
917 safe_signal(SIGPIPE, savepipe);
918 _pop3_lock = 0;
919 goto jleave;
921 mb.mb_type = MB_POP3;
922 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
923 pop3_setptr(&mb);
924 setmsize(msgCount);
925 sawcom = FAL0;
927 safe_signal(SIGINT, saveint);
928 safe_signal(SIGPIPE, savepipe);
929 _pop3_lock = 0;
930 if (!edit && msgCount == 0) {
931 if (mb.mb_type == MB_POP3 && !ok_blook(emptystart))
932 fprintf(stderr, tr(258, "No mail at %s\n"), server);
933 goto jleave;
935 rv = 0;
936 jleave:
937 NYD_LEAVE;
938 return rv;
941 FL enum okay
942 pop3_header(struct message *m)
944 enum okay rv;
945 NYD_ENTER;
947 rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
948 NYD_LEAVE;
949 return rv;
952 FL enum okay
953 pop3_body(struct message *m)
955 enum okay rv;
956 NYD_ENTER;
958 rv = pop3_get(&mb, m, NEED_BODY);
959 NYD_LEAVE;
960 return rv;
963 FL void
964 pop3_quit(void)
966 sighandler_type volatile saveint, savepipe;
967 NYD_ENTER;
969 if (mb.mb_sock.s_fd < 0) {
970 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
971 goto jleave;
974 _pop3_lock = 1;
975 saveint = safe_signal(SIGINT, SIG_IGN);
976 savepipe = safe_signal(SIGPIPE, SIG_IGN);
977 if (sigsetjmp(_pop3_jmp, 1)) {
978 safe_signal(SIGINT, saveint);
979 safe_signal(SIGPIPE, saveint);
980 _pop3_lock = 0;
981 goto jleave;
983 if (saveint != SIG_IGN)
984 safe_signal(SIGINT, pop3catch);
985 if (savepipe != SIG_IGN)
986 safe_signal(SIGPIPE, pop3catch);
987 pop3_update(&mb);
988 pop3_exit(&mb);
989 sclose(&mb.mb_sock);
990 safe_signal(SIGINT, saveint);
991 safe_signal(SIGPIPE, savepipe);
992 _pop3_lock = 0;
993 jleave:
994 NYD_LEAVE;
996 #endif /* HAVE_POP3 */
998 /* vim:set fenc=utf-8:s-it-mode */