nail.1: tweak (?) a sentence
[s-mailx.git] / pop3.c
blobee8cd3ad27f8186b83ff14aa3363f1060dce3446
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 if (!xok_blook(pop3_no_apop, &scp->sc_url, oxm)) {
144 #ifdef HAVE_MD5
145 if (ts != NULL) {
146 if ((rv = _pop3_auth_apop(mp, scp, ts)) != OKAY)
147 n_err(_("POP3 \"APOP\" authentication failed, "
148 "maybe try setting *pop3-no-apop*\n"));
149 goto jleave;
150 } else
151 #endif
152 if (options & OPT_D_V) {
153 char const *ccp = "";
155 #ifdef HAVE_SSL
156 if (scp->sc_sock.s_use_ssl)
157 ccp = _(" (over encrypted connection)");
158 #endif
159 n_err(_("No POP3 \"APOP\" support, plain text authentication%s\n"),
160 ccp);
164 rv = _pop3_auth_plain(mp, scp);
165 jleave:
166 NYD_LEAVE;
167 return rv;
170 #ifdef HAVE_MD5
171 static char *
172 _pop3_lookup_apop_timestamp(char const *bp)
174 /* RFC 1939:
175 * A POP3 server which implements the APOP command will include
176 * a timestamp in its banner greeting. The syntax of the timestamp
177 * corresponds to the "msg-id" in [RFC822]
178 * RFC 822:
179 * msg-id = "<" addr-spec ">"
180 * addr-spec = local-part "@" domain */
181 char const *cp, *ep;
182 size_t tl;
183 char *rp = NULL;
184 bool_t hadat = FAL0;
185 NYD_ENTER;
187 if ((cp = strchr(bp, '<')) == NULL)
188 goto jleave;
190 /* xxx What about malformed APOP timestamp (<@>) here? */
191 for (ep = cp; *ep != '\0'; ++ep) {
192 if (spacechar(*ep))
193 goto jleave;
194 else if (*ep == '@')
195 hadat = TRU1;
196 else if (*ep == '>') {
197 if (!hadat)
198 goto jleave;
199 break;
202 if (*ep != '>')
203 goto jleave;
205 tl = PTR2SIZE(++ep - cp);
206 rp = salloc(tl +1);
207 memcpy(rp, cp, tl);
208 rp[tl] = '\0';
209 jleave:
210 NYD_LEAVE;
211 return rp;
213 #endif
215 #ifdef HAVE_MD5
216 static enum okay
217 _pop3_auth_apop(struct mailbox *mp, struct sockconn const *scp, char const *ts)
219 unsigned char digest[16];
220 char hex[MD5TOHEX_SIZE], *cp;
221 md5_ctx ctx;
222 size_t i;
223 enum okay rv = STOP;
224 NYD_ENTER;
226 md5_init(&ctx);
227 md5_update(&ctx, (uc_i*)UNCONST(ts), strlen(ts));
228 md5_update(&ctx, (uc_i*)scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
229 md5_final(digest, &ctx);
230 md5tohex(hex, digest);
232 i = scp->sc_cred.cc_user.l;
233 cp = ac_alloc(5 + i + 1 + MD5TOHEX_SIZE + sizeof(NETNL)-1 +1);
235 memcpy(cp, "APOP ", 5);
236 memcpy(cp + 5, scp->sc_cred.cc_user.s, i);
237 i += 5;
238 cp[i++] = ' ';
239 memcpy(cp + i, hex, MD5TOHEX_SIZE);
240 i += MD5TOHEX_SIZE;
241 memcpy(cp + i, NETNL, sizeof(NETNL));
242 POP3_OUT(rv, cp, MB_COMD, goto jleave);
243 POP3_ANSWER(rv, goto jleave);
245 rv = OKAY;
246 jleave:
247 ac_free(cp);
248 NYD_LEAVE;
249 return rv;
251 #endif /* HAVE_MD5 */
253 static enum okay
254 _pop3_auth_plain(struct mailbox *mp, struct sockconn const *scp)
256 char *cp;
257 enum okay rv = STOP;
258 NYD_ENTER;
260 /* The USER/PASS plain text version */
261 cp = ac_alloc(MAX(scp->sc_cred.cc_user.l, scp->sc_cred.cc_pass.l) + 5 +
262 sizeof(NETNL)-1 +1);
264 memcpy(cp, "USER ", 5);
265 memcpy(cp + 5, scp->sc_cred.cc_user.s, scp->sc_cred.cc_user.l);
266 memcpy(cp + 5 + scp->sc_cred.cc_user.l, NETNL, sizeof(NETNL));
267 POP3_OUT(rv, cp, MB_COMD, goto jleave);
268 POP3_ANSWER(rv, goto jleave);
270 memcpy(cp, "PASS ", 5);
271 memcpy(cp + 5, scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
272 memcpy(cp + 5 + scp->sc_cred.cc_pass.l, NETNL, sizeof(NETNL));
273 POP3_OUT(rv, cp, MB_COMD, goto jleave);
274 POP3_ANSWER(rv, goto jleave);
276 rv = OKAY;
277 jleave:
278 ac_free(cp);
279 NYD_LEAVE;
280 return rv;
283 static void
284 pop3_timer_off(void)
286 NYD_ENTER;
287 if (_pop3_keepalive > 0) {
288 alarm(0);
289 safe_signal(SIGALRM, _pop3_savealrm);
291 NYD_LEAVE;
294 static enum okay
295 pop3_answer(struct mailbox *mp)
297 int sz;
298 enum okay rv = STOP;
299 NYD_ENTER;
301 jretry:
302 if ((sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock)) > 0) {
303 if ((mp->mb_active & (MB_COMD | MB_MULT)) == MB_MULT)
304 goto jmultiline;
305 if (options & OPT_VERBVERB)
306 n_err(_pop3_buf);
307 switch (*_pop3_buf) {
308 case '+':
309 rv = OKAY;
310 mp->mb_active &= ~MB_COMD;
311 break;
312 case '-':
313 rv = STOP;
314 mp->mb_active = MB_NONE;
315 n_err(_("POP3 error: %s"), _pop3_buf);
316 break;
317 default:
318 /* If the answer starts neither with '+' nor with '-', it must be part
319 * of a multiline response. Get lines until a single dot appears */
320 jmultiline:
321 while (_pop3_buf[0] != '.' || _pop3_buf[1] != NETNL[0] ||
322 _pop3_buf[2] != NETNL[1] || _pop3_buf[3] != '\0') {
323 sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock);
324 if (sz <= 0)
325 goto jeof;
327 mp->mb_active &= ~MB_MULT;
328 if (mp->mb_active != MB_NONE)
329 goto jretry;
331 } else {
332 jeof:
333 rv = STOP;
334 mp->mb_active = MB_NONE;
336 NYD_LEAVE;
337 return rv;
340 static enum okay
341 pop3_finish(struct mailbox *mp)
343 NYD_ENTER;
344 while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
345 pop3_answer(mp);
346 NYD_LEAVE;
347 return OKAY;
350 static void
351 pop3catch(int s)
353 NYD_X; /* Signal handler */
354 switch (s) {
355 case SIGINT:
356 /*n_err_sighdl(_("Interrupt during POP3 operation\n"));*/
357 interrupts = 2; /* Force "Interrupt" message shall we onintr(0) */
358 siglongjmp(_pop3_jmp, 1);
359 break;
360 case SIGPIPE:
361 n_err_sighdl(_("Received SIGPIPE during POP3 operation\n"));
362 break;
366 static void
367 _pop3_maincatch(int s)
369 NYD_X; /* Signal handler */
370 UNUSED(s);
371 if (interrupts++ == 0)
372 n_err_sighdl(_("\n(Interrupt -- one more to abort operation)\n"));
373 else
374 onintr(0);
377 static enum okay
378 pop3_noop1(struct mailbox *mp)
380 enum okay rv;
381 NYD_ENTER;
383 POP3_OUT(rv, "NOOP" NETNL, MB_COMD, goto jleave);
384 POP3_ANSWER(rv, goto jleave);
385 jleave:
386 NYD_LEAVE;
387 return rv;
390 static void
391 pop3alarm(int s)
393 sighandler_type volatile saveint, savepipe;
394 NYD_X; /* Signal handler */
395 UNUSED(s);
397 if (_pop3_lock++ == 0) {
398 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
399 safe_signal(SIGINT, &_pop3_maincatch);
400 savepipe = safe_signal(SIGPIPE, SIG_IGN);
401 if (sigsetjmp(_pop3_jmp, 1)) {
402 interrupts = 0;
403 safe_signal(SIGINT, saveint);
404 safe_signal(SIGPIPE, savepipe);
405 goto jbrk;
407 if (savepipe != SIG_IGN)
408 safe_signal(SIGPIPE, pop3catch);
409 if (pop3_noop1(&mb) != OKAY) {
410 safe_signal(SIGINT, saveint);
411 safe_signal(SIGPIPE, savepipe);
412 goto jleave;
414 safe_signal(SIGINT, saveint);
415 safe_signal(SIGPIPE, savepipe);
417 jbrk:
418 alarm(_pop3_keepalive);
419 jleave:
420 --_pop3_lock;
423 static enum okay
424 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
426 char *cp;
427 enum okay rv;
428 NYD_ENTER;
430 POP3_OUT(rv, "STAT" NETNL, MB_COMD, goto jleave);
431 POP3_ANSWER(rv, goto jleave);
433 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
435 while (*cp != '\0' && spacechar(*cp))
436 ++cp;
438 if (*cp != '\0') {
439 *cnt = (int)strtol(cp, NULL, 10);
440 while (*cp != '\0' && !spacechar(*cp))
441 ++cp;
442 while (*cp != '\0' && spacechar(*cp))
443 ++cp;
444 if (*cp != '\0')
445 *size = (int)strtol(cp, NULL, 10);
446 else
447 rv = STOP;
448 } else
449 rv = STOP;
451 if (rv == STOP)
452 n_err(_("Invalid POP3 STAT response: %s\n"), _pop3_buf);
453 jleave:
454 NYD_LEAVE;
455 return rv;
458 static enum okay
459 pop3_list(struct mailbox *mp, int n, size_t *size)
461 char o[LINESIZE], *cp;
462 enum okay rv;
463 NYD_ENTER;
465 snprintf(o, sizeof o, "LIST %u" NETNL, n);
466 POP3_OUT(rv, o, MB_COMD, goto jleave);
467 POP3_ANSWER(rv, goto jleave);
469 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
471 while (*cp != '\0' && spacechar(*cp))
472 ++cp;
473 while (*cp != '\0' && !spacechar(*cp))
474 ++cp;
475 while (*cp != '\0' && spacechar(*cp))
476 ++cp;
477 if (*cp != '\0')
478 *size = (size_t)strtol(cp, NULL, 10);
479 jleave:
480 NYD_LEAVE;
481 return rv;
484 static void
485 pop3_setptr(struct mailbox *mp, struct sockconn const *scp)
487 size_t i;
488 enum needspec ns;
489 NYD_ENTER;
491 message = scalloc(msgCount + 1, sizeof *message);
492 message[msgCount].m_size = 0;
493 message[msgCount].m_lines = 0;
494 dot = message; /* (Just do it: avoid crash -- shall i now do ointr(0).. */
496 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
497 struct message *m = message + i;
498 m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
499 m->m_block = 0;
500 m->m_offset = 0;
501 m->m_size = m->m_xsize = 0;
504 for (i = 0; UICMP(z, i, <, msgCount); ++i)
505 if (!pop3_list(mp, i + 1, &message[i].m_xsize))
506 goto jleave;
508 /* Force the load of all messages right now */
509 ns = xok_blook(pop3_bulk_load, &scp->sc_url, OXM_ALL)
510 ? NEED_BODY : NEED_HEADER;
511 for (i = 0; UICMP(z, i, <, msgCount); ++i)
512 if (!pop3_get(mp, message + i, ns))
513 goto jleave;
515 srelax_hold();
516 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
517 struct message *m = message + i;
518 char const *cp;
520 if ((cp = hfield1("status", m)) != NULL)
521 while (*cp != '\0') {
522 if (*cp == 'R')
523 m->m_flag |= MREAD;
524 else if (*cp == 'O')
525 m->m_flag &= ~MNEW;
526 ++cp;
529 substdate(m);
530 srelax();
532 srelax_rele();
534 setdot(message);
535 jleave:
536 NYD_LEAVE;
539 static enum okay
540 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
542 char o[LINESIZE], *line, *lp;
543 sighandler_type volatile saveint, savepipe;
544 size_t linesize, linelen, size;
545 int number, lines;
546 int volatile emptyline;
547 off_t offset;
548 enum okay volatile rv;
549 NYD_ENTER;
551 line = NULL; /* TODO line pool */
552 saveint = savepipe = SIG_IGN;
553 linesize = 0;
554 number = (int)PTR2SIZE(m - message + 1);
555 emptyline = 0;
556 rv = STOP;
558 if (mp->mb_sock.s_fd < 0) {
559 n_err(_("POP3 connection already closed\n"));
560 ++_pop3_lock;
561 goto jleave;
564 if (_pop3_lock++ == 0) {
565 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
566 safe_signal(SIGINT, &_pop3_maincatch);
567 savepipe = safe_signal(SIGPIPE, SIG_IGN);
568 if (sigsetjmp(_pop3_jmp, 1))
569 goto jleave;
570 if (savepipe != SIG_IGN)
571 safe_signal(SIGPIPE, pop3catch);
574 fseek(mp->mb_otf, 0L, SEEK_END);
575 offset = ftell(mp->mb_otf);
576 jretry:
577 switch (need) {
578 case NEED_HEADER:
579 snprintf(o, sizeof o, "TOP %u 0" NETNL, number);
580 break;
581 case NEED_BODY:
582 snprintf(o, sizeof o, "RETR %u" NETNL, number);
583 break;
584 case NEED_UNSPEC:
585 abort(); /* XXX */
587 POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
589 if (pop3_answer(mp) == STOP) {
590 if (need == NEED_HEADER) {
591 /* The TOP POP3 command is optional, so retry with entire message */
592 need = NEED_BODY;
593 goto jretry;
595 goto jleave;
598 size = 0;
599 lines = 0;
600 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
601 if (line[0] == '.' && line[1] == NETNL[0] && line[2] == NETNL[1] &&
602 line[3] == '\0') {
603 mp->mb_active &= ~MB_MULT;
604 break;
606 if (line[0] == '.') {
607 lp = line + 1;
608 --linelen;
609 } else
610 lp = line;
611 /* TODO >>
612 * Need to mask 'From ' lines. This cannot be done properly
613 * since some servers pass them as 'From ' and others as
614 * '>From '. Although one could identify the first kind of
615 * server in principle, it is not possible to identify the
616 * second as '>From ' may also come from a server of the
617 * first type as actual data. So do what is absolutely
618 * necessary only - mask 'From '.
620 * If the line is the first line of the message header, it
621 * is likely a real 'From ' line. In this case, it is just
622 * ignored since it violates all standards.
623 * TODO i have *never* seen the latter?!?!?
624 * TODO <<
626 /* Since we simply copy over data without doing any transfer
627 * encoding reclassification/adjustment we *have* to perform
628 * RFC 4155 compliant From_ quoting here */
629 if (emptyline && is_head(lp, linelen, TRU1)) {
630 putc('>', mp->mb_otf);
631 ++size;
633 lines++;
634 if (lp[linelen-1] == NETNL[1] &&
635 (linelen == 1 || lp[linelen-2] == NETNL[0])) {
636 emptyline = linelen <= 2;
637 if (linelen > 2)
638 fwrite(lp, 1, linelen - 2, mp->mb_otf);
639 putc('\n', mp->mb_otf);
640 size += linelen - 1;
641 } else {
642 emptyline = 0;
643 fwrite(lp, 1, linelen, mp->mb_otf);
644 size += linelen;
647 if (!emptyline) {
648 /* This is very ugly; but some POP3 daemons don't end a
649 * message with NETNL NETNL, and we need \n\n for mbox format */
650 putc('\n', mp->mb_otf);
651 ++lines;
652 ++size;
654 m->m_size = size;
655 m->m_lines = lines;
656 m->m_block = mailx_blockof(offset);
657 m->m_offset = mailx_offsetof(offset);
658 fflush(mp->mb_otf);
660 switch (need) {
661 case NEED_HEADER:
662 m->m_have |= HAVE_HEADER;
663 break;
664 case NEED_BODY:
665 m->m_have |= HAVE_HEADER | HAVE_BODY;
666 m->m_xlines = m->m_lines;
667 m->m_xsize = m->m_size;
668 break;
669 case NEED_UNSPEC:
670 break;
673 rv = OKAY;
674 jleave:
675 if (line != NULL)
676 free(line);
677 if (saveint != SIG_IGN)
678 safe_signal(SIGINT, saveint);
679 if (savepipe != SIG_IGN)
680 safe_signal(SIGPIPE, savepipe);
681 --_pop3_lock;
682 NYD_LEAVE;
683 if (interrupts)
684 onintr(0);
685 return rv;
688 static enum okay
689 pop3_exit(struct mailbox *mp)
691 enum okay rv;
692 NYD_ENTER;
694 POP3_OUT(rv, "QUIT" NETNL, MB_COMD, goto jleave);
695 POP3_ANSWER(rv, goto jleave);
696 jleave:
697 NYD_LEAVE;
698 return rv;
701 static enum okay
702 pop3_delete(struct mailbox *mp, int n)
704 char o[LINESIZE];
705 enum okay rv;
706 NYD_ENTER;
708 snprintf(o, sizeof o, "DELE %u" NETNL, n);
709 POP3_OUT(rv, o, MB_COMD, goto jleave);
710 POP3_ANSWER(rv, goto jleave);
711 jleave:
712 NYD_LEAVE;
713 return rv;
716 static enum okay
717 pop3_update(struct mailbox *mp)
719 struct message *m;
720 int dodel, c, gotcha, held;
721 NYD_ENTER;
723 if (!(pstate & PS_EDIT)) {
724 holdbits();
725 c = 0;
726 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
727 if (m->m_flag & MBOX)
728 ++c;
729 if (c > 0)
730 makembox();
733 gotcha = held = 0;
734 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
735 if (pstate & PS_EDIT)
736 dodel = m->m_flag & MDELETED;
737 else
738 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
739 if (dodel) {
740 pop3_delete(mp, PTR2SIZE(m - message + 1));
741 ++gotcha;
742 } else
743 ++held;
745 if (gotcha && (pstate & PS_EDIT)) {
746 printf(_("\"%s\" "), displayname);
747 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
748 ? _("complete\n") : _("updated\n"));
749 } else if (held && !(pstate & PS_EDIT)) {
750 if (held == 1)
751 printf(_("Held 1 message in %s\n"), displayname);
752 else
753 printf(_("Held %d messages in %s\n"), held, displayname);
755 fflush(stdout);
756 NYD_LEAVE;
757 return OKAY;
760 FL enum okay
761 pop3_noop(void)
763 sighandler_type volatile saveint, savepipe;
764 enum okay rv = STOP;
765 NYD_ENTER;
767 _pop3_lock = 1;
768 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
769 safe_signal(SIGINT, &_pop3_maincatch);
770 savepipe = safe_signal(SIGPIPE, SIG_IGN);
771 if (sigsetjmp(_pop3_jmp, 1) == 0) {
772 if (savepipe != SIG_IGN)
773 safe_signal(SIGPIPE, pop3catch);
774 rv = pop3_noop1(&mb);
776 safe_signal(SIGINT, saveint);
777 safe_signal(SIGPIPE, savepipe);
778 _pop3_lock = 0;
779 NYD_LEAVE;
780 return rv;
783 FL int
784 pop3_setfile(char const *server, enum fedit_mode fm)
786 struct sockconn sc;
787 sighandler_type saveint, savepipe;
788 char const *cp;
789 int volatile rv;
790 NYD_ENTER;
792 rv = 1;
793 if (fm & FEDIT_NEWMAIL)
794 goto jleave;
795 rv = -1;
797 if (!url_parse(&sc.sc_url, CPROTO_POP3, server))
798 goto jleave;
799 if (!ok_blook(v15_compat) &&
800 (!sc.sc_url.url_had_user || sc.sc_url.url_pass.s != NULL)) {
801 n_err(_("New-style URL used without *v15-compat* being set\n"));
802 goto jleave;
805 if (!(ok_blook(v15_compat) ? ccred_lookup(&sc.sc_cred, &sc.sc_url)
806 : ccred_lookup_old(&sc.sc_cred, CPROTO_POP3,
807 (sc.sc_url.url_had_user ? sc.sc_url.url_eu_h_p.s
808 : sc.sc_url.url_u_h_p.s))))
809 goto jleave;
811 if (!sopen(&sc.sc_sock, &sc.sc_url))
812 goto jleave;
814 rv = 1;
815 quit();
817 if (fm & FEDIT_SYSBOX)
818 pstate &= ~PS_EDIT;
819 else
820 pstate |= PS_EDIT;
821 if (mb.mb_sock.s_fd >= 0)
822 sclose(&mb.mb_sock);
823 if (mb.mb_itf) {
824 fclose(mb.mb_itf);
825 mb.mb_itf = NULL;
827 if (mb.mb_otf) {
828 fclose(mb.mb_otf);
829 mb.mb_otf = NULL;
832 initbox(sc.sc_url.url_p_u_h_p);
833 mb.mb_type = MB_VOID;
834 _pop3_lock = 1;
835 mb.mb_sock = sc.sc_sock;
837 saveint = safe_signal(SIGINT, SIG_IGN);
838 savepipe = safe_signal(SIGPIPE, SIG_IGN);
839 if (sigsetjmp(_pop3_jmp, 1)) {
840 sclose(&mb.mb_sock);
841 n_err(_("POP3 connection closed\n"));
842 safe_signal(SIGINT, saveint);
843 safe_signal(SIGPIPE, savepipe);
844 _pop3_lock = 0;
845 rv = -1;
846 if (interrupts > 0)
847 onintr(0);
848 goto jleave;
850 if (saveint != SIG_IGN)
851 safe_signal(SIGINT, pop3catch);
852 if (savepipe != SIG_IGN)
853 safe_signal(SIGPIPE, pop3catch);
855 if ((cp = xok_vlook(pop3_keepalive, &sc.sc_url, OXM_ALL)) != NULL) {
856 if ((_pop3_keepalive = (int)strtol(cp, NULL, 10)) > 0) {
857 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
858 alarm(_pop3_keepalive);
862 mb.mb_sock.s_desc = sc.sc_url.url_needs_tls ? "POP3S" : "POP3";
863 mb.mb_sock.s_onclose = pop3_timer_off;
864 if (_pop3_login(&mb, &sc) != OKAY ||
865 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
866 sclose(&mb.mb_sock);
867 pop3_timer_off();
868 safe_signal(SIGINT, saveint);
869 safe_signal(SIGPIPE, savepipe);
870 _pop3_lock = 0;
871 goto jleave;
874 setmsize(msgCount);
875 mb.mb_type = MB_POP3;
876 mb.mb_perm = ((options & OPT_R_FLAG) || (fm & FEDIT_RDONLY)) ? 0 : MB_DELE;
877 pop3_setptr(&mb, &sc);
878 pstate &= ~PS_SAW_COMMAND;
880 safe_signal(SIGINT, saveint);
881 safe_signal(SIGPIPE, savepipe);
882 _pop3_lock = 0;
884 if (options & OPT_EXISTONLY) {
885 rv = (msgCount == 0);
886 goto jleave;
889 if (!(pstate & PS_EDIT) && msgCount == 0) {
890 if (!ok_blook(emptystart))
891 n_err(_("No mail at %s\n"), server);
892 goto jleave;
895 rv = 0;
896 jleave:
897 NYD_LEAVE;
898 return rv;
901 FL enum okay
902 pop3_header(struct message *m)
904 enum okay rv;
905 NYD_ENTER;
907 /* TODO no URL here, no OXM possible; (however it is used in setfile()..) */
908 rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
909 NYD_LEAVE;
910 return rv;
913 FL enum okay
914 pop3_body(struct message *m)
916 enum okay rv;
917 NYD_ENTER;
919 rv = pop3_get(&mb, m, NEED_BODY);
920 NYD_LEAVE;
921 return rv;
924 FL void
925 pop3_quit(void)
927 sighandler_type volatile saveint, savepipe;
928 NYD_ENTER;
930 if (mb.mb_sock.s_fd < 0) {
931 n_err(_("POP3 connection already closed\n"));
932 goto jleave;
935 _pop3_lock = 1;
936 saveint = safe_signal(SIGINT, SIG_IGN);
937 savepipe = safe_signal(SIGPIPE, SIG_IGN);
938 if (sigsetjmp(_pop3_jmp, 1)) {
939 safe_signal(SIGINT, saveint);
940 safe_signal(SIGPIPE, saveint);
941 _pop3_lock = 0;
942 interrupts = 0;
943 goto jleave;
945 if (saveint != SIG_IGN)
946 safe_signal(SIGINT, pop3catch);
947 if (savepipe != SIG_IGN)
948 safe_signal(SIGPIPE, pop3catch);
949 pop3_update(&mb);
950 pop3_exit(&mb);
951 sclose(&mb.mb_sock);
952 safe_signal(SIGINT, saveint);
953 safe_signal(SIGPIPE, savepipe);
954 _pop3_lock = 0;
955 jleave:
956 NYD_LEAVE;
958 #endif /* HAVE_POP3 */
960 /* s-it-mode */