([BWDIC!]) Big notational warp of SSL to TLS; drop rand EGD support
[s-mailx.git] / pop3.c
bloba29e841c4fa1fc3d893804acc12f289d60c7b72e
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ POP3 (RFCs 1939, 2595) client.
3 *@ TODO UIDL (as struct message.m_uid, *headline* %U), etc...
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
7 */
8 /*
9 * Copyright (c) 2002
10 * Gunnar Ritter. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
40 #undef n_FILE
41 #define n_FILE pop3
43 #ifndef HAVE_AMALGAMATION
44 # include "nail.h"
45 #endif
47 EMPTY_FILE()
48 #ifdef HAVE_POP3
50 #define POP3_ANSWER(RV,ACTIONSTOP) \
51 do if (((RV) = pop3_answer(mp)) == STOP) {\
52 ACTIONSTOP;\
53 } while (0)
55 #define POP3_OUT(RV,X,Y,ACTIONSTOP) \
56 do {\
57 if (((RV) = pop3_finish(mp)) == STOP) {\
58 ACTIONSTOP;\
60 if (n_poption & n_PO_VERBVERB)\
61 n_err(">>> %s", X);\
62 mp->mb_active |= Y;\
63 if (((RV) = swrite(&mp->mb_sock, X)) == STOP) {\
64 ACTIONSTOP;\
66 } while (0)
68 static char *_pop3_buf;
69 static size_t _pop3_bufsize;
70 static sigjmp_buf _pop3_jmp;
71 static sighandler_type _pop3_savealrm;
72 static si32_t _pop3_keepalive;
73 static int volatile _pop3_lock;
75 /* Perform entire login handshake */
76 static enum okay _pop3_login(struct mailbox *mp, struct sockconn *scp);
78 /* APOP: get greeting credential or NULL */
79 #ifdef HAVE_MD5
80 static char * _pop3_lookup_apop_timestamp(char const *bp);
81 #endif
83 /* Several authentication methods */
84 #ifdef HAVE_MD5
85 static enum okay _pop3_auth_apop(struct mailbox *mp,
86 struct sockconn const *scp, char const *ts);
87 #endif
88 static enum okay _pop3_auth_plain(struct mailbox *mp,
89 struct sockconn const *scp);
91 static void pop3_timer_off(void);
92 static enum okay pop3_answer(struct mailbox *mp);
93 static enum okay pop3_finish(struct mailbox *mp);
94 static void pop3catch(int s);
95 static void _pop3_maincatch(int s);
96 static enum okay pop3_noop1(struct mailbox *mp);
97 static void pop3alarm(int s);
98 static enum okay pop3_stat(struct mailbox *mp, off_t *size, int *cnt);
99 static enum okay pop3_list(struct mailbox *mp, int n, size_t *size);
100 static void pop3_setptr(struct mailbox *mp,
101 struct sockconn const *scp);
102 static enum okay pop3_get(struct mailbox *mp, struct message *m,
103 enum needspec need);
104 static enum okay pop3_exit(struct mailbox *mp);
105 static enum okay pop3_delete(struct mailbox *mp, int n);
106 static enum okay pop3_update(struct mailbox *mp);
108 static enum okay
109 _pop3_login(struct mailbox *mp, struct sockconn *scp)
111 #ifdef HAVE_MD5
112 char *ts;
113 #endif
114 enum okey_xlook_mode oxm;
115 enum okay rv;
116 NYD_ENTER;
118 oxm = ok_blook(v15_compat) ? OXM_ALL : OXM_PLAIN | OXM_U_H_P;
120 /* Get the greeting, check whether APOP is advertised */
121 POP3_ANSWER(rv, goto jleave);
122 #ifdef HAVE_MD5
123 ts = _pop3_lookup_apop_timestamp(_pop3_buf);
124 #endif
126 /* If not yet secured, can we upgrade to TLS? */
127 #ifdef HAVE_TLS
128 if (!(scp->sc_url.url_flags & n_URL_TLS_REQUIRED) &&
129 xok_blook(pop3_use_starttls, &scp->sc_url, oxm)) {
130 POP3_OUT(rv, "STLS" NETNL, MB_COMD, goto jleave);
131 POP3_ANSWER(rv, goto jleave);
132 if(!n_tls_open(&scp->sc_url, &scp->sc_sock)){
133 rv = STOP;
134 goto jleave;
137 #else
138 if (xok_blook(pop3_use_starttls, &scp->sc_url, oxm)) {
139 n_err(_("No TLS support compiled in\n"));
140 rv = STOP;
141 goto jleave;
143 #endif
145 /* Use the APOP single roundtrip? */
146 #ifdef HAVE_MD5
147 if (ts != NULL && !xok_blook(pop3_no_apop, &scp->sc_url, oxm)) {
148 if ((rv = _pop3_auth_apop(mp, scp, ts)) != OKAY) {
149 char const *ccp;
151 # ifdef HAVE_TLS
152 if (scp->sc_sock.s_use_tls)
153 ccp = _("over an encrypted connection");
154 else
155 # endif
156 ccp = _("(unsafe clear text!)");
157 n_err(_("POP3 APOP authentication failed!\n"
158 " Server indicated support.. Set *pop3-no-apop*\n"
159 " for plain text authentication %s\n"), ccp);
161 goto jleave;
163 #endif
165 rv = _pop3_auth_plain(mp, scp);
166 jleave:
167 NYD_LEAVE;
168 return rv;
171 #ifdef HAVE_MD5
172 static char *
173 _pop3_lookup_apop_timestamp(char const *bp)
175 /* RFC 1939:
176 * A POP3 server which implements the APOP command will include
177 * a timestamp in its banner greeting. The syntax of the timestamp
178 * corresponds to the "msg-id" in [RFC822]
179 * RFC 822:
180 * msg-id = "<" addr-spec ">"
181 * addr-spec = local-part "@" domain */
182 char const *cp, *ep;
183 size_t tl;
184 char *rp = NULL;
185 bool_t hadat = FAL0;
186 NYD_ENTER;
188 if ((cp = strchr(bp, '<')) == NULL)
189 goto jleave;
191 /* xxx What about malformed APOP timestamp (<@>) here? */
192 for (ep = cp; *ep != '\0'; ++ep) {
193 if (spacechar(*ep))
194 goto jleave;
195 else if (*ep == '@')
196 hadat = TRU1;
197 else if (*ep == '>') {
198 if (!hadat)
199 goto jleave;
200 break;
203 if (*ep != '>')
204 goto jleave;
206 tl = PTR2SIZE(++ep - cp);
207 rp = n_autorec_alloc(tl +1);
208 memcpy(rp, cp, tl);
209 rp[tl] = '\0';
210 jleave:
211 NYD_LEAVE;
212 return rp;
214 #endif
216 #ifdef HAVE_MD5
217 static enum okay
218 _pop3_auth_apop(struct mailbox *mp, struct sockconn const *scp, char const *ts)
220 unsigned char digest[16];
221 char hex[MD5TOHEX_SIZE], *cp;
222 md5_ctx ctx;
223 size_t i;
224 enum okay rv = STOP;
225 NYD_ENTER;
227 md5_init(&ctx);
228 md5_update(&ctx, (uc_i*)n_UNCONST(ts), strlen(ts));
229 md5_update(&ctx, (uc_i*)scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
230 md5_final(digest, &ctx);
231 md5tohex(hex, digest);
233 i = scp->sc_cred.cc_user.l;
234 cp = n_lofi_alloc(5 + i + 1 + MD5TOHEX_SIZE + sizeof(NETNL)-1 +1);
236 memcpy(cp, "APOP ", 5);
237 memcpy(cp + 5, scp->sc_cred.cc_user.s, i);
238 i += 5;
239 cp[i++] = ' ';
240 memcpy(cp + i, hex, MD5TOHEX_SIZE);
241 i += MD5TOHEX_SIZE;
242 memcpy(cp + i, NETNL, sizeof(NETNL));
243 POP3_OUT(rv, cp, MB_COMD, goto jleave);
244 POP3_ANSWER(rv, goto jleave);
246 rv = OKAY;
247 jleave:
248 n_lofi_free(cp);
249 NYD_LEAVE;
250 return rv;
252 #endif /* HAVE_MD5 */
254 static enum okay
255 _pop3_auth_plain(struct mailbox *mp, struct sockconn const *scp)
257 char *cp;
258 enum okay rv = STOP;
259 NYD_ENTER;
261 /* The USER/PASS plain text version */
262 cp = n_lofi_alloc(n_MAX(scp->sc_cred.cc_user.l, scp->sc_cred.cc_pass.l) +
263 5 + sizeof(NETNL)-1 +1);
265 memcpy(cp, "USER ", 5);
266 memcpy(cp + 5, scp->sc_cred.cc_user.s, scp->sc_cred.cc_user.l);
267 memcpy(cp + 5 + scp->sc_cred.cc_user.l, NETNL, sizeof(NETNL));
268 POP3_OUT(rv, cp, MB_COMD, goto jleave);
269 POP3_ANSWER(rv, goto jleave);
271 memcpy(cp, "PASS ", 5);
272 memcpy(cp + 5, scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
273 memcpy(cp + 5 + scp->sc_cred.cc_pass.l, NETNL, sizeof(NETNL));
274 POP3_OUT(rv, cp, MB_COMD, goto jleave);
275 POP3_ANSWER(rv, goto jleave);
277 rv = OKAY;
278 jleave:
279 n_lofi_free(cp);
280 NYD_LEAVE;
281 return rv;
284 static void
285 pop3_timer_off(void)
287 NYD_ENTER;
288 if (_pop3_keepalive > 0) {
289 alarm(0);
290 safe_signal(SIGALRM, _pop3_savealrm);
292 NYD_LEAVE;
295 static enum okay
296 pop3_answer(struct mailbox *mp)
298 int sz;
299 size_t blen;
300 enum okay rv = STOP;
301 NYD_ENTER;
303 jretry:
304 if ((sz = sgetline(&_pop3_buf, &_pop3_bufsize, &blen, &mp->mb_sock)) > 0) {
305 if ((mp->mb_active & (MB_COMD | MB_MULT)) == MB_MULT)
306 goto jmultiline;
307 if (n_poption & n_PO_VERBVERB)
308 n_err(_pop3_buf);
309 switch (*_pop3_buf) {
310 case '+':
311 rv = OKAY;
312 mp->mb_active &= ~MB_COMD;
313 break;
314 case '-':
315 rv = STOP;
316 mp->mb_active = MB_NONE;
317 while (blen > 0 &&
318 (_pop3_buf[blen - 1] == NETNL[0] ||
319 _pop3_buf[blen - 1] == NETNL[1]))
320 _pop3_buf[--blen] = '\0';
321 n_err(_("POP3 error: %s\n"), _pop3_buf);
322 break;
323 default:
324 /* If the answer starts neither with '+' nor with '-', it must be part
325 * of a multiline response. Get lines until a single dot appears */
326 jmultiline:
327 while (_pop3_buf[0] != '.' || _pop3_buf[1] != NETNL[0] ||
328 _pop3_buf[2] != NETNL[1] || _pop3_buf[3] != '\0') {
329 sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock);
330 if (sz <= 0)
331 goto jeof;
333 mp->mb_active &= ~MB_MULT;
334 if (mp->mb_active != MB_NONE)
335 goto jretry;
337 } else {
338 jeof:
339 rv = STOP;
340 mp->mb_active = MB_NONE;
342 NYD_LEAVE;
343 return rv;
346 static enum okay
347 pop3_finish(struct mailbox *mp)
349 NYD_ENTER;
350 while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
351 pop3_answer(mp);
352 NYD_LEAVE;
353 return OKAY;
356 static void
357 pop3catch(int s)
359 NYD_X; /* Signal handler */
360 switch (s) {
361 case SIGINT:
362 /*n_err_sighdl(_("Interrupt during POP3 operation\n"));*/
363 interrupts = 2; /* Force "Interrupt" message shall we onintr(0) */
364 siglongjmp(_pop3_jmp, 1);
365 case SIGPIPE:
366 n_err_sighdl(_("Received SIGPIPE during POP3 operation\n"));
367 break;
371 static void
372 _pop3_maincatch(int s)
374 NYD_X; /* Signal handler */
375 n_UNUSED(s);
376 if (interrupts == 0)
377 n_err_sighdl(_("\n(Interrupt -- one more to abort operation)\n"));
378 else {
379 interrupts = 1;
380 siglongjmp(_pop3_jmp, 1);
384 static enum okay
385 pop3_noop1(struct mailbox *mp)
387 enum okay rv;
388 NYD_ENTER;
390 POP3_OUT(rv, "NOOP" NETNL, MB_COMD, goto jleave);
391 POP3_ANSWER(rv, goto jleave);
392 jleave:
393 NYD_LEAVE;
394 return rv;
397 static void
398 pop3alarm(int s)
400 sighandler_type volatile saveint, savepipe;
401 NYD_X; /* Signal handler */
402 n_UNUSED(s);
404 if (_pop3_lock++ == 0) {
405 hold_all_sigs();
406 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
407 safe_signal(SIGINT, &_pop3_maincatch);
408 savepipe = safe_signal(SIGPIPE, SIG_IGN);
409 if (sigsetjmp(_pop3_jmp, 1)) {
410 interrupts = 0;
411 safe_signal(SIGINT, saveint);
412 safe_signal(SIGPIPE, savepipe);
413 goto jbrk;
415 if (savepipe != SIG_IGN)
416 safe_signal(SIGPIPE, pop3catch);
417 rele_all_sigs();
418 if (pop3_noop1(&mb) != OKAY) {
419 safe_signal(SIGINT, saveint);
420 safe_signal(SIGPIPE, savepipe);
421 goto jleave;
423 safe_signal(SIGINT, saveint);
424 safe_signal(SIGPIPE, savepipe);
426 jbrk:
427 alarm(_pop3_keepalive);
428 jleave:
429 --_pop3_lock;
432 static enum okay
433 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
435 char const *cp;
436 enum okay rv;
437 NYD_ENTER;
439 POP3_OUT(rv, "STAT" NETNL, MB_COMD, goto jleave);
440 POP3_ANSWER(rv, goto jleave);
442 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
444 while (*cp != '\0' && spacechar(*cp))
445 ++cp;
447 rv = STOP;
448 if (*cp != '\0') {
449 size_t i;
451 if(n_idec_uiz_cp(&i, cp, 10, &cp) & n_IDEC_STATE_EMASK)
452 goto jerr;
453 if(i > INT_MAX)
454 goto jerr;
455 *cnt = (int)i;
457 while(*cp != '\0' && !spacechar(*cp))
458 ++cp;
459 while(*cp != '\0' && spacechar(*cp))
460 ++cp;
462 if(*cp == '\0')
463 goto jerr;
464 if(n_idec_uiz_cp(&i, cp, 10, NULL) & n_IDEC_STATE_EMASK)
465 goto jerr;
466 *size = (off_t)i;
467 rv = OKAY;
470 if (rv == STOP)
471 jerr:
472 n_err(_("Invalid POP3 STAT response: %s\n"), _pop3_buf);
473 jleave:
474 NYD_LEAVE;
475 return rv;
478 static enum okay
479 pop3_list(struct mailbox *mp, int n, size_t *size)
481 char o[LINESIZE], *cp;
482 enum okay rv;
483 NYD_ENTER;
485 snprintf(o, sizeof o, "LIST %u" NETNL, n);
486 POP3_OUT(rv, o, MB_COMD, goto jleave);
487 POP3_ANSWER(rv, goto jleave);
489 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
491 while (*cp != '\0' && spacechar(*cp))
492 ++cp;
493 while (*cp != '\0' && !spacechar(*cp))
494 ++cp;
495 while (*cp != '\0' && spacechar(*cp))
496 ++cp;
497 if (*cp != '\0')
498 n_idec_uiz_cp(size, cp, 10, NULL);
499 jleave:
500 NYD_LEAVE;
501 return rv;
504 static void
505 pop3_setptr(struct mailbox *mp, struct sockconn const *scp)
507 size_t i;
508 enum needspec ns;
509 NYD_ENTER;
511 message = n_calloc(msgCount + 1, sizeof *message);
512 message[msgCount].m_size = 0;
513 message[msgCount].m_lines = 0;
514 dot = message; /* (Just do it: avoid crash -- shall i now do ointr(0).. */
516 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
517 struct message *m = message + i;
518 m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
519 m->m_block = 0;
520 m->m_offset = 0;
521 m->m_size = m->m_xsize = 0;
524 for (i = 0; UICMP(z, i, <, msgCount); ++i)
525 if (!pop3_list(mp, i + 1, &message[i].m_xsize))
526 goto jleave;
528 /* Force the load of all messages right now */
529 ns = xok_blook(pop3_bulk_load, &scp->sc_url, OXM_ALL)
530 ? NEED_BODY : NEED_HEADER;
531 for (i = 0; UICMP(z, i, <, msgCount); ++i)
532 if (!pop3_get(mp, message + i, ns))
533 goto jleave;
535 srelax_hold();
536 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
537 struct message *m = message + i;
538 char const *cp;
540 if ((cp = hfield1("status", m)) != NULL)
541 while (*cp != '\0') {
542 if (*cp == 'R')
543 m->m_flag |= MREAD;
544 else if (*cp == 'O')
545 m->m_flag &= ~MNEW;
546 ++cp;
549 substdate(m);
550 srelax();
552 srelax_rele();
554 setdot(message);
555 jleave:
556 NYD_LEAVE;
559 static enum okay
560 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
562 char o[LINESIZE], *line, *lp;
563 sighandler_type volatile saveint, savepipe;
564 size_t linesize, linelen, size;
565 int number, lines;
566 int volatile emptyline;
567 off_t offset;
568 enum okay volatile rv;
569 NYD_ENTER;
571 line = NULL; /* TODO line pool */
572 saveint = savepipe = SIG_IGN;
573 linesize = 0;
574 number = (int)PTR2SIZE(m - message + 1);
575 emptyline = 0;
576 rv = STOP;
578 if (mp->mb_sock.s_fd < 0) {
579 n_err(_("POP3 connection already closed\n"));
580 ++_pop3_lock;
581 goto jleave;
584 if (_pop3_lock++ == 0) {
585 hold_all_sigs();
586 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
587 safe_signal(SIGINT, &_pop3_maincatch);
588 savepipe = safe_signal(SIGPIPE, SIG_IGN);
589 if (sigsetjmp(_pop3_jmp, 1))
590 goto jleave;
591 if (savepipe != SIG_IGN)
592 safe_signal(SIGPIPE, pop3catch);
593 rele_all_sigs();
596 fseek(mp->mb_otf, 0L, SEEK_END);
597 offset = ftell(mp->mb_otf);
598 jretry:
599 switch (need) {
600 case NEED_HEADER:
601 snprintf(o, sizeof o, "TOP %u 0" NETNL, number);
602 break;
603 case NEED_BODY:
604 snprintf(o, sizeof o, "RETR %u" NETNL, number);
605 break;
606 case NEED_UNSPEC:
607 abort(); /* XXX */
609 POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
611 if (pop3_answer(mp) == STOP) {
612 if (need == NEED_HEADER) {
613 /* The TOP POP3 command is optional, so retry with entire message */
614 need = NEED_BODY;
615 goto jretry;
617 goto jleave;
620 size = 0;
621 lines = 0;
622 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
623 if (line[0] == '.' && line[1] == NETNL[0] && line[2] == NETNL[1] &&
624 line[3] == '\0') {
625 mp->mb_active &= ~MB_MULT;
626 break;
628 if (line[0] == '.') {
629 lp = line + 1;
630 --linelen;
631 } else
632 lp = line;
633 /* TODO >>
634 * Need to mask 'From ' lines. This cannot be done properly
635 * since some servers pass them as 'From ' and others as
636 * '>From '. Although one could identify the first kind of
637 * server in principle, it is not possible to identify the
638 * second as '>From ' may also come from a server of the
639 * first type as actual data. So do what is absolutely
640 * necessary only - mask 'From '.
642 * If the line is the first line of the message header, it
643 * is likely a real 'From ' line. In this case, it is just
644 * ignored since it violates all standards.
645 * TODO i have *never* seen the latter?!?!?
646 * TODO <<
648 /* Since we simply copy over data without doing any transfer
649 * encoding reclassification/adjustment we *have* to perform
650 * RFC 4155 compliant From_ quoting here */
651 if (emptyline && is_head(lp, linelen, FAL0)) {
652 putc('>', mp->mb_otf);
653 ++size;
655 lines++;
656 if (lp[linelen-1] == NETNL[1] &&
657 (linelen == 1 || lp[linelen-2] == NETNL[0])) {
658 emptyline = linelen <= 2;
659 if (linelen > 2)
660 fwrite(lp, 1, linelen - 2, mp->mb_otf);
661 putc('\n', mp->mb_otf);
662 size += linelen - 1;
663 } else {
664 emptyline = 0;
665 fwrite(lp, 1, linelen, mp->mb_otf);
666 size += linelen;
669 if (!emptyline) {
670 /* TODO This is very ugly; but some POP3 daemons don't end a
671 * TODO message with NETNL NETNL, and we need \n\n for mbox format.
672 * TODO That is to say we do it wrong here in order to get it right
673 * TODO when send.c stuff or with MBOX handling, even though THIS
674 * TODO line is solely a property of the MBOX database format! */
675 putc('\n', mp->mb_otf);
676 ++lines;
677 ++size;
679 m->m_size = size;
680 m->m_lines = lines;
681 m->m_block = mailx_blockof(offset);
682 m->m_offset = mailx_offsetof(offset);
683 fflush(mp->mb_otf);
685 switch (need) {
686 case NEED_HEADER:
687 m->m_content_info |= CI_HAVE_HEADER;
688 break;
689 case NEED_BODY:
690 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
691 m->m_xlines = m->m_lines;
692 m->m_xsize = m->m_size;
693 break;
694 case NEED_UNSPEC:
695 break;
698 rv = OKAY;
699 jleave:
700 if (line != NULL)
701 n_free(line);
702 if (saveint != SIG_IGN)
703 safe_signal(SIGINT, saveint);
704 if (savepipe != SIG_IGN)
705 safe_signal(SIGPIPE, savepipe);
706 --_pop3_lock;
707 NYD_LEAVE;
708 if (interrupts)
709 n_raise(SIGINT);
710 return rv;
713 static enum okay
714 pop3_exit(struct mailbox *mp)
716 enum okay rv;
717 NYD_ENTER;
719 POP3_OUT(rv, "QUIT" NETNL, MB_COMD, goto jleave);
720 POP3_ANSWER(rv, goto jleave);
721 jleave:
722 NYD_LEAVE;
723 return rv;
726 static enum okay
727 pop3_delete(struct mailbox *mp, int n)
729 char o[LINESIZE];
730 enum okay rv;
731 NYD_ENTER;
733 snprintf(o, sizeof o, "DELE %u" NETNL, n);
734 POP3_OUT(rv, o, MB_COMD, goto jleave);
735 POP3_ANSWER(rv, goto jleave);
736 jleave:
737 NYD_LEAVE;
738 return rv;
741 static enum okay
742 pop3_update(struct mailbox *mp)
744 struct message *m;
745 int dodel, c, gotcha, held;
746 NYD_ENTER;
748 if (!(n_pstate & n_PS_EDIT)) {
749 holdbits();
750 c = 0;
751 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
752 if (m->m_flag & MBOX)
753 ++c;
754 if (c > 0)
755 makembox();
758 gotcha = held = 0;
759 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
760 if (n_pstate & n_PS_EDIT)
761 dodel = m->m_flag & MDELETED;
762 else
763 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
764 if (dodel) {
765 pop3_delete(mp, PTR2SIZE(m - message + 1));
766 ++gotcha;
767 } else
768 ++held;
771 /* C99 */{
772 char const *dnq;
774 dnq = n_shexp_quote_cp(displayname, FAL0);
776 if (gotcha && (n_pstate & n_PS_EDIT)) {
777 fprintf(n_stdout, _("%s "), dnq);
778 fprintf(n_stdout, (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
779 ? _("complete\n") : _("updated\n"));
780 } else if (held && !(n_pstate & n_PS_EDIT)) {
781 if (held == 1)
782 fprintf(n_stdout, _("Held 1 message in %s\n"), dnq);
783 else
784 fprintf(n_stdout, _("Held %d messages in %s\n"), held, dnq);
787 fflush(n_stdout);
788 NYD_LEAVE;
789 return OKAY;
792 FL enum okay
793 pop3_noop(void)
795 sighandler_type volatile saveint, savepipe;
796 enum okay volatile rv = STOP;
797 NYD_ENTER;
799 _pop3_lock = 1;
800 hold_all_sigs();
801 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
802 safe_signal(SIGINT, &_pop3_maincatch);
803 savepipe = safe_signal(SIGPIPE, SIG_IGN);
804 if (sigsetjmp(_pop3_jmp, 1) == 0) {
805 if (savepipe != SIG_IGN)
806 safe_signal(SIGPIPE, pop3catch);
807 rele_all_sigs();
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, enum fedit_mode fm)
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 (fm & FEDIT_NEWMAIL)
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_flags & n_URL_HAD_USER) ||
835 sc.sc_url.url_pass.s != NULL)) {
836 n_err(_("New-style URL used without *v15-compat* being set\n"));
837 goto jleave;
840 if (!(ok_blook(v15_compat) ? ccred_lookup(&sc.sc_cred, &sc.sc_url)
841 : ccred_lookup_old(&sc.sc_cred, CPROTO_POP3,
842 ((sc.sc_url.url_flags & n_URL_HAD_USER) ? sc.sc_url.url_eu_h_p.s
843 : sc.sc_url.url_u_h_p.s))))
844 goto jleave;
846 if (!quit(FAL0))
847 goto jleave;
849 if (!sopen(&sc.sc_sock, &sc.sc_url))
850 goto jleave;
852 rv = 1;
854 if (fm & FEDIT_SYSBOX)
855 n_pstate &= ~n_PS_EDIT;
856 else
857 n_pstate |= n_PS_EDIT;
858 if (mb.mb_sock.s_fd >= 0)
859 sclose(&mb.mb_sock);
860 if (mb.mb_itf) {
861 fclose(mb.mb_itf);
862 mb.mb_itf = NULL;
864 if (mb.mb_otf) {
865 fclose(mb.mb_otf);
866 mb.mb_otf = NULL;
869 initbox(sc.sc_url.url_p_u_h_p);
870 mb.mb_type = MB_VOID;
871 _pop3_lock = 1;
872 mb.mb_sock = sc.sc_sock;
874 saveint = safe_signal(SIGINT, SIG_IGN);
875 savepipe = safe_signal(SIGPIPE, SIG_IGN);
876 if (sigsetjmp(_pop3_jmp, 1)) {
877 sclose(&mb.mb_sock);
878 n_err(_("POP3 connection closed\n"));
879 safe_signal(SIGINT, saveint);
880 safe_signal(SIGPIPE, savepipe);
881 _pop3_lock = 0;
882 rv = -1;
883 if (interrupts > 0)
884 n_raise(SIGINT);
885 goto jleave;
887 if (saveint != SIG_IGN)
888 safe_signal(SIGINT, pop3catch);
889 if (savepipe != SIG_IGN)
890 safe_signal(SIGPIPE, pop3catch);
892 if ((cp = xok_vlook(pop3_keepalive, &sc.sc_url, OXM_ALL)) != NULL) {
893 n_idec_si32_cp(&_pop3_keepalive, cp, 10, NULL);
894 if (_pop3_keepalive > 0) {
895 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
896 alarm(_pop3_keepalive);
900 mb.mb_sock.s_desc = (sc.sc_url.url_flags & n_URL_TLS_REQUIRED)
901 ? "POP3S" : "POP3";
902 mb.mb_sock.s_onclose = pop3_timer_off;
903 if (_pop3_login(&mb, &sc) != OKAY ||
904 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
905 sclose(&mb.mb_sock);
906 pop3_timer_off();
907 safe_signal(SIGINT, saveint);
908 safe_signal(SIGPIPE, savepipe);
909 _pop3_lock = 0;
910 goto jleave;
913 setmsize(msgCount);
914 mb.mb_type = MB_POP3;
915 mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY))
916 ? 0 : MB_DELE;
917 pop3_setptr(&mb, &sc);
919 /*if (!(fm & FEDIT_NEWMAIL)) */{
920 n_pstate &= ~n_PS_SAW_COMMAND;
921 n_pstate |= n_PS_SETFILE_OPENED;
924 safe_signal(SIGINT, saveint);
925 safe_signal(SIGPIPE, savepipe);
926 _pop3_lock = 0;
928 if ((n_poption & (n_PO_EXISTONLY | n_PO_HEADERLIST)) == n_PO_EXISTONLY) {
929 rv = (msgCount == 0);
930 goto jleave;
933 if (!(n_pstate & n_PS_EDIT) && msgCount == 0) {
934 if (!ok_blook(emptystart))
935 n_err(_("No mail at %s\n"), server);
936 goto jleave;
939 rv = 0;
940 jleave:
941 NYD_LEAVE;
942 return rv;
945 FL enum okay
946 pop3_header(struct message *m)
948 enum okay rv;
949 NYD_ENTER;
951 /* TODO no URL here, no OXM possible; (however it is used in setfile()..) */
952 rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
953 NYD_LEAVE;
954 return rv;
957 FL enum okay
958 pop3_body(struct message *m)
960 enum okay rv;
961 NYD_ENTER;
963 rv = pop3_get(&mb, m, NEED_BODY);
964 NYD_LEAVE;
965 return rv;
968 FL bool_t
969 pop3_quit(bool_t hold_sigs_on)
971 sighandler_type volatile saveint, savepipe;
972 bool_t rv;
973 NYD_ENTER;
975 if(hold_sigs_on)
976 rele_sigs();
978 rv = FAL0;
980 if (mb.mb_sock.s_fd < 0) {
981 n_err(_("POP3 connection already closed\n"));
982 rv = TRU1;
983 goto jleave;
986 _pop3_lock = 1;
987 saveint = safe_signal(SIGINT, SIG_IGN);
988 savepipe = safe_signal(SIGPIPE, SIG_IGN);
989 if (sigsetjmp(_pop3_jmp, 1)) {
990 safe_signal(SIGINT, saveint);
991 safe_signal(SIGPIPE, savepipe);
992 _pop3_lock = 0;
993 interrupts = 0;
994 goto jleave;
996 if (saveint != SIG_IGN)
997 safe_signal(SIGINT, pop3catch);
998 if (savepipe != SIG_IGN)
999 safe_signal(SIGPIPE, pop3catch);
1000 pop3_update(&mb);
1001 pop3_exit(&mb);
1002 sclose(&mb.mb_sock);
1003 safe_signal(SIGINT, saveint);
1004 safe_signal(SIGPIPE, savepipe);
1005 _pop3_lock = 0;
1007 rv = TRU1;
1008 jleave:
1009 if(hold_sigs_on)
1010 hold_sigs();
1011 NYD_LEAVE;
1012 return rv;
1014 #endif /* HAVE_POP3 */
1016 /* s-it-mode */