Merge branch 'topic/mbbidi'
[s-mailx.git] / pop3.c
blob1f62631ae28549d27ee000691e086cc2c1291616
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 #undef NL
48 #undef LINE
49 #define NL "\015\012"
50 #define LINE(X) X NL
52 #define POP3_ANSWER(RV,ACTIONSTOP) \
53 do if (((RV) = pop3_answer(mp)) == STOP) {\
54 ACTIONSTOP;\
55 } while (0)
57 #define POP3_OUT(RV,X,Y,ACTIONSTOP) \
58 do {\
59 if (((RV) = pop3_finish(mp)) == STOP) {\
60 ACTIONSTOP;\
62 if (options & OPT_VERBVERB)\
63 fprintf(stderr, ">>> %s", X);\
64 mp->mb_active |= Y;\
65 if (((RV) = swrite(&mp->mb_sock, X)) == STOP) {\
66 ACTIONSTOP;\
68 } while (0)
70 static char *_pop3_buf;
71 static size_t _pop3_bufsize;
72 static sigjmp_buf _pop3_jmp;
73 static sighandler_type _pop3_savealrm;
74 static int _pop3_keepalive;
75 static int volatile _pop3_lock;
77 /* Perform entire login handshake */
78 static enum okay _pop3_login(struct mailbox *mp, struct sockconn *scp);
80 /* APOP: get greeting credential or NULL */
81 #ifdef HAVE_MD5
82 static char * _pop3_lookup_apop_timestamp(char const *bp);
83 #endif
85 static bool_t _pop3_use_starttls(struct sockconn const *scp);
87 /* APOP: shall we use it? */
88 static bool_t _pop3_no_apop(struct sockconn const *scp);
90 /* Several authentication methods */
91 #ifdef HAVE_MD5
92 static enum okay _pop3_auth_apop(struct mailbox *mp,
93 struct sockconn const *scp, char const *ts);
94 #endif
95 static enum okay _pop3_auth_plain(struct mailbox *mp,
96 struct sockconn const *scp);
98 static void pop3_timer_off(void);
99 static enum okay pop3_answer(struct mailbox *mp);
100 static enum okay pop3_finish(struct mailbox *mp);
101 static void pop3catch(int s);
102 static void _pop3_maincatch(int s);
103 static enum okay pop3_noop1(struct mailbox *mp);
104 static void pop3alarm(int s);
105 static enum okay pop3_stat(struct mailbox *mp, off_t *size, int *cnt);
106 static enum okay pop3_list(struct mailbox *mp, int n, size_t *size);
107 static void pop3_init(struct mailbox *mp, int n);
108 static void pop3_dates(struct mailbox *mp);
109 static void pop3_setptr(struct mailbox *mp);
110 static enum okay pop3_get(struct mailbox *mp, struct message *m,
111 enum needspec need);
112 static enum okay pop3_exit(struct mailbox *mp);
113 static enum okay pop3_delete(struct mailbox *mp, int n);
114 static enum okay pop3_update(struct mailbox *mp);
116 static enum okay
117 _pop3_login(struct mailbox *mp, struct sockconn *scp)
119 #ifdef HAVE_MD5
120 char *ts;
121 #endif
122 enum okay rv;
123 NYD_ENTER;
125 /* Get the greeting, check wether APOP is advertised */
126 POP3_ANSWER(rv, goto jleave);
127 #ifdef HAVE_MD5
128 ts = _pop3_lookup_apop_timestamp(_pop3_buf);
129 #endif
131 /* If not yet secured, can we upgrade to TLS? */
132 #ifdef HAVE_SSL
133 if (!scp->sc_url.url_needs_tls && _pop3_use_starttls(scp)) {
134 POP3_OUT(rv, "STLS" NL, MB_COMD, goto jleave);
135 POP3_ANSWER(rv, goto jleave);
136 if ((rv = ssl_open(scp->sc_url.url_host.s, &scp->sc_sock,
137 scp->sc_url.url_uhp.s)) != OKAY)
138 goto jleave;
140 #else
141 if (_pop3_use_starttls(scp->sc_url.url_uhp.s)) {
142 fprintf(stderr, "No SSL support compiled in.\n");
143 rv = STOP;
144 goto jleave;
146 #endif
148 /* Use the APOP single roundtrip? */
149 if (!_pop3_no_apop(scp)) {
150 #ifdef HAVE_MD5
151 if (ts != NULL) {
152 if ((rv = _pop3_auth_apop(mp, scp, ts)) != OKAY)
153 fprintf(stderr, tr(276, "POP3 `APOP' authentication failed, "
154 "maybe try setting *pop3-no-apop*\n"));
155 goto jleave;
156 } else
157 #endif
158 if (options & OPT_VERB)
159 fprintf(stderr, tr(204, "No POP3 `APOP' support "
160 "available, sending password in clear text\n"));
163 rv = _pop3_auth_plain(mp, scp);
164 jleave:
165 NYD_LEAVE;
166 return rv;
169 #ifdef HAVE_MD5
170 static char *
171 _pop3_lookup_apop_timestamp(char const *bp)
173 /* RFC 1939:
174 * A POP3 server which implements the APOP command will include
175 * a timestamp in its banner greeting. The syntax of the timestamp
176 * corresponds to the `msg-id' in [RFC822]
177 * RFC 822:
178 * msg-id = "<" addr-spec ">"
179 * addr-spec = local-part "@" domain */
180 char const *cp, *ep;
181 size_t tl;
182 char *rp = NULL;
183 bool_t hadat = FAL0;
184 NYD_ENTER;
186 if ((cp = strchr(bp, '<')) == NULL)
187 goto jleave;
189 /* xxx What about malformed APOP timestamp (<@>) here? */
190 for (ep = cp; *ep != '\0'; ++ep) {
191 if (spacechar(*ep))
192 goto jleave;
193 else if (*ep == '@')
194 hadat = TRU1;
195 else if (*ep == '>') {
196 if (!hadat)
197 goto jleave;
198 break;
201 if (*ep != '>')
202 goto jleave;
204 tl = PTR2SIZE(++ep - cp);
205 rp = salloc(tl +1);
206 memcpy(rp, cp, tl);
207 rp[tl] = '\0';
208 jleave:
209 NYD_LEAVE;
210 return rp;
212 #endif
214 static bool_t
215 _pop3_use_starttls(struct sockconn const *scp)
217 bool_t rv;
218 NYD_ENTER;
220 if (!(rv = ok_blook(pop3_use_starttls)))
221 if (!ok_blook(v15_compat) ||
222 !(rv = vok_blook(savecat("pop3-use-starttls-",
223 scp->sc_url.url_hp.s))))
224 rv = vok_blook(savecat("pop3-use-starttls-", scp->sc_url.url_uhp.s));
225 NYD_LEAVE;
226 return rv;
229 static bool_t
230 _pop3_no_apop(struct sockconn const *scp)
232 bool_t rv;
233 NYD_ENTER;
235 if (!(rv = ok_blook(pop3_no_apop)))
236 if (!ok_blook(v15_compat) ||
237 !(rv = vok_blook(savecat("pop3-no-apop-", scp->sc_url.url_hp.s))))
238 rv = vok_blook(savecat("pop3-no-apop-", scp->sc_url.url_uhp.s));
239 NYD_LEAVE;
240 return rv;
243 #ifdef HAVE_MD5
244 static enum okay
245 _pop3_auth_apop(struct mailbox *mp, struct sockconn const *scp, char const *ts)
247 unsigned char digest[16];
248 char hex[MD5TOHEX_SIZE], *cp;
249 md5_ctx ctx;
250 size_t i;
251 enum okay rv = STOP;
252 NYD_ENTER;
254 md5_init(&ctx);
255 md5_update(&ctx, (uc_it*)UNCONST(ts), strlen(ts));
256 md5_update(&ctx, (uc_it const*)scp->sc_cred.cc_pass.s,
257 scp->sc_cred.cc_pass.l);
258 md5_final(digest, &ctx);
259 md5tohex(hex, digest);
261 i = scp->sc_cred.cc_user.l;
262 cp = ac_alloc(5 + i + 1 + MD5TOHEX_SIZE + sizeof(NL)-1 +1);
264 memcpy(cp, "APOP ", 5);
265 memcpy(cp + 5, scp->sc_cred.cc_user.s, i);
266 i += 5;
267 cp[i++] = ' ';
268 memcpy(cp + i, hex, MD5TOHEX_SIZE);
269 i += MD5TOHEX_SIZE;
270 memcpy(cp + i, NL, sizeof(NL));
271 POP3_OUT(rv, cp, MB_COMD, goto jleave);
272 POP3_ANSWER(rv, goto jleave);
274 rv = OKAY;
275 jleave:
276 ac_free(cp);
277 NYD_LEAVE;
278 return rv;
280 #endif /* HAVE_MD5 */
282 static enum okay
283 _pop3_auth_plain(struct mailbox *mp, struct sockconn const *scp)
285 char *cp;
286 enum okay rv = STOP;
287 NYD_ENTER;
289 /* The USER/PASS plain text version */
290 cp = ac_alloc(MAX(scp->sc_cred.cc_user.l, scp->sc_cred.cc_pass.l) + 5 +
291 sizeof(NL)-1 +1);
293 memcpy(cp, "USER ", 5);
294 memcpy(cp + 5, scp->sc_cred.cc_user.s, scp->sc_cred.cc_user.l);
295 memcpy(cp + 5 + scp->sc_cred.cc_user.l, NL, sizeof(NL));
296 POP3_OUT(rv, cp, MB_COMD, goto jleave);
297 POP3_ANSWER(rv, goto jleave);
299 memcpy(cp, "PASS ", 5);
300 memcpy(cp + 5, scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
301 memcpy(cp + 5 + scp->sc_cred.cc_pass.l, NL, sizeof(NL));
302 POP3_OUT(rv, cp, MB_COMD, goto jleave);
303 POP3_ANSWER(rv, goto jleave);
305 rv = OKAY;
306 jleave:
307 ac_free(cp);
308 NYD_LEAVE;
309 return rv;
312 static void
313 pop3_timer_off(void)
315 NYD_ENTER;
316 if (_pop3_keepalive > 0) {
317 alarm(0);
318 safe_signal(SIGALRM, _pop3_savealrm);
320 NYD_LEAVE;
323 static enum okay
324 pop3_answer(struct mailbox *mp)
326 int sz;
327 enum okay rv = STOP;
328 NYD_ENTER;
330 jretry:
331 if ((sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock)) > 0) {
332 if ((mp->mb_active & (MB_COMD | MB_MULT)) == MB_MULT)
333 goto jmultiline;
334 if (options & OPT_VERBVERB)
335 fputs(_pop3_buf, stderr);
336 switch (*_pop3_buf) {
337 case '+':
338 rv = OKAY;
339 mp->mb_active &= ~MB_COMD;
340 break;
341 case '-':
342 rv = STOP;
343 mp->mb_active = MB_NONE;
344 fprintf(stderr, tr(218, "POP3 error: %s"), _pop3_buf);
345 break;
346 default:
347 /* If the answer starts neither with '+' nor with '-', it must be part
348 * of a multiline response. Get lines until a single dot appears */
349 jmultiline:
350 while (_pop3_buf[0] != '.' || _pop3_buf[1] != NL[0] ||
351 _pop3_buf[2] != NL[1] || _pop3_buf[3] != '\0') {
352 sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock);
353 if (sz <= 0)
354 goto jeof;
356 mp->mb_active &= ~MB_MULT;
357 if (mp->mb_active != MB_NONE)
358 goto jretry;
360 } else {
361 jeof:
362 rv = STOP;
363 mp->mb_active = MB_NONE;
365 NYD_LEAVE;
366 return rv;
369 static enum okay
370 pop3_finish(struct mailbox *mp)
372 NYD_ENTER;
373 while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
374 pop3_answer(mp);
375 NYD_LEAVE;
376 return OKAY;
379 static void
380 pop3catch(int s)
382 NYD_X; /* Signal handler */
383 switch (s) {
384 case SIGINT:
385 fprintf(stderr, tr(102, "Interrupt\n"));
386 siglongjmp(_pop3_jmp, 1);
387 break;
388 case SIGPIPE:
389 fprintf(stderr, "Received SIGPIPE during POP3 operation\n");
390 break;
394 static void
395 _pop3_maincatch(int s)
397 NYD_X; /* Signal handler */
398 UNUSED(s);
400 if (interrupts++ == 0)
401 fprintf(stderr, tr(102, "Interrupt\n"));
402 else
403 onintr(0);
406 static enum okay
407 pop3_noop1(struct mailbox *mp)
409 enum okay rv;
410 NYD_ENTER;
412 POP3_OUT(rv, "NOOP" NL, MB_COMD, goto jleave);
413 POP3_ANSWER(rv, goto jleave);
414 jleave:
415 NYD_LEAVE;
416 return rv;
419 static void
420 pop3alarm(int s)
422 sighandler_type volatile saveint, savepipe;
423 NYD_X; /* Signal handler */
424 UNUSED(s);
426 if (_pop3_lock++ == 0) {
427 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
428 safe_signal(SIGINT, &_pop3_maincatch);
429 savepipe = safe_signal(SIGPIPE, SIG_IGN);
430 if (sigsetjmp(_pop3_jmp, 1)) {
431 safe_signal(SIGINT, saveint);
432 safe_signal(SIGPIPE, savepipe);
433 goto jbrk;
435 if (savepipe != SIG_IGN)
436 safe_signal(SIGPIPE, pop3catch);
437 if (pop3_noop1(&mb) != OKAY) {
438 safe_signal(SIGINT, saveint);
439 safe_signal(SIGPIPE, savepipe);
440 goto jleave;
442 safe_signal(SIGINT, saveint);
443 safe_signal(SIGPIPE, savepipe);
445 jbrk:
446 alarm(_pop3_keepalive);
447 jleave:
448 --_pop3_lock;
451 static enum okay
452 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
454 char *cp;
455 enum okay rv;
456 NYD_ENTER;
458 POP3_OUT(rv, "STAT" NL, MB_COMD, goto jleave);
459 POP3_ANSWER(rv, goto jleave);
461 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
463 while (*cp != '\0' && spacechar(*cp))
464 ++cp;
466 if (*cp != '\0') {
467 *cnt = (int)strtol(cp, NULL, 10);
468 while (*cp != '\0' && !spacechar(*cp))
469 ++cp;
470 while (*cp != '\0' && spacechar(*cp))
471 ++cp;
472 if (*cp != '\0')
473 *size = (int)strtol(cp, NULL, 10);
474 else
475 rv = STOP;
476 } else
477 rv = STOP;
479 if (rv == STOP)
480 fprintf(stderr, tr(260, "invalid POP3 STAT response: %s\n"), _pop3_buf);
481 jleave:
482 NYD_LEAVE;
483 return rv;
486 static enum okay
487 pop3_list(struct mailbox *mp, int n, size_t *size)
489 char o[LINESIZE], *cp;
490 enum okay rv;
491 NYD_ENTER;
493 snprintf(o, sizeof o, "LIST %u" NL, n);
494 POP3_OUT(rv, o, MB_COMD, goto jleave);
495 POP3_ANSWER(rv, goto jleave);
497 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
499 while (*cp != '\0' && spacechar(*cp))
500 ++cp;
501 while (*cp != '\0' && !spacechar(*cp))
502 ++cp;
503 while (*cp != '\0' && spacechar(*cp))
504 ++cp;
505 if (*cp != '\0')
506 *size = (size_t)strtol(cp, NULL, 10);
507 else
508 *size = 0;
509 jleave:
510 NYD_LEAVE;
511 return rv;
514 static void
515 pop3_init(struct mailbox *mp, int n)
517 struct message *m;
518 char *cp;
519 NYD_ENTER;
521 m = message + n;
522 m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
523 m->m_block = 0;
524 m->m_offset = 0;
525 pop3_list(mp, PTR2SIZE(m - message + 1), &m->m_xsize);
527 if ((cp = hfield1("status", m)) != NULL) {
528 while (*cp != '\0') {
529 if (*cp == 'R')
530 m->m_flag |= MREAD;
531 else if (*cp == 'O')
532 m->m_flag &= ~MNEW;
533 ++cp;
536 NYD_LEAVE;
539 static void
540 pop3_dates(struct mailbox *mp)
542 size_t i;
543 NYD_ENTER;
544 UNUSED(mp);
546 for (i = 0; UICMP(z, i, <, msgCount); ++i)
547 substdate(message + i);
548 NYD_LEAVE;
551 static void
552 pop3_setptr(struct mailbox *mp)
554 size_t i;
555 NYD_ENTER;
557 message = scalloc(msgCount + 1, sizeof *message);
558 for (i = 0; UICMP(z, i, <, msgCount); ++i)
559 pop3_init(mp, i);
561 setdot(message);
562 message[msgCount].m_size = 0;
563 message[msgCount].m_lines = 0;
564 pop3_dates(mp);
565 NYD_LEAVE;
568 static enum okay
569 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
571 char o[LINESIZE], *line, *lp;
572 sighandler_type volatile saveint, savepipe;
573 size_t linesize, linelen, size;
574 int number, lines;
575 int volatile emptyline;
576 off_t offset;
577 enum okay volatile rv;
578 NYD_ENTER;
580 line = NULL; /* TODO line pool */
581 saveint = savepipe = SIG_IGN;
582 linesize = 0;
583 number = (int)PTR2SIZE(m - message + 1);
584 emptyline = 0;
585 rv = STOP;
587 if (mp->mb_sock.s_fd < 0) {
588 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
589 ++_pop3_lock;
590 goto jleave;
593 if (_pop3_lock++ == 0) {
594 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
595 safe_signal(SIGINT, &_pop3_maincatch);
596 savepipe = safe_signal(SIGPIPE, SIG_IGN);
597 if (sigsetjmp(_pop3_jmp, 1)) {
598 safe_signal(SIGINT, saveint);
599 safe_signal(SIGPIPE, savepipe);
600 --_pop3_lock;
601 goto jleave;
603 if (savepipe != SIG_IGN)
604 safe_signal(SIGPIPE, pop3catch);
607 fseek(mp->mb_otf, 0L, SEEK_END);
608 offset = ftell(mp->mb_otf);
609 jretry:
610 switch (need) {
611 case NEED_HEADER:
612 snprintf(o, sizeof o, "TOP %u 0" NL, number);
613 break;
614 case NEED_BODY:
615 snprintf(o, sizeof o, "RETR %u" NL, number);
616 break;
617 case NEED_UNSPEC:
618 abort(); /* XXX */
620 POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
622 if (pop3_answer(mp) == STOP) {
623 if (need == NEED_HEADER) {
624 /* The TOP POP3 command is optional, so retry with entire message */
625 need = NEED_BODY;
626 goto jretry;
628 goto jleave;
631 size = 0;
632 lines = 0;
633 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
634 if (line[0] == '.' && line[1] == NL[0] && line[2] == NL[1] &&
635 line[3] == '\0') {
636 mp->mb_active &= ~MB_MULT;
637 break;
639 if (line[0] == '.') {
640 lp = line + 1;
641 --linelen;
642 } else
643 lp = line;
644 /* TODO >>
645 * Need to mask 'From ' lines. This cannot be done properly
646 * since some servers pass them as 'From ' and others as
647 * '>From '. Although one could identify the first kind of
648 * server in principle, it is not possible to identify the
649 * second as '>From ' may also come from a server of the
650 * first type as actual data. So do what is absolutely
651 * necessary only - mask 'From '.
653 * If the line is the first line of the message header, it
654 * is likely a real 'From ' line. In this case, it is just
655 * ignored since it violates all standards.
656 * TODO i have *never* seen the latter?!?!?
657 * TODO <<
659 /* Since we simply copy over data without doing any transfer
660 * encoding reclassification/adjustment we *have* to perform
661 * RFC 4155 compliant From_ quoting here */
662 if (is_head(lp, linelen)) {
663 if (lines == 0)
664 continue;
665 fputc('>', mp->mb_otf);
666 ++size;
668 lines++;
669 if (lp[linelen-1] == NL[1] && (linelen == 1 || lp[linelen-2] == NL[0])) {
670 emptyline = linelen <= 2;
671 if (linelen > 2)
672 fwrite(lp, 1, linelen - 2, mp->mb_otf);
673 fputc('\n', mp->mb_otf);
674 size += linelen - 1;
675 } else {
676 emptyline = 0;
677 fwrite(lp, 1, linelen, mp->mb_otf);
678 size += linelen;
681 if (!emptyline) {
682 /* This is very ugly; but some POP3 daemons don't end a
683 * message with NL NL, and we need \n\n for mbox format */
684 fputc('\n', mp->mb_otf);
685 ++lines;
686 ++size;
688 m->m_size = size;
689 m->m_lines = lines;
690 m->m_block = mailx_blockof(offset);
691 m->m_offset = mailx_offsetof(offset);
692 fflush(mp->mb_otf);
694 switch (need) {
695 case NEED_HEADER:
696 m->m_have |= HAVE_HEADER;
697 break;
698 case NEED_BODY:
699 m->m_have |= HAVE_HEADER | HAVE_BODY;
700 m->m_xlines = m->m_lines;
701 m->m_xsize = m->m_size;
702 break;
703 case NEED_UNSPEC:
704 break;
707 rv = OKAY;
708 jleave:
709 if (line != NULL)
710 free(line);
711 if (saveint != SIG_IGN)
712 safe_signal(SIGINT, saveint);
713 if (savepipe != SIG_IGN)
714 safe_signal(SIGPIPE, savepipe);
715 --_pop3_lock;
716 NYD_LEAVE;
717 if (interrupts)
718 onintr(0);
719 return rv;
722 static enum okay
723 pop3_exit(struct mailbox *mp)
725 enum okay rv;
726 NYD_ENTER;
728 POP3_OUT(rv, "QUIT" NL, MB_COMD, goto jleave);
729 POP3_ANSWER(rv, goto jleave);
730 jleave:
731 NYD_LEAVE;
732 return rv;
735 static enum okay
736 pop3_delete(struct mailbox *mp, int n)
738 char o[LINESIZE];
739 enum okay rv;
740 NYD_ENTER;
742 snprintf(o, sizeof o, "DELE %u" NL, n);
743 POP3_OUT(rv, o, MB_COMD, goto jleave);
744 POP3_ANSWER(rv, goto jleave);
745 jleave:
746 NYD_LEAVE;
747 return rv;
750 static enum okay
751 pop3_update(struct mailbox *mp)
753 struct message *m;
754 int dodel, c, gotcha, held;
755 NYD_ENTER;
757 if (!edit) {
758 holdbits();
759 c = 0;
760 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
761 if (m->m_flag & MBOX)
762 ++c;
763 if (c > 0)
764 makembox();
767 gotcha = held = 0;
768 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
769 if (edit)
770 dodel = m->m_flag & MDELETED;
771 else
772 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
773 if (dodel) {
774 pop3_delete(mp, PTR2SIZE(m - message + 1));
775 ++gotcha;
776 } else
777 ++held;
779 if (gotcha && edit) {
780 printf(tr(168, "\"%s\" "), displayname);
781 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
782 ? tr(170, "complete\n") : tr(212, "updated.\n"));
783 } else if (held && !edit) {
784 if (held == 1)
785 printf(tr(155, "Held 1 message in %s\n"), displayname);
786 else
787 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
789 fflush(stdout);
790 NYD_LEAVE;
791 return OKAY;
794 FL enum okay
795 pop3_noop(void)
797 sighandler_type volatile saveint, savepipe;
798 enum okay rv = STOP;
799 NYD_ENTER;
801 _pop3_lock = 1;
802 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
803 safe_signal(SIGINT, &_pop3_maincatch);
804 savepipe = safe_signal(SIGPIPE, SIG_IGN);
805 if (sigsetjmp(_pop3_jmp, 1) == 0) {
806 if (savepipe != SIG_IGN)
807 safe_signal(SIGPIPE, pop3catch);
808 rv = pop3_noop1(&mb);
810 safe_signal(SIGINT, saveint);
811 safe_signal(SIGPIPE, savepipe);
812 _pop3_lock = 0;
813 NYD_LEAVE;
814 return rv;
817 FL int
818 pop3_setfile(char const *server, int nmail, int isedit)
820 struct sockconn sc;
821 sighandler_type saveint, savepipe;
822 char const *cp;
823 int volatile rv;
824 NYD_ENTER;
826 rv = 1;
827 if (nmail)
828 goto jleave;
829 rv = -1;
831 if (!url_parse(&sc.sc_url, CPROTO_POP3, server))
832 goto jleave;
833 if (!ok_blook(v15_compat) &&
834 (!sc.sc_url.url_had_user || sc.sc_url.url_pass.s != NULL)) {
835 fprintf(stderr, "New-style URL used without *v15-compat* being set\n");
836 goto jleave;
839 if (!(ok_blook(v15_compat) ? ccred_lookup(&sc.sc_cred, &sc.sc_url)
840 : ccred_lookup_old(&sc.sc_cred, CPROTO_POP3, sc.sc_url.url_uhp.s)))
841 goto jleave;
843 if (!sopen(&sc.sc_sock, &sc.sc_url))
844 goto jleave;
846 rv = 1;
847 quit();
849 edit = (isedit != 0);
850 if (mb.mb_sock.s_fd >= 0)
851 sclose(&mb.mb_sock);
852 if (mb.mb_itf) {
853 fclose(mb.mb_itf);
854 mb.mb_itf = NULL;
856 if (mb.mb_otf) {
857 fclose(mb.mb_otf);
858 mb.mb_otf = NULL;
861 { char *nmn = ac_alloc(sc.sc_url.url_proto_xlen + sc.sc_url.url_uhp.l +1);
862 sc.sc_url.url_proto[sc.sc_url.url_proto_len] = ':';
863 memcpy(sstpcpy(nmn, sc.sc_url.url_proto),
864 sc.sc_url.url_uhp.s, sc.sc_url.url_uhp.l +1);
865 sc.sc_url.url_proto[sc.sc_url.url_proto_len] = '\0';
866 initbox(nmn);
867 ac_free(nmn);
869 mb.mb_type = MB_VOID;
870 _pop3_lock = 1;
871 mb.mb_sock = sc.sc_sock;
873 saveint = safe_signal(SIGINT, SIG_IGN);
874 savepipe = safe_signal(SIGPIPE, SIG_IGN);
875 if (sigsetjmp(_pop3_jmp, 1)) {
876 sclose(&mb.mb_sock);
877 safe_signal(SIGINT, saveint);
878 safe_signal(SIGPIPE, savepipe);
879 _pop3_lock = 0;
880 goto jleave;
882 if (saveint != SIG_IGN)
883 safe_signal(SIGINT, pop3catch);
884 if (savepipe != SIG_IGN)
885 safe_signal(SIGPIPE, pop3catch);
887 if ((cp = ok_vlook(pop3_keepalive)) != NULL) {
888 if ((_pop3_keepalive = (int)strtol(cp, NULL, 10)) > 0) {
889 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
890 alarm(_pop3_keepalive);
894 mb.mb_sock.s_desc = sc.sc_url.url_proto;
895 mb.mb_sock.s_onclose = pop3_timer_off;
896 if (_pop3_login(&mb, &sc) != OKAY ||
897 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
898 sclose(&mb.mb_sock);
899 pop3_timer_off();
900 safe_signal(SIGINT, saveint);
901 safe_signal(SIGPIPE, savepipe);
902 _pop3_lock = 0;
903 goto jleave;
905 mb.mb_type = MB_POP3;
906 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
907 pop3_setptr(&mb);
908 setmsize(msgCount);
909 sawcom = FAL0;
911 safe_signal(SIGINT, saveint);
912 safe_signal(SIGPIPE, savepipe);
913 _pop3_lock = 0;
914 if (!edit && msgCount == 0) {
915 if (mb.mb_type == MB_POP3 && !ok_blook(emptystart))
916 fprintf(stderr, tr(258, "No mail at %s\n"), server);
917 goto jleave;
920 rv = 0;
921 jleave:
922 NYD_LEAVE;
923 return rv;
926 FL enum okay
927 pop3_header(struct message *m)
929 enum okay rv;
930 NYD_ENTER;
932 rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
933 NYD_LEAVE;
934 return rv;
937 FL enum okay
938 pop3_body(struct message *m)
940 enum okay rv;
941 NYD_ENTER;
943 rv = pop3_get(&mb, m, NEED_BODY);
944 NYD_LEAVE;
945 return rv;
948 FL void
949 pop3_quit(void)
951 sighandler_type volatile saveint, savepipe;
952 NYD_ENTER;
954 if (mb.mb_sock.s_fd < 0) {
955 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
956 goto jleave;
959 _pop3_lock = 1;
960 saveint = safe_signal(SIGINT, SIG_IGN);
961 savepipe = safe_signal(SIGPIPE, SIG_IGN);
962 if (sigsetjmp(_pop3_jmp, 1)) {
963 safe_signal(SIGINT, saveint);
964 safe_signal(SIGPIPE, saveint);
965 _pop3_lock = 0;
966 goto jleave;
968 if (saveint != SIG_IGN)
969 safe_signal(SIGINT, pop3catch);
970 if (savepipe != SIG_IGN)
971 safe_signal(SIGPIPE, pop3catch);
972 pop3_update(&mb);
973 pop3_exit(&mb);
974 sclose(&mb.mb_sock);
975 safe_signal(SIGINT, saveint);
976 safe_signal(SIGPIPE, savepipe);
977 _pop3_lock = 0;
978 jleave:
979 NYD_LEAVE;
982 #undef LINE
983 #undef NL
984 #endif /* HAVE_POP3 */
986 /* vim:set fenc=utf-8:s-it-mode */