*inbox*: if empty, only bypass *folder* to $MAIL or builtin default
[s-mailx.git] / pop3.c
blobd19b66e261a987126f894f9358015f24e420b0ca
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 - 2015 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.
39 #undef n_FILE
40 #define n_FILE pop3
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 EMPTY_FILE()
47 #ifdef HAVE_POP3
49 #define POP3_ANSWER(RV,ACTIONSTOP) \
50 do if (((RV) = pop3_answer(mp)) == STOP) {\
51 ACTIONSTOP;\
52 } while (0)
54 #define POP3_OUT(RV,X,Y,ACTIONSTOP) \
55 do {\
56 if (((RV) = pop3_finish(mp)) == STOP) {\
57 ACTIONSTOP;\
59 if (options & OPT_VERBVERB)\
60 n_err(">>> %s", X);\
61 mp->mb_active |= Y;\
62 if (((RV) = swrite(&mp->mb_sock, X)) == STOP) {\
63 ACTIONSTOP;\
65 } while (0)
67 static char *_pop3_buf;
68 static size_t _pop3_bufsize;
69 static sigjmp_buf _pop3_jmp;
70 static sighandler_type _pop3_savealrm;
71 static int _pop3_keepalive;
72 static int volatile _pop3_lock;
74 /* Perform entire login handshake */
75 static enum okay _pop3_login(struct mailbox *mp, struct sockconn *scp);
77 /* APOP: get greeting credential or NULL */
78 #ifdef HAVE_MD5
79 static char * _pop3_lookup_apop_timestamp(char const *bp);
80 #endif
82 /* Several authentication methods */
83 #ifdef HAVE_MD5
84 static enum okay _pop3_auth_apop(struct mailbox *mp,
85 struct sockconn const *scp, char const *ts);
86 #endif
87 static enum okay _pop3_auth_plain(struct mailbox *mp,
88 struct sockconn const *scp);
90 static void pop3_timer_off(void);
91 static enum okay pop3_answer(struct mailbox *mp);
92 static enum okay pop3_finish(struct mailbox *mp);
93 static void pop3catch(int s);
94 static void _pop3_maincatch(int s);
95 static enum okay pop3_noop1(struct mailbox *mp);
96 static void pop3alarm(int s);
97 static enum okay pop3_stat(struct mailbox *mp, off_t *size, int *cnt);
98 static enum okay pop3_list(struct mailbox *mp, int n, size_t *size);
99 static void pop3_setptr(struct mailbox *mp,
100 struct sockconn const *scp);
101 static enum okay pop3_get(struct mailbox *mp, struct message *m,
102 enum needspec need);
103 static enum okay pop3_exit(struct mailbox *mp);
104 static enum okay pop3_delete(struct mailbox *mp, int n);
105 static enum okay pop3_update(struct mailbox *mp);
107 static enum okay
108 _pop3_login(struct mailbox *mp, struct sockconn *scp)
110 #ifdef HAVE_MD5
111 char *ts;
112 #endif
113 enum okey_xlook_mode oxm;
114 enum okay rv;
115 NYD_ENTER;
117 oxm = ok_blook(v15_compat) ? OXM_ALL : OXM_PLAIN | OXM_U_H_P;
119 /* Get the greeting, check wether APOP is advertised */
120 POP3_ANSWER(rv, goto jleave);
121 #ifdef HAVE_MD5
122 ts = _pop3_lookup_apop_timestamp(_pop3_buf);
123 #endif
125 /* If not yet secured, can we upgrade to TLS? */
126 #ifdef HAVE_SSL
127 if (!scp->sc_url.url_needs_tls &&
128 xok_blook(pop3_use_starttls, &scp->sc_url, oxm)) {
129 POP3_OUT(rv, "STLS" NETNL, MB_COMD, goto jleave);
130 POP3_ANSWER(rv, goto jleave);
131 if ((rv = ssl_open(&scp->sc_url, &scp->sc_sock)) != OKAY)
132 goto jleave;
134 #else
135 if (xok_blook(pop3_use_starttls, &scp->sc_url, oxm)) {
136 n_err(_("No SSL support compiled in\n"));
137 rv = STOP;
138 goto jleave;
140 #endif
142 /* Use the APOP single roundtrip? */
143 #ifdef HAVE_MD5
144 if (ts != NULL && !xok_blook(pop3_no_apop, &scp->sc_url, oxm)) {
145 if ((rv = _pop3_auth_apop(mp, scp, ts)) != OKAY) {
146 char const *ccp = "";
147 # ifdef HAVE_SSL
148 if (scp->sc_sock.s_use_ssl)
149 ccp = _(" (over an encrypted connection)");
150 # endif
151 n_err(_("POP3 \"APOP\" authentication failed!\n"
152 " The server indicated support - but disable via *pop3-no-apop*\n"
153 " to use plain text authentication%s\n"), ccp);
155 goto jleave;
157 #endif
159 rv = _pop3_auth_plain(mp, scp);
160 jleave:
161 NYD_LEAVE;
162 return rv;
165 #ifdef HAVE_MD5
166 static char *
167 _pop3_lookup_apop_timestamp(char const *bp)
169 /* RFC 1939:
170 * A POP3 server which implements the APOP command will include
171 * a timestamp in its banner greeting. The syntax of the timestamp
172 * corresponds to the "msg-id" in [RFC822]
173 * RFC 822:
174 * msg-id = "<" addr-spec ">"
175 * addr-spec = local-part "@" domain */
176 char const *cp, *ep;
177 size_t tl;
178 char *rp = NULL;
179 bool_t hadat = FAL0;
180 NYD_ENTER;
182 if ((cp = strchr(bp, '<')) == NULL)
183 goto jleave;
185 /* xxx What about malformed APOP timestamp (<@>) here? */
186 for (ep = cp; *ep != '\0'; ++ep) {
187 if (spacechar(*ep))
188 goto jleave;
189 else if (*ep == '@')
190 hadat = TRU1;
191 else if (*ep == '>') {
192 if (!hadat)
193 goto jleave;
194 break;
197 if (*ep != '>')
198 goto jleave;
200 tl = PTR2SIZE(++ep - cp);
201 rp = salloc(tl +1);
202 memcpy(rp, cp, tl);
203 rp[tl] = '\0';
204 jleave:
205 NYD_LEAVE;
206 return rp;
208 #endif
210 #ifdef HAVE_MD5
211 static enum okay
212 _pop3_auth_apop(struct mailbox *mp, struct sockconn const *scp, char const *ts)
214 unsigned char digest[16];
215 char hex[MD5TOHEX_SIZE], *cp;
216 md5_ctx ctx;
217 size_t i;
218 enum okay rv = STOP;
219 NYD_ENTER;
221 md5_init(&ctx);
222 md5_update(&ctx, (uc_i*)UNCONST(ts), strlen(ts));
223 md5_update(&ctx, (uc_i*)scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
224 md5_final(digest, &ctx);
225 md5tohex(hex, digest);
227 i = scp->sc_cred.cc_user.l;
228 cp = ac_alloc(5 + i + 1 + MD5TOHEX_SIZE + sizeof(NETNL)-1 +1);
230 memcpy(cp, "APOP ", 5);
231 memcpy(cp + 5, scp->sc_cred.cc_user.s, i);
232 i += 5;
233 cp[i++] = ' ';
234 memcpy(cp + i, hex, MD5TOHEX_SIZE);
235 i += MD5TOHEX_SIZE;
236 memcpy(cp + i, NETNL, sizeof(NETNL));
237 POP3_OUT(rv, cp, MB_COMD, goto jleave);
238 POP3_ANSWER(rv, goto jleave);
240 rv = OKAY;
241 jleave:
242 ac_free(cp);
243 NYD_LEAVE;
244 return rv;
246 #endif /* HAVE_MD5 */
248 static enum okay
249 _pop3_auth_plain(struct mailbox *mp, struct sockconn const *scp)
251 char *cp;
252 enum okay rv = STOP;
253 NYD_ENTER;
255 /* The USER/PASS plain text version */
256 cp = ac_alloc(MAX(scp->sc_cred.cc_user.l, scp->sc_cred.cc_pass.l) + 5 +
257 sizeof(NETNL)-1 +1);
259 memcpy(cp, "USER ", 5);
260 memcpy(cp + 5, scp->sc_cred.cc_user.s, scp->sc_cred.cc_user.l);
261 memcpy(cp + 5 + scp->sc_cred.cc_user.l, NETNL, sizeof(NETNL));
262 POP3_OUT(rv, cp, MB_COMD, goto jleave);
263 POP3_ANSWER(rv, goto jleave);
265 memcpy(cp, "PASS ", 5);
266 memcpy(cp + 5, scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
267 memcpy(cp + 5 + scp->sc_cred.cc_pass.l, NETNL, sizeof(NETNL));
268 POP3_OUT(rv, cp, MB_COMD, goto jleave);
269 POP3_ANSWER(rv, goto jleave);
271 rv = OKAY;
272 jleave:
273 ac_free(cp);
274 NYD_LEAVE;
275 return rv;
278 static void
279 pop3_timer_off(void)
281 NYD_ENTER;
282 if (_pop3_keepalive > 0) {
283 alarm(0);
284 safe_signal(SIGALRM, _pop3_savealrm);
286 NYD_LEAVE;
289 static enum okay
290 pop3_answer(struct mailbox *mp)
292 int sz;
293 enum okay rv = STOP;
294 NYD_ENTER;
296 jretry:
297 if ((sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock)) > 0) {
298 if ((mp->mb_active & (MB_COMD | MB_MULT)) == MB_MULT)
299 goto jmultiline;
300 if (options & OPT_VERBVERB)
301 n_err(_pop3_buf);
302 switch (*_pop3_buf) {
303 case '+':
304 rv = OKAY;
305 mp->mb_active &= ~MB_COMD;
306 break;
307 case '-':
308 rv = STOP;
309 mp->mb_active = MB_NONE;
310 n_err(_("POP3 error: %s"), _pop3_buf);
311 break;
312 default:
313 /* If the answer starts neither with '+' nor with '-', it must be part
314 * of a multiline response. Get lines until a single dot appears */
315 jmultiline:
316 while (_pop3_buf[0] != '.' || _pop3_buf[1] != NETNL[0] ||
317 _pop3_buf[2] != NETNL[1] || _pop3_buf[3] != '\0') {
318 sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock);
319 if (sz <= 0)
320 goto jeof;
322 mp->mb_active &= ~MB_MULT;
323 if (mp->mb_active != MB_NONE)
324 goto jretry;
326 } else {
327 jeof:
328 rv = STOP;
329 mp->mb_active = MB_NONE;
331 NYD_LEAVE;
332 return rv;
335 static enum okay
336 pop3_finish(struct mailbox *mp)
338 NYD_ENTER;
339 while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
340 pop3_answer(mp);
341 NYD_LEAVE;
342 return OKAY;
345 static void
346 pop3catch(int s)
348 NYD_X; /* Signal handler */
349 switch (s) {
350 case SIGINT:
351 /*n_err_sighdl(_("Interrupt during POP3 operation\n"));*/
352 interrupts = 2; /* Force "Interrupt" message shall we onintr(0) */
353 siglongjmp(_pop3_jmp, 1);
354 break;
355 case SIGPIPE:
356 n_err_sighdl(_("Received SIGPIPE during POP3 operation\n"));
357 break;
361 static void
362 _pop3_maincatch(int s)
364 NYD_X; /* Signal handler */
365 UNUSED(s);
366 if (interrupts++ == 0)
367 n_err_sighdl(_("\n(Interrupt -- one more to abort operation)\n"));
368 else
369 onintr(0);
372 static enum okay
373 pop3_noop1(struct mailbox *mp)
375 enum okay rv;
376 NYD_ENTER;
378 POP3_OUT(rv, "NOOP" NETNL, MB_COMD, goto jleave);
379 POP3_ANSWER(rv, goto jleave);
380 jleave:
381 NYD_LEAVE;
382 return rv;
385 static void
386 pop3alarm(int s)
388 sighandler_type volatile saveint, savepipe;
389 NYD_X; /* Signal handler */
390 UNUSED(s);
392 if (_pop3_lock++ == 0) {
393 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
394 safe_signal(SIGINT, &_pop3_maincatch);
395 savepipe = safe_signal(SIGPIPE, SIG_IGN);
396 if (sigsetjmp(_pop3_jmp, 1)) {
397 interrupts = 0;
398 safe_signal(SIGINT, saveint);
399 safe_signal(SIGPIPE, savepipe);
400 goto jbrk;
402 if (savepipe != SIG_IGN)
403 safe_signal(SIGPIPE, pop3catch);
404 if (pop3_noop1(&mb) != OKAY) {
405 safe_signal(SIGINT, saveint);
406 safe_signal(SIGPIPE, savepipe);
407 goto jleave;
409 safe_signal(SIGINT, saveint);
410 safe_signal(SIGPIPE, savepipe);
412 jbrk:
413 alarm(_pop3_keepalive);
414 jleave:
415 --_pop3_lock;
418 static enum okay
419 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
421 char *cp;
422 enum okay rv;
423 NYD_ENTER;
425 POP3_OUT(rv, "STAT" NETNL, MB_COMD, goto jleave);
426 POP3_ANSWER(rv, goto jleave);
428 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
430 while (*cp != '\0' && spacechar(*cp))
431 ++cp;
433 if (*cp != '\0') {
434 *cnt = (int)strtol(cp, NULL, 10);
435 while (*cp != '\0' && !spacechar(*cp))
436 ++cp;
437 while (*cp != '\0' && spacechar(*cp))
438 ++cp;
439 if (*cp != '\0')
440 *size = (int)strtol(cp, NULL, 10);
441 else
442 rv = STOP;
443 } else
444 rv = STOP;
446 if (rv == STOP)
447 n_err(_("Invalid POP3 STAT response: %s\n"), _pop3_buf);
448 jleave:
449 NYD_LEAVE;
450 return rv;
453 static enum okay
454 pop3_list(struct mailbox *mp, int n, size_t *size)
456 char o[LINESIZE], *cp;
457 enum okay rv;
458 NYD_ENTER;
460 snprintf(o, sizeof o, "LIST %u" NETNL, n);
461 POP3_OUT(rv, o, MB_COMD, goto jleave);
462 POP3_ANSWER(rv, goto jleave);
464 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
466 while (*cp != '\0' && spacechar(*cp))
467 ++cp;
468 while (*cp != '\0' && !spacechar(*cp))
469 ++cp;
470 while (*cp != '\0' && spacechar(*cp))
471 ++cp;
472 if (*cp != '\0')
473 *size = (size_t)strtol(cp, NULL, 10);
474 jleave:
475 NYD_LEAVE;
476 return rv;
479 static void
480 pop3_setptr(struct mailbox *mp, struct sockconn const *scp)
482 size_t i;
483 enum needspec ns;
484 NYD_ENTER;
486 message = scalloc(msgCount + 1, sizeof *message);
487 message[msgCount].m_size = 0;
488 message[msgCount].m_lines = 0;
489 dot = message; /* (Just do it: avoid crash -- shall i now do ointr(0).. */
491 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
492 struct message *m = message + i;
493 m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
494 m->m_block = 0;
495 m->m_offset = 0;
496 m->m_size = m->m_xsize = 0;
499 for (i = 0; UICMP(z, i, <, msgCount); ++i)
500 if (!pop3_list(mp, i + 1, &message[i].m_xsize))
501 goto jleave;
503 /* Force the load of all messages right now */
504 ns = xok_blook(pop3_bulk_load, &scp->sc_url, OXM_ALL)
505 ? NEED_BODY : NEED_HEADER;
506 for (i = 0; UICMP(z, i, <, msgCount); ++i)
507 if (!pop3_get(mp, message + i, ns))
508 goto jleave;
510 srelax_hold();
511 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
512 struct message *m = message + i;
513 char const *cp;
515 if ((cp = hfield1("status", m)) != NULL)
516 while (*cp != '\0') {
517 if (*cp == 'R')
518 m->m_flag |= MREAD;
519 else if (*cp == 'O')
520 m->m_flag &= ~MNEW;
521 ++cp;
524 substdate(m);
525 srelax();
527 srelax_rele();
529 setdot(message);
530 jleave:
531 NYD_LEAVE;
534 static enum okay
535 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
537 char o[LINESIZE], *line, *lp;
538 sighandler_type volatile saveint, savepipe;
539 size_t linesize, linelen, size;
540 int number, lines;
541 int volatile emptyline;
542 off_t offset;
543 enum okay volatile rv;
544 NYD_ENTER;
546 line = NULL; /* TODO line pool */
547 saveint = savepipe = SIG_IGN;
548 linesize = 0;
549 number = (int)PTR2SIZE(m - message + 1);
550 emptyline = 0;
551 rv = STOP;
553 if (mp->mb_sock.s_fd < 0) {
554 n_err(_("POP3 connection already closed\n"));
555 ++_pop3_lock;
556 goto jleave;
559 if (_pop3_lock++ == 0) {
560 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
561 safe_signal(SIGINT, &_pop3_maincatch);
562 savepipe = safe_signal(SIGPIPE, SIG_IGN);
563 if (sigsetjmp(_pop3_jmp, 1))
564 goto jleave;
565 if (savepipe != SIG_IGN)
566 safe_signal(SIGPIPE, pop3catch);
569 fseek(mp->mb_otf, 0L, SEEK_END);
570 offset = ftell(mp->mb_otf);
571 jretry:
572 switch (need) {
573 case NEED_HEADER:
574 snprintf(o, sizeof o, "TOP %u 0" NETNL, number);
575 break;
576 case NEED_BODY:
577 snprintf(o, sizeof o, "RETR %u" NETNL, number);
578 break;
579 case NEED_UNSPEC:
580 abort(); /* XXX */
582 POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
584 if (pop3_answer(mp) == STOP) {
585 if (need == NEED_HEADER) {
586 /* The TOP POP3 command is optional, so retry with entire message */
587 need = NEED_BODY;
588 goto jretry;
590 goto jleave;
593 size = 0;
594 lines = 0;
595 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
596 if (line[0] == '.' && line[1] == NETNL[0] && line[2] == NETNL[1] &&
597 line[3] == '\0') {
598 mp->mb_active &= ~MB_MULT;
599 break;
601 if (line[0] == '.') {
602 lp = line + 1;
603 --linelen;
604 } else
605 lp = line;
606 /* TODO >>
607 * Need to mask 'From ' lines. This cannot be done properly
608 * since some servers pass them as 'From ' and others as
609 * '>From '. Although one could identify the first kind of
610 * server in principle, it is not possible to identify the
611 * second as '>From ' may also come from a server of the
612 * first type as actual data. So do what is absolutely
613 * necessary only - mask 'From '.
615 * If the line is the first line of the message header, it
616 * is likely a real 'From ' line. In this case, it is just
617 * ignored since it violates all standards.
618 * TODO i have *never* seen the latter?!?!?
619 * TODO <<
621 /* Since we simply copy over data without doing any transfer
622 * encoding reclassification/adjustment we *have* to perform
623 * RFC 4155 compliant From_ quoting here */
624 if (emptyline && is_head(lp, linelen, FAL0)) {
625 putc('>', mp->mb_otf);
626 ++size;
628 lines++;
629 if (lp[linelen-1] == NETNL[1] &&
630 (linelen == 1 || lp[linelen-2] == NETNL[0])) {
631 emptyline = linelen <= 2;
632 if (linelen > 2)
633 fwrite(lp, 1, linelen - 2, mp->mb_otf);
634 putc('\n', mp->mb_otf);
635 size += linelen - 1;
636 } else {
637 emptyline = 0;
638 fwrite(lp, 1, linelen, mp->mb_otf);
639 size += linelen;
642 if (!emptyline) {
643 /* This is very ugly; but some POP3 daemons don't end a
644 * message with NETNL NETNL, and we need \n\n for mbox format */
645 putc('\n', mp->mb_otf);
646 ++lines;
647 ++size;
649 m->m_size = size;
650 m->m_lines = lines;
651 m->m_block = mailx_blockof(offset);
652 m->m_offset = mailx_offsetof(offset);
653 fflush(mp->mb_otf);
655 switch (need) {
656 case NEED_HEADER:
657 m->m_have |= HAVE_HEADER;
658 break;
659 case NEED_BODY:
660 m->m_have |= HAVE_HEADER | HAVE_BODY;
661 m->m_xlines = m->m_lines;
662 m->m_xsize = m->m_size;
663 break;
664 case NEED_UNSPEC:
665 break;
668 rv = OKAY;
669 jleave:
670 if (line != NULL)
671 free(line);
672 if (saveint != SIG_IGN)
673 safe_signal(SIGINT, saveint);
674 if (savepipe != SIG_IGN)
675 safe_signal(SIGPIPE, savepipe);
676 --_pop3_lock;
677 NYD_LEAVE;
678 if (interrupts)
679 onintr(0);
680 return rv;
683 static enum okay
684 pop3_exit(struct mailbox *mp)
686 enum okay rv;
687 NYD_ENTER;
689 POP3_OUT(rv, "QUIT" NETNL, MB_COMD, goto jleave);
690 POP3_ANSWER(rv, goto jleave);
691 jleave:
692 NYD_LEAVE;
693 return rv;
696 static enum okay
697 pop3_delete(struct mailbox *mp, int n)
699 char o[LINESIZE];
700 enum okay rv;
701 NYD_ENTER;
703 snprintf(o, sizeof o, "DELE %u" NETNL, n);
704 POP3_OUT(rv, o, MB_COMD, goto jleave);
705 POP3_ANSWER(rv, goto jleave);
706 jleave:
707 NYD_LEAVE;
708 return rv;
711 static enum okay
712 pop3_update(struct mailbox *mp)
714 struct message *m;
715 int dodel, c, gotcha, held;
716 NYD_ENTER;
718 if (!(pstate & PS_EDIT)) {
719 holdbits();
720 c = 0;
721 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
722 if (m->m_flag & MBOX)
723 ++c;
724 if (c > 0)
725 makembox();
728 gotcha = held = 0;
729 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
730 if (pstate & PS_EDIT)
731 dodel = m->m_flag & MDELETED;
732 else
733 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
734 if (dodel) {
735 pop3_delete(mp, PTR2SIZE(m - message + 1));
736 ++gotcha;
737 } else
738 ++held;
740 if (gotcha && (pstate & PS_EDIT)) {
741 printf(_("\"%s\" "), displayname);
742 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
743 ? _("complete\n") : _("updated\n"));
744 } else if (held && !(pstate & PS_EDIT)) {
745 if (held == 1)
746 printf(_("Held 1 message in %s\n"), displayname);
747 else
748 printf(_("Held %d messages in %s\n"), held, displayname);
750 fflush(stdout);
751 NYD_LEAVE;
752 return OKAY;
755 FL enum okay
756 pop3_noop(void)
758 sighandler_type volatile saveint, savepipe;
759 enum okay rv = STOP;
760 NYD_ENTER;
762 _pop3_lock = 1;
763 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
764 safe_signal(SIGINT, &_pop3_maincatch);
765 savepipe = safe_signal(SIGPIPE, SIG_IGN);
766 if (sigsetjmp(_pop3_jmp, 1) == 0) {
767 if (savepipe != SIG_IGN)
768 safe_signal(SIGPIPE, pop3catch);
769 rv = pop3_noop1(&mb);
771 safe_signal(SIGINT, saveint);
772 safe_signal(SIGPIPE, savepipe);
773 _pop3_lock = 0;
774 NYD_LEAVE;
775 return rv;
778 FL int
779 pop3_setfile(char const *server, enum fedit_mode fm)
781 struct sockconn sc;
782 sighandler_type saveint, savepipe;
783 char const *cp;
784 int volatile rv;
785 NYD_ENTER;
787 rv = 1;
788 if (fm & FEDIT_NEWMAIL)
789 goto jleave;
790 rv = -1;
792 if (!url_parse(&sc.sc_url, CPROTO_POP3, server))
793 goto jleave;
794 if (!ok_blook(v15_compat) &&
795 (!sc.sc_url.url_had_user || sc.sc_url.url_pass.s != NULL)) {
796 n_err(_("New-style URL used without *v15-compat* being set\n"));
797 goto jleave;
800 if (!(ok_blook(v15_compat) ? ccred_lookup(&sc.sc_cred, &sc.sc_url)
801 : ccred_lookup_old(&sc.sc_cred, CPROTO_POP3,
802 (sc.sc_url.url_had_user ? sc.sc_url.url_eu_h_p.s
803 : sc.sc_url.url_u_h_p.s))))
804 goto jleave;
806 if (!sopen(&sc.sc_sock, &sc.sc_url))
807 goto jleave;
809 rv = 1;
810 quit();
812 if (fm & FEDIT_SYSBOX)
813 pstate &= ~PS_EDIT;
814 else
815 pstate |= PS_EDIT;
816 if (mb.mb_sock.s_fd >= 0)
817 sclose(&mb.mb_sock);
818 if (mb.mb_itf) {
819 fclose(mb.mb_itf);
820 mb.mb_itf = NULL;
822 if (mb.mb_otf) {
823 fclose(mb.mb_otf);
824 mb.mb_otf = NULL;
827 initbox(sc.sc_url.url_p_u_h_p);
828 mb.mb_type = MB_VOID;
829 _pop3_lock = 1;
830 mb.mb_sock = sc.sc_sock;
832 saveint = safe_signal(SIGINT, SIG_IGN);
833 savepipe = safe_signal(SIGPIPE, SIG_IGN);
834 if (sigsetjmp(_pop3_jmp, 1)) {
835 sclose(&mb.mb_sock);
836 n_err(_("POP3 connection closed\n"));
837 safe_signal(SIGINT, saveint);
838 safe_signal(SIGPIPE, savepipe);
839 _pop3_lock = 0;
840 rv = -1;
841 if (interrupts > 0)
842 onintr(0);
843 goto jleave;
845 if (saveint != SIG_IGN)
846 safe_signal(SIGINT, pop3catch);
847 if (savepipe != SIG_IGN)
848 safe_signal(SIGPIPE, pop3catch);
850 if ((cp = xok_vlook(pop3_keepalive, &sc.sc_url, OXM_ALL)) != NULL) {
851 if ((_pop3_keepalive = (int)strtol(cp, NULL, 10)) > 0) {
852 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
853 alarm(_pop3_keepalive);
857 mb.mb_sock.s_desc = sc.sc_url.url_needs_tls ? "POP3S" : "POP3";
858 mb.mb_sock.s_onclose = pop3_timer_off;
859 if (_pop3_login(&mb, &sc) != OKAY ||
860 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
861 sclose(&mb.mb_sock);
862 pop3_timer_off();
863 safe_signal(SIGINT, saveint);
864 safe_signal(SIGPIPE, savepipe);
865 _pop3_lock = 0;
866 goto jleave;
869 setmsize(msgCount);
870 mb.mb_type = MB_POP3;
871 mb.mb_perm = ((options & OPT_R_FLAG) || (fm & FEDIT_RDONLY)) ? 0 : MB_DELE;
872 pop3_setptr(&mb, &sc);
873 pstate &= ~PS_SAW_COMMAND;
875 safe_signal(SIGINT, saveint);
876 safe_signal(SIGPIPE, savepipe);
877 _pop3_lock = 0;
879 if (options & OPT_EXISTONLY) {
880 rv = (msgCount == 0);
881 goto jleave;
884 if (!(pstate & PS_EDIT) && msgCount == 0) {
885 if (!ok_blook(emptystart))
886 n_err(_("No mail at %s\n"), server);
887 goto jleave;
890 rv = 0;
891 jleave:
892 NYD_LEAVE;
893 return rv;
896 FL enum okay
897 pop3_header(struct message *m)
899 enum okay rv;
900 NYD_ENTER;
902 /* TODO no URL here, no OXM possible; (however it is used in setfile()..) */
903 rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
904 NYD_LEAVE;
905 return rv;
908 FL enum okay
909 pop3_body(struct message *m)
911 enum okay rv;
912 NYD_ENTER;
914 rv = pop3_get(&mb, m, NEED_BODY);
915 NYD_LEAVE;
916 return rv;
919 FL void
920 pop3_quit(void)
922 sighandler_type volatile saveint, savepipe;
923 NYD_ENTER;
925 if (mb.mb_sock.s_fd < 0) {
926 n_err(_("POP3 connection already closed\n"));
927 goto jleave;
930 _pop3_lock = 1;
931 saveint = safe_signal(SIGINT, SIG_IGN);
932 savepipe = safe_signal(SIGPIPE, SIG_IGN);
933 if (sigsetjmp(_pop3_jmp, 1)) {
934 safe_signal(SIGINT, saveint);
935 safe_signal(SIGPIPE, savepipe);
936 _pop3_lock = 0;
937 interrupts = 0;
938 goto jleave;
940 if (saveint != SIG_IGN)
941 safe_signal(SIGINT, pop3catch);
942 if (savepipe != SIG_IGN)
943 safe_signal(SIGPIPE, pop3catch);
944 pop3_update(&mb);
945 pop3_exit(&mb);
946 sclose(&mb.mb_sock);
947 safe_signal(SIGINT, saveint);
948 safe_signal(SIGPIPE, savepipe);
949 _pop3_lock = 0;
950 jleave:
951 NYD_LEAVE;
953 #endif /* HAVE_POP3 */
955 /* s-it-mode */