cc-test.sh: t_behave_iconv_mainbody() should compile test instead, sigh!
[s-mailx.git] / pop3.c
blob2e2f5a7fe618764c40712faaa7f1660767bf7aef
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 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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 (n_poption & n_PO_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 si32_t _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 whether 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_flags & n_URL_TLS_REQUIRED) &&
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;
148 # ifdef HAVE_SSL
149 if (scp->sc_sock.s_use_ssl)
150 ccp = _("over an encrypted connection");
151 else
152 # endif
153 ccp = _("(unsafe clear text!)");
154 n_err(_("POP3 APOP authentication failed!\n"
155 " Server indicated support.. Set *pop3-no-apop*\n"
156 " for plain text authentication %s\n"), ccp);
158 goto jleave;
160 #endif
162 rv = _pop3_auth_plain(mp, scp);
163 jleave:
164 NYD_LEAVE;
165 return rv;
168 #ifdef HAVE_MD5
169 static char *
170 _pop3_lookup_apop_timestamp(char const *bp)
172 /* RFC 1939:
173 * A POP3 server which implements the APOP command will include
174 * a timestamp in its banner greeting. The syntax of the timestamp
175 * corresponds to the "msg-id" in [RFC822]
176 * RFC 822:
177 * msg-id = "<" addr-spec ">"
178 * addr-spec = local-part "@" domain */
179 char const *cp, *ep;
180 size_t tl;
181 char *rp = NULL;
182 bool_t hadat = FAL0;
183 NYD_ENTER;
185 if ((cp = strchr(bp, '<')) == NULL)
186 goto jleave;
188 /* xxx What about malformed APOP timestamp (<@>) here? */
189 for (ep = cp; *ep != '\0'; ++ep) {
190 if (spacechar(*ep))
191 goto jleave;
192 else if (*ep == '@')
193 hadat = TRU1;
194 else if (*ep == '>') {
195 if (!hadat)
196 goto jleave;
197 break;
200 if (*ep != '>')
201 goto jleave;
203 tl = PTR2SIZE(++ep - cp);
204 rp = salloc(tl +1);
205 memcpy(rp, cp, tl);
206 rp[tl] = '\0';
207 jleave:
208 NYD_LEAVE;
209 return rp;
211 #endif
213 #ifdef HAVE_MD5
214 static enum okay
215 _pop3_auth_apop(struct mailbox *mp, struct sockconn const *scp, char const *ts)
217 unsigned char digest[16];
218 char hex[MD5TOHEX_SIZE], *cp;
219 md5_ctx ctx;
220 size_t i;
221 enum okay rv = STOP;
222 NYD_ENTER;
224 md5_init(&ctx);
225 md5_update(&ctx, (uc_i*)n_UNCONST(ts), strlen(ts));
226 md5_update(&ctx, (uc_i*)scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
227 md5_final(digest, &ctx);
228 md5tohex(hex, digest);
230 i = scp->sc_cred.cc_user.l;
231 cp = ac_alloc(5 + i + 1 + MD5TOHEX_SIZE + sizeof(NETNL)-1 +1);
233 memcpy(cp, "APOP ", 5);
234 memcpy(cp + 5, scp->sc_cred.cc_user.s, i);
235 i += 5;
236 cp[i++] = ' ';
237 memcpy(cp + i, hex, MD5TOHEX_SIZE);
238 i += MD5TOHEX_SIZE;
239 memcpy(cp + i, NETNL, sizeof(NETNL));
240 POP3_OUT(rv, cp, MB_COMD, goto jleave);
241 POP3_ANSWER(rv, goto jleave);
243 rv = OKAY;
244 jleave:
245 ac_free(cp);
246 NYD_LEAVE;
247 return rv;
249 #endif /* HAVE_MD5 */
251 static enum okay
252 _pop3_auth_plain(struct mailbox *mp, struct sockconn const *scp)
254 char *cp;
255 enum okay rv = STOP;
256 NYD_ENTER;
258 /* The USER/PASS plain text version */
259 cp = ac_alloc(n_MAX(scp->sc_cred.cc_user.l, scp->sc_cred.cc_pass.l) + 5 +
260 sizeof(NETNL)-1 +1);
262 memcpy(cp, "USER ", 5);
263 memcpy(cp + 5, scp->sc_cred.cc_user.s, scp->sc_cred.cc_user.l);
264 memcpy(cp + 5 + scp->sc_cred.cc_user.l, NETNL, sizeof(NETNL));
265 POP3_OUT(rv, cp, MB_COMD, goto jleave);
266 POP3_ANSWER(rv, goto jleave);
268 memcpy(cp, "PASS ", 5);
269 memcpy(cp + 5, scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
270 memcpy(cp + 5 + scp->sc_cred.cc_pass.l, NETNL, sizeof(NETNL));
271 POP3_OUT(rv, cp, MB_COMD, goto jleave);
272 POP3_ANSWER(rv, goto jleave);
274 rv = OKAY;
275 jleave:
276 ac_free(cp);
277 NYD_LEAVE;
278 return rv;
281 static void
282 pop3_timer_off(void)
284 NYD_ENTER;
285 if (_pop3_keepalive > 0) {
286 alarm(0);
287 safe_signal(SIGALRM, _pop3_savealrm);
289 NYD_LEAVE;
292 static enum okay
293 pop3_answer(struct mailbox *mp)
295 int sz;
296 size_t blen;
297 enum okay rv = STOP;
298 NYD_ENTER;
300 jretry:
301 if ((sz = sgetline(&_pop3_buf, &_pop3_bufsize, &blen, &mp->mb_sock)) > 0) {
302 if ((mp->mb_active & (MB_COMD | MB_MULT)) == MB_MULT)
303 goto jmultiline;
304 if (n_poption & n_PO_VERBVERB)
305 n_err(_pop3_buf);
306 switch (*_pop3_buf) {
307 case '+':
308 rv = OKAY;
309 mp->mb_active &= ~MB_COMD;
310 break;
311 case '-':
312 rv = STOP;
313 mp->mb_active = MB_NONE;
314 while (blen > 0 &&
315 (_pop3_buf[blen - 1] == NETNL[0] ||
316 _pop3_buf[blen - 1] == NETNL[1]))
317 _pop3_buf[--blen] = '\0';
318 n_err(_("POP3 error: %s\n"), _pop3_buf);
319 break;
320 default:
321 /* If the answer starts neither with '+' nor with '-', it must be part
322 * of a multiline response. Get lines until a single dot appears */
323 jmultiline:
324 while (_pop3_buf[0] != '.' || _pop3_buf[1] != NETNL[0] ||
325 _pop3_buf[2] != NETNL[1] || _pop3_buf[3] != '\0') {
326 sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock);
327 if (sz <= 0)
328 goto jeof;
330 mp->mb_active &= ~MB_MULT;
331 if (mp->mb_active != MB_NONE)
332 goto jretry;
334 } else {
335 jeof:
336 rv = STOP;
337 mp->mb_active = MB_NONE;
339 NYD_LEAVE;
340 return rv;
343 static enum okay
344 pop3_finish(struct mailbox *mp)
346 NYD_ENTER;
347 while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
348 pop3_answer(mp);
349 NYD_LEAVE;
350 return OKAY;
353 static void
354 pop3catch(int s)
356 NYD_X; /* Signal handler */
357 switch (s) {
358 case SIGINT:
359 /*n_err_sighdl(_("Interrupt during POP3 operation\n"));*/
360 interrupts = 2; /* Force "Interrupt" message shall we onintr(0) */
361 siglongjmp(_pop3_jmp, 1);
362 case SIGPIPE:
363 n_err_sighdl(_("Received SIGPIPE during POP3 operation\n"));
364 break;
368 static void
369 _pop3_maincatch(int s)
371 NYD_X; /* Signal handler */
372 n_UNUSED(s);
373 if (interrupts == 0)
374 n_err_sighdl(_("\n(Interrupt -- one more to abort operation)\n"));
375 else {
376 interrupts = 1;
377 siglongjmp(_pop3_jmp, 1);
381 static enum okay
382 pop3_noop1(struct mailbox *mp)
384 enum okay rv;
385 NYD_ENTER;
387 POP3_OUT(rv, "NOOP" NETNL, MB_COMD, goto jleave);
388 POP3_ANSWER(rv, goto jleave);
389 jleave:
390 NYD_LEAVE;
391 return rv;
394 static void
395 pop3alarm(int s)
397 sighandler_type volatile saveint, savepipe;
398 NYD_X; /* Signal handler */
399 n_UNUSED(s);
401 if (_pop3_lock++ == 0) {
402 hold_all_sigs();
403 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
404 safe_signal(SIGINT, &_pop3_maincatch);
405 savepipe = safe_signal(SIGPIPE, SIG_IGN);
406 if (sigsetjmp(_pop3_jmp, 1)) {
407 interrupts = 0;
408 safe_signal(SIGINT, saveint);
409 safe_signal(SIGPIPE, savepipe);
410 goto jbrk;
412 if (savepipe != SIG_IGN)
413 safe_signal(SIGPIPE, pop3catch);
414 rele_all_sigs();
415 if (pop3_noop1(&mb) != OKAY) {
416 safe_signal(SIGINT, saveint);
417 safe_signal(SIGPIPE, savepipe);
418 goto jleave;
420 safe_signal(SIGINT, saveint);
421 safe_signal(SIGPIPE, savepipe);
423 jbrk:
424 alarm(_pop3_keepalive);
425 jleave:
426 --_pop3_lock;
429 static enum okay
430 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
432 char const *cp;
433 enum okay rv;
434 NYD_ENTER;
436 POP3_OUT(rv, "STAT" NETNL, MB_COMD, goto jleave);
437 POP3_ANSWER(rv, goto jleave);
439 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
441 while (*cp != '\0' && spacechar(*cp))
442 ++cp;
444 rv = STOP;
445 if (*cp != '\0') {
446 size_t i;
448 if(n_idec_uiz_cp(&i, cp, 10, &cp) & n_IDEC_STATE_EMASK)
449 goto jerr;
450 if(i > INT_MAX)
451 goto jerr;
452 *cnt = (int)i;
454 while(*cp != '\0' && !spacechar(*cp))
455 ++cp;
456 while(*cp != '\0' && spacechar(*cp))
457 ++cp;
459 if(*cp == '\0')
460 goto jerr;
461 if(n_idec_uiz_cp(&i, cp, 10, NULL) & n_IDEC_STATE_EMASK)
462 goto jerr;
463 *size = (off_t)i;
464 rv = OKAY;
467 if (rv == STOP)
468 jerr:
469 n_err(_("Invalid POP3 STAT response: %s\n"), _pop3_buf);
470 jleave:
471 NYD_LEAVE;
472 return rv;
475 static enum okay
476 pop3_list(struct mailbox *mp, int n, size_t *size)
478 char o[LINESIZE], *cp;
479 enum okay rv;
480 NYD_ENTER;
482 snprintf(o, sizeof o, "LIST %u" NETNL, n);
483 POP3_OUT(rv, o, MB_COMD, goto jleave);
484 POP3_ANSWER(rv, goto jleave);
486 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
488 while (*cp != '\0' && spacechar(*cp))
489 ++cp;
490 while (*cp != '\0' && !spacechar(*cp))
491 ++cp;
492 while (*cp != '\0' && spacechar(*cp))
493 ++cp;
494 if (*cp != '\0')
495 n_idec_uiz_cp(size, cp, 10, NULL);
496 jleave:
497 NYD_LEAVE;
498 return rv;
501 static void
502 pop3_setptr(struct mailbox *mp, struct sockconn const *scp)
504 size_t i;
505 enum needspec ns;
506 NYD_ENTER;
508 message = scalloc(msgCount + 1, sizeof *message);
509 message[msgCount].m_size = 0;
510 message[msgCount].m_lines = 0;
511 dot = message; /* (Just do it: avoid crash -- shall i now do ointr(0).. */
513 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
514 struct message *m = message + i;
515 m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
516 m->m_block = 0;
517 m->m_offset = 0;
518 m->m_size = m->m_xsize = 0;
521 for (i = 0; UICMP(z, i, <, msgCount); ++i)
522 if (!pop3_list(mp, i + 1, &message[i].m_xsize))
523 goto jleave;
525 /* Force the load of all messages right now */
526 ns = xok_blook(pop3_bulk_load, &scp->sc_url, OXM_ALL)
527 ? NEED_BODY : NEED_HEADER;
528 for (i = 0; UICMP(z, i, <, msgCount); ++i)
529 if (!pop3_get(mp, message + i, ns))
530 goto jleave;
532 srelax_hold();
533 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
534 struct message *m = message + i;
535 char const *cp;
537 if ((cp = hfield1("status", m)) != NULL)
538 while (*cp != '\0') {
539 if (*cp == 'R')
540 m->m_flag |= MREAD;
541 else if (*cp == 'O')
542 m->m_flag &= ~MNEW;
543 ++cp;
546 substdate(m);
547 srelax();
549 srelax_rele();
551 setdot(message);
552 jleave:
553 NYD_LEAVE;
556 static enum okay
557 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
559 char o[LINESIZE], *line, *lp;
560 sighandler_type volatile saveint, savepipe;
561 size_t linesize, linelen, size;
562 int number, lines;
563 int volatile emptyline;
564 off_t offset;
565 enum okay volatile rv;
566 NYD_ENTER;
568 line = NULL; /* TODO line pool */
569 saveint = savepipe = SIG_IGN;
570 linesize = 0;
571 number = (int)PTR2SIZE(m - message + 1);
572 emptyline = 0;
573 rv = STOP;
575 if (mp->mb_sock.s_fd < 0) {
576 n_err(_("POP3 connection already closed\n"));
577 ++_pop3_lock;
578 goto jleave;
581 if (_pop3_lock++ == 0) {
582 hold_all_sigs();
583 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
584 safe_signal(SIGINT, &_pop3_maincatch);
585 savepipe = safe_signal(SIGPIPE, SIG_IGN);
586 if (sigsetjmp(_pop3_jmp, 1))
587 goto jleave;
588 if (savepipe != SIG_IGN)
589 safe_signal(SIGPIPE, pop3catch);
590 rele_all_sigs();
593 fseek(mp->mb_otf, 0L, SEEK_END);
594 offset = ftell(mp->mb_otf);
595 jretry:
596 switch (need) {
597 case NEED_HEADER:
598 snprintf(o, sizeof o, "TOP %u 0" NETNL, number);
599 break;
600 case NEED_BODY:
601 snprintf(o, sizeof o, "RETR %u" NETNL, number);
602 break;
603 case NEED_UNSPEC:
604 abort(); /* XXX */
606 POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
608 if (pop3_answer(mp) == STOP) {
609 if (need == NEED_HEADER) {
610 /* The TOP POP3 command is optional, so retry with entire message */
611 need = NEED_BODY;
612 goto jretry;
614 goto jleave;
617 size = 0;
618 lines = 0;
619 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
620 if (line[0] == '.' && line[1] == NETNL[0] && line[2] == NETNL[1] &&
621 line[3] == '\0') {
622 mp->mb_active &= ~MB_MULT;
623 break;
625 if (line[0] == '.') {
626 lp = line + 1;
627 --linelen;
628 } else
629 lp = line;
630 /* TODO >>
631 * Need to mask 'From ' lines. This cannot be done properly
632 * since some servers pass them as 'From ' and others as
633 * '>From '. Although one could identify the first kind of
634 * server in principle, it is not possible to identify the
635 * second as '>From ' may also come from a server of the
636 * first type as actual data. So do what is absolutely
637 * necessary only - mask 'From '.
639 * If the line is the first line of the message header, it
640 * is likely a real 'From ' line. In this case, it is just
641 * ignored since it violates all standards.
642 * TODO i have *never* seen the latter?!?!?
643 * TODO <<
645 /* Since we simply copy over data without doing any transfer
646 * encoding reclassification/adjustment we *have* to perform
647 * RFC 4155 compliant From_ quoting here */
648 if (emptyline && is_head(lp, linelen, FAL0)) {
649 putc('>', mp->mb_otf);
650 ++size;
652 lines++;
653 if (lp[linelen-1] == NETNL[1] &&
654 (linelen == 1 || lp[linelen-2] == NETNL[0])) {
655 emptyline = linelen <= 2;
656 if (linelen > 2)
657 fwrite(lp, 1, linelen - 2, mp->mb_otf);
658 putc('\n', mp->mb_otf);
659 size += linelen - 1;
660 } else {
661 emptyline = 0;
662 fwrite(lp, 1, linelen, mp->mb_otf);
663 size += linelen;
666 if (!emptyline) {
667 /* This is very ugly; but some POP3 daemons don't end a
668 * message with NETNL NETNL, and we need \n\n for mbox format */
669 putc('\n', mp->mb_otf);
670 ++lines;
671 ++size;
673 m->m_size = size;
674 m->m_lines = lines;
675 m->m_block = mailx_blockof(offset);
676 m->m_offset = mailx_offsetof(offset);
677 fflush(mp->mb_otf);
679 switch (need) {
680 case NEED_HEADER:
681 m->m_content_info |= CI_HAVE_HEADER;
682 break;
683 case NEED_BODY:
684 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
685 m->m_xlines = m->m_lines;
686 m->m_xsize = m->m_size;
687 break;
688 case NEED_UNSPEC:
689 break;
692 rv = OKAY;
693 jleave:
694 if (line != NULL)
695 free(line);
696 if (saveint != SIG_IGN)
697 safe_signal(SIGINT, saveint);
698 if (savepipe != SIG_IGN)
699 safe_signal(SIGPIPE, savepipe);
700 --_pop3_lock;
701 NYD_LEAVE;
702 if (interrupts)
703 n_raise(SIGINT);
704 return rv;
707 static enum okay
708 pop3_exit(struct mailbox *mp)
710 enum okay rv;
711 NYD_ENTER;
713 POP3_OUT(rv, "QUIT" NETNL, MB_COMD, goto jleave);
714 POP3_ANSWER(rv, goto jleave);
715 jleave:
716 NYD_LEAVE;
717 return rv;
720 static enum okay
721 pop3_delete(struct mailbox *mp, int n)
723 char o[LINESIZE];
724 enum okay rv;
725 NYD_ENTER;
727 snprintf(o, sizeof o, "DELE %u" NETNL, n);
728 POP3_OUT(rv, o, MB_COMD, goto jleave);
729 POP3_ANSWER(rv, goto jleave);
730 jleave:
731 NYD_LEAVE;
732 return rv;
735 static enum okay
736 pop3_update(struct mailbox *mp)
738 struct message *m;
739 int dodel, c, gotcha, held;
740 NYD_ENTER;
742 if (!(n_pstate & n_PS_EDIT)) {
743 holdbits();
744 c = 0;
745 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
746 if (m->m_flag & MBOX)
747 ++c;
748 if (c > 0)
749 makembox();
752 gotcha = held = 0;
753 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
754 if (n_pstate & n_PS_EDIT)
755 dodel = m->m_flag & MDELETED;
756 else
757 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
758 if (dodel) {
759 pop3_delete(mp, PTR2SIZE(m - message + 1));
760 ++gotcha;
761 } else
762 ++held;
765 /* C99 */{
766 char const *dnq;
768 dnq = n_shexp_quote_cp(displayname, FAL0);
770 if (gotcha && (n_pstate & n_PS_EDIT)) {
771 fprintf(n_stdout, _("%s "), dnq);
772 fprintf(n_stdout, (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
773 ? _("complete\n") : _("updated\n"));
774 } else if (held && !(n_pstate & n_PS_EDIT)) {
775 if (held == 1)
776 fprintf(n_stdout, _("Held 1 message in %s\n"), dnq);
777 else
778 fprintf(n_stdout, _("Held %d messages in %s\n"), held, dnq);
781 fflush(n_stdout);
782 NYD_LEAVE;
783 return OKAY;
786 FL enum okay
787 pop3_noop(void)
789 sighandler_type volatile saveint, savepipe;
790 enum okay volatile rv = STOP;
791 NYD_ENTER;
793 _pop3_lock = 1;
794 hold_all_sigs();
795 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
796 safe_signal(SIGINT, &_pop3_maincatch);
797 savepipe = safe_signal(SIGPIPE, SIG_IGN);
798 if (sigsetjmp(_pop3_jmp, 1) == 0) {
799 if (savepipe != SIG_IGN)
800 safe_signal(SIGPIPE, pop3catch);
801 rele_all_sigs();
802 rv = pop3_noop1(&mb);
804 safe_signal(SIGINT, saveint);
805 safe_signal(SIGPIPE, savepipe);
806 _pop3_lock = 0;
807 NYD_LEAVE;
808 return rv;
811 FL int
812 pop3_setfile(char const *server, enum fedit_mode fm)
814 struct sockconn sc;
815 sighandler_type saveint, savepipe;
816 char const *cp;
817 int volatile rv;
818 NYD_ENTER;
820 rv = 1;
821 if (fm & FEDIT_NEWMAIL)
822 goto jleave;
823 rv = -1;
825 if (!url_parse(&sc.sc_url, CPROTO_POP3, server))
826 goto jleave;
827 if (!ok_blook(v15_compat) &&
828 (!(sc.sc_url.url_flags & n_URL_HAD_USER) ||
829 sc.sc_url.url_pass.s != NULL)) {
830 n_err(_("New-style URL used without *v15-compat* being set\n"));
831 goto jleave;
834 if (!(ok_blook(v15_compat) ? ccred_lookup(&sc.sc_cred, &sc.sc_url)
835 : ccred_lookup_old(&sc.sc_cred, CPROTO_POP3,
836 ((sc.sc_url.url_flags & n_URL_HAD_USER) ? sc.sc_url.url_eu_h_p.s
837 : sc.sc_url.url_u_h_p.s))))
838 goto jleave;
840 if (!quit(FAL0))
841 goto jleave;
843 if (!sopen(&sc.sc_sock, &sc.sc_url))
844 goto jleave;
846 rv = 1;
848 if (fm & FEDIT_SYSBOX)
849 n_pstate &= ~n_PS_EDIT;
850 else
851 n_pstate |= n_PS_EDIT;
852 if (mb.mb_sock.s_fd >= 0)
853 sclose(&mb.mb_sock);
854 if (mb.mb_itf) {
855 fclose(mb.mb_itf);
856 mb.mb_itf = NULL;
858 if (mb.mb_otf) {
859 fclose(mb.mb_otf);
860 mb.mb_otf = NULL;
863 initbox(sc.sc_url.url_p_u_h_p);
864 mb.mb_type = MB_VOID;
865 _pop3_lock = 1;
866 mb.mb_sock = sc.sc_sock;
868 saveint = safe_signal(SIGINT, SIG_IGN);
869 savepipe = safe_signal(SIGPIPE, SIG_IGN);
870 if (sigsetjmp(_pop3_jmp, 1)) {
871 sclose(&mb.mb_sock);
872 n_err(_("POP3 connection closed\n"));
873 safe_signal(SIGINT, saveint);
874 safe_signal(SIGPIPE, savepipe);
875 _pop3_lock = 0;
876 rv = -1;
877 if (interrupts > 0)
878 n_raise(SIGINT);
879 goto jleave;
881 if (saveint != SIG_IGN)
882 safe_signal(SIGINT, pop3catch);
883 if (savepipe != SIG_IGN)
884 safe_signal(SIGPIPE, pop3catch);
886 if ((cp = xok_vlook(pop3_keepalive, &sc.sc_url, OXM_ALL)) != NULL) {
887 n_idec_si32_cp(&_pop3_keepalive, cp, 10, NULL);
888 if (_pop3_keepalive > 0) {
889 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
890 alarm(_pop3_keepalive);
894 mb.mb_sock.s_desc = (sc.sc_url.url_flags & n_URL_TLS_REQUIRED)
895 ? "POP3S" : "POP3";
896 mb.mb_sock.s_onclose = pop3_timer_off;
897 if (_pop3_login(&mb, &sc) != OKAY ||
898 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
899 sclose(&mb.mb_sock);
900 pop3_timer_off();
901 safe_signal(SIGINT, saveint);
902 safe_signal(SIGPIPE, savepipe);
903 _pop3_lock = 0;
904 goto jleave;
907 setmsize(msgCount);
908 mb.mb_type = MB_POP3;
909 mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY))
910 ? 0 : MB_DELE;
911 pop3_setptr(&mb, &sc);
913 /*if (!(fm & FEDIT_NEWMAIL)) */{
914 n_pstate &= ~n_PS_SAW_COMMAND;
915 n_pstate |= n_PS_SETFILE_OPENED;
918 safe_signal(SIGINT, saveint);
919 safe_signal(SIGPIPE, savepipe);
920 _pop3_lock = 0;
922 if ((n_poption & (n_PO_EXISTONLY | n_PO_HEADERLIST)) == n_PO_EXISTONLY) {
923 rv = (msgCount == 0);
924 goto jleave;
927 if (!(n_pstate & n_PS_EDIT) && msgCount == 0) {
928 if (!ok_blook(emptystart))
929 n_err(_("No mail at %s\n"), server);
930 goto jleave;
933 rv = 0;
934 jleave:
935 NYD_LEAVE;
936 return rv;
939 FL enum okay
940 pop3_header(struct message *m)
942 enum okay rv;
943 NYD_ENTER;
945 /* TODO no URL here, no OXM possible; (however it is used in setfile()..) */
946 rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
947 NYD_LEAVE;
948 return rv;
951 FL enum okay
952 pop3_body(struct message *m)
954 enum okay rv;
955 NYD_ENTER;
957 rv = pop3_get(&mb, m, NEED_BODY);
958 NYD_LEAVE;
959 return rv;
962 FL bool_t
963 pop3_quit(bool_t hold_sigs_on)
965 sighandler_type volatile saveint, savepipe;
966 bool_t rv;
967 NYD_ENTER;
969 if(hold_sigs_on)
970 rele_sigs();
972 rv = FAL0;
974 if (mb.mb_sock.s_fd < 0) {
975 n_err(_("POP3 connection already closed\n"));
976 rv = TRU1;
977 goto jleave;
980 _pop3_lock = 1;
981 saveint = safe_signal(SIGINT, SIG_IGN);
982 savepipe = safe_signal(SIGPIPE, SIG_IGN);
983 if (sigsetjmp(_pop3_jmp, 1)) {
984 safe_signal(SIGINT, saveint);
985 safe_signal(SIGPIPE, savepipe);
986 _pop3_lock = 0;
987 interrupts = 0;
988 goto jleave;
990 if (saveint != SIG_IGN)
991 safe_signal(SIGINT, pop3catch);
992 if (savepipe != SIG_IGN)
993 safe_signal(SIGPIPE, pop3catch);
994 pop3_update(&mb);
995 pop3_exit(&mb);
996 sclose(&mb.mb_sock);
997 safe_signal(SIGINT, saveint);
998 safe_signal(SIGPIPE, savepipe);
999 _pop3_lock = 0;
1001 rv = TRU1;
1002 jleave:
1003 if(hold_sigs_on)
1004 hold_sigs();
1005 NYD_LEAVE;
1006 return rv;
1008 #endif /* HAVE_POP3 */
1010 /* s-it-mode */