TODO: How High The Moon
[s-mailx.git] / pop3.c
blobf76811a8301ec070b6c85e6fffb2583575081381
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 * SPDX-License-Identifier: BSD-4-Clause
8 */
9 /*
10 * Copyright (c) 2002
11 * Gunnar Ritter. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by Gunnar Ritter
24 * and his contributors.
25 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
41 #undef n_FILE
42 #define n_FILE pop3
44 #ifndef HAVE_AMALGAMATION
45 # include "nail.h"
46 #endif
48 EMPTY_FILE()
49 #ifdef HAVE_POP3
51 #define POP3_ANSWER(RV,ACTIONSTOP) \
52 do if (((RV) = pop3_answer(mp)) == STOP) {\
53 ACTIONSTOP;\
54 } while (0)
56 #define POP3_OUT(RV,X,Y,ACTIONSTOP) \
57 do {\
58 if (((RV) = pop3_finish(mp)) == STOP) {\
59 ACTIONSTOP;\
61 if (n_poption & n_PO_VERBVERB)\
62 n_err(">>> %s", X);\
63 mp->mb_active |= Y;\
64 if (((RV) = swrite(&mp->mb_sock, X)) == STOP) {\
65 ACTIONSTOP;\
67 } while (0)
69 static char *_pop3_buf;
70 static size_t _pop3_bufsize;
71 static sigjmp_buf _pop3_jmp;
72 static sighandler_type _pop3_savealrm;
73 static si32_t _pop3_keepalive;
74 static int volatile _pop3_lock;
76 /* Perform entire login handshake */
77 static enum okay _pop3_login(struct mailbox *mp, struct sockconn *scp);
79 /* APOP: get greeting credential or NULL */
80 #ifdef HAVE_MD5
81 static char * _pop3_lookup_apop_timestamp(char const *bp);
82 #endif
84 /* Several authentication methods */
85 #ifdef HAVE_MD5
86 static enum okay _pop3_auth_apop(struct mailbox *mp,
87 struct sockconn const *scp, char const *ts);
88 #endif
89 static enum okay _pop3_auth_plain(struct mailbox *mp,
90 struct sockconn const *scp);
92 static void pop3_timer_off(void);
93 static enum okay pop3_answer(struct mailbox *mp);
94 static enum okay pop3_finish(struct mailbox *mp);
95 static void pop3catch(int s);
96 static void _pop3_maincatch(int s);
97 static enum okay pop3_noop1(struct mailbox *mp);
98 static void pop3alarm(int s);
99 static enum okay pop3_stat(struct mailbox *mp, off_t *size, int *cnt);
100 static enum okay pop3_list(struct mailbox *mp, int n, size_t *size);
101 static void pop3_setptr(struct mailbox *mp,
102 struct sockconn const *scp);
103 static enum okay pop3_get(struct mailbox *mp, struct message *m,
104 enum needspec need);
105 static enum okay pop3_exit(struct mailbox *mp);
106 static enum okay pop3_delete(struct mailbox *mp, int n);
107 static enum okay pop3_update(struct mailbox *mp);
109 static enum okay
110 _pop3_login(struct mailbox *mp, struct sockconn *scp)
112 #ifdef HAVE_MD5
113 char *ts;
114 #endif
115 enum okey_xlook_mode oxm;
116 enum okay rv;
117 NYD_ENTER;
119 oxm = ok_blook(v15_compat) ? OXM_ALL : OXM_PLAIN | OXM_U_H_P;
121 /* Get the greeting, check whether APOP is advertised */
122 POP3_ANSWER(rv, goto jleave);
123 #ifdef HAVE_MD5
124 ts = _pop3_lookup_apop_timestamp(_pop3_buf);
125 #endif
127 /* If not yet secured, can we upgrade to TLS? */
128 #ifdef HAVE_TLS
129 if (!(scp->sc_url.url_flags & n_URL_TLS_REQUIRED) &&
130 xok_blook(pop3_use_starttls, &scp->sc_url, oxm)) {
131 POP3_OUT(rv, "STLS" NETNL, MB_COMD, goto jleave);
132 POP3_ANSWER(rv, goto jleave);
133 if(!n_tls_open(&scp->sc_url, &scp->sc_sock)){
134 rv = STOP;
135 goto jleave;
138 #else
139 if (xok_blook(pop3_use_starttls, &scp->sc_url, oxm)) {
140 n_err(_("No TLS support compiled in\n"));
141 rv = STOP;
142 goto jleave;
144 #endif
146 /* Use the APOP single roundtrip? */
147 #ifdef HAVE_MD5
148 if (ts != NULL && !xok_blook(pop3_no_apop, &scp->sc_url, oxm)) {
149 if ((rv = _pop3_auth_apop(mp, scp, ts)) != OKAY) {
150 char const *ccp;
152 # ifdef HAVE_TLS
153 if (scp->sc_sock.s_use_tls)
154 ccp = _("over an encrypted connection");
155 else
156 # endif
157 ccp = _("(unsafe clear text!)");
158 n_err(_("POP3 APOP authentication failed!\n"
159 " Server indicated support.. Set *pop3-no-apop*\n"
160 " for plain text authentication %s\n"), ccp);
162 goto jleave;
164 #endif
166 rv = _pop3_auth_plain(mp, scp);
167 jleave:
168 NYD_LEAVE;
169 return rv;
172 #ifdef HAVE_MD5
173 static char *
174 _pop3_lookup_apop_timestamp(char const *bp)
176 /* RFC 1939:
177 * A POP3 server which implements the APOP command will include
178 * a timestamp in its banner greeting. The syntax of the timestamp
179 * corresponds to the "msg-id" in [RFC822]
180 * RFC 822:
181 * msg-id = "<" addr-spec ">"
182 * addr-spec = local-part "@" domain */
183 char const *cp, *ep;
184 size_t tl;
185 char *rp = NULL;
186 bool_t hadat = FAL0;
187 NYD_ENTER;
189 if ((cp = strchr(bp, '<')) == NULL)
190 goto jleave;
192 /* xxx What about malformed APOP timestamp (<@>) here? */
193 for (ep = cp; *ep != '\0'; ++ep) {
194 if (spacechar(*ep))
195 goto jleave;
196 else if (*ep == '@')
197 hadat = TRU1;
198 else if (*ep == '>') {
199 if (!hadat)
200 goto jleave;
201 break;
204 if (*ep != '>')
205 goto jleave;
207 tl = PTR2SIZE(++ep - cp);
208 rp = n_autorec_alloc(tl +1);
209 memcpy(rp, cp, tl);
210 rp[tl] = '\0';
211 jleave:
212 NYD_LEAVE;
213 return rp;
215 #endif
217 #ifdef HAVE_MD5
218 static enum okay
219 _pop3_auth_apop(struct mailbox *mp, struct sockconn const *scp, char const *ts)
221 unsigned char digest[16];
222 char hex[MD5TOHEX_SIZE], *cp;
223 md5_ctx ctx;
224 size_t i;
225 enum okay rv = STOP;
226 NYD_ENTER;
228 md5_init(&ctx);
229 md5_update(&ctx, (uc_i*)n_UNCONST(ts), strlen(ts));
230 md5_update(&ctx, (uc_i*)scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
231 md5_final(digest, &ctx);
232 md5tohex(hex, digest);
234 i = scp->sc_cred.cc_user.l;
235 cp = n_lofi_alloc(5 + i + 1 + MD5TOHEX_SIZE + sizeof(NETNL)-1 +1);
237 memcpy(cp, "APOP ", 5);
238 memcpy(cp + 5, scp->sc_cred.cc_user.s, i);
239 i += 5;
240 cp[i++] = ' ';
241 memcpy(cp + i, hex, MD5TOHEX_SIZE);
242 i += MD5TOHEX_SIZE;
243 memcpy(cp + i, NETNL, sizeof(NETNL));
244 POP3_OUT(rv, cp, MB_COMD, goto jleave);
245 POP3_ANSWER(rv, goto jleave);
247 rv = OKAY;
248 jleave:
249 n_lofi_free(cp);
250 NYD_LEAVE;
251 return rv;
253 #endif /* HAVE_MD5 */
255 static enum okay
256 _pop3_auth_plain(struct mailbox *mp, struct sockconn const *scp)
258 char *cp;
259 enum okay rv = STOP;
260 NYD_ENTER;
262 /* The USER/PASS plain text version */
263 cp = n_lofi_alloc(n_MAX(scp->sc_cred.cc_user.l, scp->sc_cred.cc_pass.l) +
264 5 + sizeof(NETNL)-1 +1);
266 memcpy(cp, "USER ", 5);
267 memcpy(cp + 5, scp->sc_cred.cc_user.s, scp->sc_cred.cc_user.l);
268 memcpy(cp + 5 + scp->sc_cred.cc_user.l, NETNL, sizeof(NETNL));
269 POP3_OUT(rv, cp, MB_COMD, goto jleave);
270 POP3_ANSWER(rv, goto jleave);
272 memcpy(cp, "PASS ", 5);
273 memcpy(cp + 5, scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
274 memcpy(cp + 5 + scp->sc_cred.cc_pass.l, NETNL, sizeof(NETNL));
275 POP3_OUT(rv, cp, MB_COMD, goto jleave);
276 POP3_ANSWER(rv, goto jleave);
278 rv = OKAY;
279 jleave:
280 n_lofi_free(cp);
281 NYD_LEAVE;
282 return rv;
285 static void
286 pop3_timer_off(void)
288 NYD_ENTER;
289 if (_pop3_keepalive > 0) {
290 alarm(0);
291 safe_signal(SIGALRM, _pop3_savealrm);
293 NYD_LEAVE;
296 static enum okay
297 pop3_answer(struct mailbox *mp)
299 int sz;
300 size_t blen;
301 enum okay rv = STOP;
302 NYD_ENTER;
304 jretry:
305 if ((sz = sgetline(&_pop3_buf, &_pop3_bufsize, &blen, &mp->mb_sock)) > 0) {
306 if ((mp->mb_active & (MB_COMD | MB_MULT)) == MB_MULT)
307 goto jmultiline;
308 if (n_poption & n_PO_VERBVERB)
309 n_err(_pop3_buf);
310 switch (*_pop3_buf) {
311 case '+':
312 rv = OKAY;
313 mp->mb_active &= ~MB_COMD;
314 break;
315 case '-':
316 rv = STOP;
317 mp->mb_active = MB_NONE;
318 while (blen > 0 &&
319 (_pop3_buf[blen - 1] == NETNL[0] ||
320 _pop3_buf[blen - 1] == NETNL[1]))
321 _pop3_buf[--blen] = '\0';
322 n_err(_("POP3 error: %s\n"), _pop3_buf);
323 break;
324 default:
325 /* If the answer starts neither with '+' nor with '-', it must be part
326 * of a multiline response. Get lines until a single dot appears */
327 jmultiline:
328 while (_pop3_buf[0] != '.' || _pop3_buf[1] != NETNL[0] ||
329 _pop3_buf[2] != NETNL[1] || _pop3_buf[3] != '\0') {
330 sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock);
331 if (sz <= 0)
332 goto jeof;
334 mp->mb_active &= ~MB_MULT;
335 if (mp->mb_active != MB_NONE)
336 goto jretry;
338 } else {
339 jeof:
340 rv = STOP;
341 mp->mb_active = MB_NONE;
343 NYD_LEAVE;
344 return rv;
347 static enum okay
348 pop3_finish(struct mailbox *mp)
350 NYD_ENTER;
351 while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
352 pop3_answer(mp);
353 NYD_LEAVE;
354 return OKAY;
357 static void
358 pop3catch(int s)
360 NYD_X; /* Signal handler */
361 switch (s) {
362 case SIGINT:
363 /*n_err_sighdl(_("Interrupt during POP3 operation\n"));*/
364 interrupts = 2; /* Force "Interrupt" message shall we onintr(0) */
365 siglongjmp(_pop3_jmp, 1);
366 case SIGPIPE:
367 n_err_sighdl(_("Received SIGPIPE during POP3 operation\n"));
368 break;
372 static void
373 _pop3_maincatch(int s)
375 NYD_X; /* Signal handler */
376 n_UNUSED(s);
377 if (interrupts == 0)
378 n_err_sighdl(_("\n(Interrupt -- one more to abort operation)\n"));
379 else {
380 interrupts = 1;
381 siglongjmp(_pop3_jmp, 1);
385 static enum okay
386 pop3_noop1(struct mailbox *mp)
388 enum okay rv;
389 NYD_ENTER;
391 POP3_OUT(rv, "NOOP" NETNL, MB_COMD, goto jleave);
392 POP3_ANSWER(rv, goto jleave);
393 jleave:
394 NYD_LEAVE;
395 return rv;
398 static void
399 pop3alarm(int s)
401 sighandler_type volatile saveint, savepipe;
402 NYD_X; /* Signal handler */
403 n_UNUSED(s);
405 if (_pop3_lock++ == 0) {
406 hold_all_sigs();
407 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
408 safe_signal(SIGINT, &_pop3_maincatch);
409 savepipe = safe_signal(SIGPIPE, SIG_IGN);
410 if (sigsetjmp(_pop3_jmp, 1)) {
411 interrupts = 0;
412 safe_signal(SIGINT, saveint);
413 safe_signal(SIGPIPE, savepipe);
414 goto jbrk;
416 if (savepipe != SIG_IGN)
417 safe_signal(SIGPIPE, pop3catch);
418 rele_all_sigs();
419 if (pop3_noop1(&mb) != OKAY) {
420 safe_signal(SIGINT, saveint);
421 safe_signal(SIGPIPE, savepipe);
422 goto jleave;
424 safe_signal(SIGINT, saveint);
425 safe_signal(SIGPIPE, savepipe);
427 jbrk:
428 alarm(_pop3_keepalive);
429 jleave:
430 --_pop3_lock;
433 static enum okay
434 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
436 char const *cp;
437 enum okay rv;
438 NYD_ENTER;
440 POP3_OUT(rv, "STAT" NETNL, MB_COMD, goto jleave);
441 POP3_ANSWER(rv, goto jleave);
443 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
445 while (*cp != '\0' && spacechar(*cp))
446 ++cp;
448 rv = STOP;
449 if (*cp != '\0') {
450 size_t i;
452 if(n_idec_uiz_cp(&i, cp, 10, &cp) & n_IDEC_STATE_EMASK)
453 goto jerr;
454 if(i > INT_MAX)
455 goto jerr;
456 *cnt = (int)i;
458 while(*cp != '\0' && !spacechar(*cp))
459 ++cp;
460 while(*cp != '\0' && spacechar(*cp))
461 ++cp;
463 if(*cp == '\0')
464 goto jerr;
465 if(n_idec_uiz_cp(&i, cp, 10, NULL) & n_IDEC_STATE_EMASK)
466 goto jerr;
467 *size = (off_t)i;
468 rv = OKAY;
471 if (rv == STOP)
472 jerr:
473 n_err(_("Invalid POP3 STAT response: %s\n"), _pop3_buf);
474 jleave:
475 NYD_LEAVE;
476 return rv;
479 static enum okay
480 pop3_list(struct mailbox *mp, int n, size_t *size)
482 char o[LINESIZE], *cp;
483 enum okay rv;
484 NYD_ENTER;
486 snprintf(o, sizeof o, "LIST %u" NETNL, n);
487 POP3_OUT(rv, o, MB_COMD, goto jleave);
488 POP3_ANSWER(rv, goto jleave);
490 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
492 while (*cp != '\0' && spacechar(*cp))
493 ++cp;
494 while (*cp != '\0' && !spacechar(*cp))
495 ++cp;
496 while (*cp != '\0' && spacechar(*cp))
497 ++cp;
498 if (*cp != '\0')
499 n_idec_uiz_cp(size, cp, 10, NULL);
500 jleave:
501 NYD_LEAVE;
502 return rv;
505 static void
506 pop3_setptr(struct mailbox *mp, struct sockconn const *scp)
508 size_t i;
509 enum needspec ns;
510 NYD_ENTER;
512 message = n_calloc(msgCount + 1, sizeof *message);
513 message[msgCount].m_size = 0;
514 message[msgCount].m_lines = 0;
515 dot = message; /* (Just do it: avoid crash -- shall i now do ointr(0).. */
517 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
518 struct message *m = message + i;
519 m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
520 m->m_block = 0;
521 m->m_offset = 0;
522 m->m_size = m->m_xsize = 0;
525 for (i = 0; UICMP(z, i, <, msgCount); ++i)
526 if (!pop3_list(mp, i + 1, &message[i].m_xsize))
527 goto jleave;
529 /* Force the load of all messages right now */
530 ns = xok_blook(pop3_bulk_load, &scp->sc_url, OXM_ALL)
531 ? NEED_BODY : NEED_HEADER;
532 for (i = 0; UICMP(z, i, <, msgCount); ++i)
533 if (!pop3_get(mp, message + i, ns))
534 goto jleave;
536 srelax_hold();
537 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
538 struct message *m = message + i;
539 char const *cp;
541 if ((cp = hfield1("status", m)) != NULL)
542 while (*cp != '\0') {
543 if (*cp == 'R')
544 m->m_flag |= MREAD;
545 else if (*cp == 'O')
546 m->m_flag &= ~MNEW;
547 ++cp;
550 substdate(m);
551 srelax();
553 srelax_rele();
555 setdot(message);
556 jleave:
557 NYD_LEAVE;
560 static enum okay
561 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
563 char o[LINESIZE], *line, *lp;
564 sighandler_type volatile saveint, savepipe;
565 size_t linesize, linelen, size;
566 int number, lines;
567 int volatile emptyline;
568 off_t offset;
569 enum okay volatile rv;
570 NYD_ENTER;
572 line = NULL; /* TODO line pool */
573 saveint = savepipe = SIG_IGN;
574 linesize = 0;
575 number = (int)PTR2SIZE(m - message + 1);
576 emptyline = 0;
577 rv = STOP;
579 if (mp->mb_sock.s_fd < 0) {
580 n_err(_("POP3 connection already closed\n"));
581 ++_pop3_lock;
582 goto jleave;
585 if (_pop3_lock++ == 0) {
586 hold_all_sigs();
587 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
588 safe_signal(SIGINT, &_pop3_maincatch);
589 savepipe = safe_signal(SIGPIPE, SIG_IGN);
590 if (sigsetjmp(_pop3_jmp, 1))
591 goto jleave;
592 if (savepipe != SIG_IGN)
593 safe_signal(SIGPIPE, pop3catch);
594 rele_all_sigs();
597 fseek(mp->mb_otf, 0L, SEEK_END);
598 offset = ftell(mp->mb_otf);
599 jretry:
600 switch (need) {
601 case NEED_HEADER:
602 snprintf(o, sizeof o, "TOP %u 0" NETNL, number);
603 break;
604 case NEED_BODY:
605 snprintf(o, sizeof o, "RETR %u" NETNL, number);
606 break;
607 case NEED_UNSPEC:
608 abort(); /* XXX */
610 POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
612 if (pop3_answer(mp) == STOP) {
613 if (need == NEED_HEADER) {
614 /* The TOP POP3 command is optional, so retry with entire message */
615 need = NEED_BODY;
616 goto jretry;
618 goto jleave;
621 size = 0;
622 lines = 0;
623 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
624 if (line[0] == '.' && line[1] == NETNL[0] && line[2] == NETNL[1] &&
625 line[3] == '\0') {
626 mp->mb_active &= ~MB_MULT;
627 break;
629 if (line[0] == '.') {
630 lp = line + 1;
631 --linelen;
632 } else
633 lp = line;
634 /* TODO >>
635 * Need to mask 'From ' lines. This cannot be done properly
636 * since some servers pass them as 'From ' and others as
637 * '>From '. Although one could identify the first kind of
638 * server in principle, it is not possible to identify the
639 * second as '>From ' may also come from a server of the
640 * first type as actual data. So do what is absolutely
641 * necessary only - mask 'From '.
643 * If the line is the first line of the message header, it
644 * is likely a real 'From ' line. In this case, it is just
645 * ignored since it violates all standards.
646 * TODO i have *never* seen the latter?!?!?
647 * TODO <<
649 /* Since we simply copy over data without doing any transfer
650 * encoding reclassification/adjustment we *have* to perform
651 * RFC 4155 compliant From_ quoting here */
652 if (emptyline && is_head(lp, linelen, FAL0)) {
653 putc('>', mp->mb_otf);
654 ++size;
656 lines++;
657 if (lp[linelen-1] == NETNL[1] &&
658 (linelen == 1 || lp[linelen-2] == NETNL[0])) {
659 emptyline = linelen <= 2;
660 if (linelen > 2)
661 fwrite(lp, 1, linelen - 2, mp->mb_otf);
662 putc('\n', mp->mb_otf);
663 size += linelen - 1;
664 } else {
665 emptyline = 0;
666 fwrite(lp, 1, linelen, mp->mb_otf);
667 size += linelen;
670 if (!emptyline) {
671 /* TODO This is very ugly; but some POP3 daemons don't end a
672 * TODO message with NETNL NETNL, and we need \n\n for mbox format.
673 * TODO That is to say we do it wrong here in order to get it right
674 * TODO when send.c stuff or with MBOX handling, even though THIS
675 * TODO line is solely a property of the MBOX database format! */
676 putc('\n', mp->mb_otf);
677 ++lines;
678 ++size;
680 m->m_size = size;
681 m->m_lines = lines;
682 m->m_block = mailx_blockof(offset);
683 m->m_offset = mailx_offsetof(offset);
684 fflush(mp->mb_otf);
686 switch (need) {
687 case NEED_HEADER:
688 m->m_content_info |= CI_HAVE_HEADER;
689 break;
690 case NEED_BODY:
691 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
692 m->m_xlines = m->m_lines;
693 m->m_xsize = m->m_size;
694 break;
695 case NEED_UNSPEC:
696 break;
699 rv = OKAY;
700 jleave:
701 if (line != NULL)
702 n_free(line);
703 if (saveint != SIG_IGN)
704 safe_signal(SIGINT, saveint);
705 if (savepipe != SIG_IGN)
706 safe_signal(SIGPIPE, savepipe);
707 --_pop3_lock;
708 NYD_LEAVE;
709 if (interrupts)
710 n_raise(SIGINT);
711 return rv;
714 static enum okay
715 pop3_exit(struct mailbox *mp)
717 enum okay rv;
718 NYD_ENTER;
720 POP3_OUT(rv, "QUIT" NETNL, MB_COMD, goto jleave);
721 POP3_ANSWER(rv, goto jleave);
722 jleave:
723 NYD_LEAVE;
724 return rv;
727 static enum okay
728 pop3_delete(struct mailbox *mp, int n)
730 char o[LINESIZE];
731 enum okay rv;
732 NYD_ENTER;
734 snprintf(o, sizeof o, "DELE %u" NETNL, n);
735 POP3_OUT(rv, o, MB_COMD, goto jleave);
736 POP3_ANSWER(rv, goto jleave);
737 jleave:
738 NYD_LEAVE;
739 return rv;
742 static enum okay
743 pop3_update(struct mailbox *mp)
745 struct message *m;
746 int dodel, c, gotcha, held;
747 NYD_ENTER;
749 if (!(n_pstate & n_PS_EDIT)) {
750 holdbits();
751 c = 0;
752 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
753 if (m->m_flag & MBOX)
754 ++c;
755 if (c > 0)
756 makembox();
759 gotcha = held = 0;
760 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
761 if (n_pstate & n_PS_EDIT)
762 dodel = m->m_flag & MDELETED;
763 else
764 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
765 if (dodel) {
766 pop3_delete(mp, PTR2SIZE(m - message + 1));
767 ++gotcha;
768 } else
769 ++held;
772 /* C99 */{
773 char const *dnq;
775 dnq = n_shexp_quote_cp(displayname, FAL0);
777 if (gotcha && (n_pstate & n_PS_EDIT)) {
778 fprintf(n_stdout, _("%s "), dnq);
779 fprintf(n_stdout, (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
780 ? _("complete\n") : _("updated\n"));
781 } else if (held && !(n_pstate & n_PS_EDIT)) {
782 if (held == 1)
783 fprintf(n_stdout, _("Held 1 message in %s\n"), dnq);
784 else
785 fprintf(n_stdout, _("Held %d messages in %s\n"), held, dnq);
788 fflush(n_stdout);
789 NYD_LEAVE;
790 return OKAY;
793 FL enum okay
794 pop3_noop(void)
796 sighandler_type volatile saveint, savepipe;
797 enum okay volatile rv = STOP;
798 NYD_ENTER;
800 _pop3_lock = 1;
801 hold_all_sigs();
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 rele_all_sigs();
809 rv = pop3_noop1(&mb);
811 safe_signal(SIGINT, saveint);
812 safe_signal(SIGPIPE, savepipe);
813 _pop3_lock = 0;
814 NYD_LEAVE;
815 return rv;
818 FL int
819 pop3_setfile(char const *who, char const *server, enum fedit_mode fm)
821 struct sockconn sc;
822 sighandler_type saveint, savepipe;
823 char const *cp;
824 int volatile rv;
825 NYD_ENTER;
827 rv = 1;
828 if (fm & FEDIT_NEWMAIL)
829 goto jleave;
830 rv = -1;
832 if (!url_parse(&sc.sc_url, CPROTO_POP3, server))
833 goto jleave;
834 if (!ok_blook(v15_compat) &&
835 (!(sc.sc_url.url_flags & n_URL_HAD_USER) ||
836 sc.sc_url.url_pass.s != NULL)) {
837 n_err(_("New-style URL used without *v15-compat* being set\n"));
838 goto jleave;
841 if (!(ok_blook(v15_compat) ? ccred_lookup(&sc.sc_cred, &sc.sc_url)
842 : ccred_lookup_old(&sc.sc_cred, CPROTO_POP3,
843 ((sc.sc_url.url_flags & n_URL_HAD_USER) ? sc.sc_url.url_eu_h_p.s
844 : sc.sc_url.url_u_h_p.s))))
845 goto jleave;
847 if (!quit(FAL0))
848 goto jleave;
850 if (!sopen(&sc.sc_sock, &sc.sc_url))
851 goto jleave;
853 rv = 1;
855 if (fm & FEDIT_SYSBOX)
856 n_pstate &= ~n_PS_EDIT;
857 else
858 n_pstate |= n_PS_EDIT;
859 if (mb.mb_sock.s_fd >= 0)
860 sclose(&mb.mb_sock);
861 if (mb.mb_itf) {
862 fclose(mb.mb_itf);
863 mb.mb_itf = NULL;
865 if (mb.mb_otf) {
866 fclose(mb.mb_otf);
867 mb.mb_otf = NULL;
870 initbox(sc.sc_url.url_p_u_h_p);
871 mb.mb_type = MB_VOID;
872 _pop3_lock = 1;
873 mb.mb_sock = sc.sc_sock;
875 saveint = safe_signal(SIGINT, SIG_IGN);
876 savepipe = safe_signal(SIGPIPE, SIG_IGN);
877 if (sigsetjmp(_pop3_jmp, 1)) {
878 sclose(&mb.mb_sock);
879 n_err(_("POP3 connection closed\n"));
880 safe_signal(SIGINT, saveint);
881 safe_signal(SIGPIPE, savepipe);
882 _pop3_lock = 0;
883 rv = -1;
884 if (interrupts > 0)
885 n_raise(SIGINT);
886 goto jleave;
888 if (saveint != SIG_IGN)
889 safe_signal(SIGINT, pop3catch);
890 if (savepipe != SIG_IGN)
891 safe_signal(SIGPIPE, pop3catch);
893 if ((cp = xok_vlook(pop3_keepalive, &sc.sc_url, OXM_ALL)) != NULL) {
894 n_idec_si32_cp(&_pop3_keepalive, cp, 10, NULL);
895 if (_pop3_keepalive > 0) {
896 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
897 alarm(_pop3_keepalive);
901 mb.mb_sock.s_desc = (sc.sc_url.url_flags & n_URL_TLS_REQUIRED)
902 ? "POP3S" : "POP3";
903 mb.mb_sock.s_onclose = pop3_timer_off;
904 if (_pop3_login(&mb, &sc) != OKAY ||
905 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
906 sclose(&mb.mb_sock);
907 pop3_timer_off();
908 safe_signal(SIGINT, saveint);
909 safe_signal(SIGPIPE, savepipe);
910 _pop3_lock = 0;
911 goto jleave;
914 setmsize(msgCount);
915 mb.mb_type = MB_POP3;
916 mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY))
917 ? 0 : MB_DELE;
918 pop3_setptr(&mb, &sc);
920 /*if (!(fm & FEDIT_NEWMAIL)) */{
921 n_pstate &= ~n_PS_SAW_COMMAND;
922 n_pstate |= n_PS_SETFILE_OPENED;
925 safe_signal(SIGINT, saveint);
926 safe_signal(SIGPIPE, savepipe);
927 _pop3_lock = 0;
929 if ((n_poption & (n_PO_EXISTONLY | n_PO_HEADERLIST)) == n_PO_EXISTONLY) {
930 rv = (msgCount == 0);
931 goto jleave;
934 if (!(n_pstate & n_PS_EDIT) && msgCount == 0) {
935 if (!ok_blook(emptystart))
936 n_err(_("No mail for %s at %s\n"), who, sc.sc_url.url_p_eu_h_p);
937 goto jleave;
940 rv = 0;
941 jleave:
942 NYD_LEAVE;
943 return rv;
946 FL enum okay
947 pop3_header(struct message *m)
949 enum okay rv;
950 NYD_ENTER;
952 /* TODO no URL here, no OXM possible; (however it is used in setfile()..) */
953 rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
954 NYD_LEAVE;
955 return rv;
958 FL enum okay
959 pop3_body(struct message *m)
961 enum okay rv;
962 NYD_ENTER;
964 rv = pop3_get(&mb, m, NEED_BODY);
965 NYD_LEAVE;
966 return rv;
969 FL bool_t
970 pop3_quit(bool_t hold_sigs_on)
972 sighandler_type volatile saveint, savepipe;
973 bool_t rv;
974 NYD_ENTER;
976 if(hold_sigs_on)
977 rele_sigs();
979 rv = FAL0;
981 if (mb.mb_sock.s_fd < 0) {
982 n_err(_("POP3 connection already closed\n"));
983 rv = TRU1;
984 goto jleave;
987 _pop3_lock = 1;
988 saveint = safe_signal(SIGINT, SIG_IGN);
989 savepipe = safe_signal(SIGPIPE, SIG_IGN);
990 if (sigsetjmp(_pop3_jmp, 1)) {
991 safe_signal(SIGINT, saveint);
992 safe_signal(SIGPIPE, savepipe);
993 _pop3_lock = 0;
994 interrupts = 0;
995 goto jleave;
997 if (saveint != SIG_IGN)
998 safe_signal(SIGINT, pop3catch);
999 if (savepipe != SIG_IGN)
1000 safe_signal(SIGPIPE, pop3catch);
1001 pop3_update(&mb);
1002 pop3_exit(&mb);
1003 sclose(&mb.mb_sock);
1004 safe_signal(SIGINT, saveint);
1005 safe_signal(SIGPIPE, savepipe);
1006 _pop3_lock = 0;
1008 rv = TRU1;
1009 jleave:
1010 if(hold_sigs_on)
1011 hold_sigs();
1012 NYD_LEAVE;
1013 return rv;
1015 #endif /* HAVE_POP3 */
1017 /* s-it-mode */