Variables: slightly tweak a_amv_var_canonify(); maxdistance: 6
[s-mailx.git] / pop3.c
blobfb37786f30ae9e315f018b7ddc9110ec25838377
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_SSL
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 ((rv = ssl_open(&scp->sc_url, &scp->sc_sock)) != OKAY)
133 goto jleave;
135 #else
136 if (xok_blook(pop3_use_starttls, &scp->sc_url, oxm)) {
137 n_err(_("No SSL support compiled in\n"));
138 rv = STOP;
139 goto jleave;
141 #endif
143 /* Use the APOP single roundtrip? */
144 #ifdef HAVE_MD5
145 if (ts != NULL && !xok_blook(pop3_no_apop, &scp->sc_url, oxm)) {
146 if ((rv = _pop3_auth_apop(mp, scp, ts)) != OKAY) {
147 char const *ccp;
149 # ifdef HAVE_SSL
150 if (scp->sc_sock.s_use_ssl)
151 ccp = _("over an encrypted connection");
152 else
153 # endif
154 ccp = _("(unsafe clear text!)");
155 n_err(_("POP3 APOP authentication failed!\n"
156 " Server indicated support.. Set *pop3-no-apop*\n"
157 " for plain text authentication %s\n"), ccp);
159 goto jleave;
161 #endif
163 rv = _pop3_auth_plain(mp, scp);
164 jleave:
165 NYD_LEAVE;
166 return rv;
169 #ifdef HAVE_MD5
170 static char *
171 _pop3_lookup_apop_timestamp(char const *bp)
173 /* RFC 1939:
174 * A POP3 server which implements the APOP command will include
175 * a timestamp in its banner greeting. The syntax of the timestamp
176 * corresponds to the "msg-id" in [RFC822]
177 * RFC 822:
178 * msg-id = "<" addr-spec ">"
179 * addr-spec = local-part "@" domain */
180 char const *cp, *ep;
181 size_t tl;
182 char *rp = NULL;
183 bool_t hadat = FAL0;
184 NYD_ENTER;
186 if ((cp = strchr(bp, '<')) == NULL)
187 goto jleave;
189 /* xxx What about malformed APOP timestamp (<@>) here? */
190 for (ep = cp; *ep != '\0'; ++ep) {
191 if (spacechar(*ep))
192 goto jleave;
193 else if (*ep == '@')
194 hadat = TRU1;
195 else if (*ep == '>') {
196 if (!hadat)
197 goto jleave;
198 break;
201 if (*ep != '>')
202 goto jleave;
204 tl = PTR2SIZE(++ep - cp);
205 rp = salloc(tl +1);
206 memcpy(rp, cp, tl);
207 rp[tl] = '\0';
208 jleave:
209 NYD_LEAVE;
210 return rp;
212 #endif
214 #ifdef HAVE_MD5
215 static enum okay
216 _pop3_auth_apop(struct mailbox *mp, struct sockconn const *scp, char const *ts)
218 unsigned char digest[16];
219 char hex[MD5TOHEX_SIZE], *cp;
220 md5_ctx ctx;
221 size_t i;
222 enum okay rv = STOP;
223 NYD_ENTER;
225 md5_init(&ctx);
226 md5_update(&ctx, (uc_i*)n_UNCONST(ts), strlen(ts));
227 md5_update(&ctx, (uc_i*)scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
228 md5_final(digest, &ctx);
229 md5tohex(hex, digest);
231 i = scp->sc_cred.cc_user.l;
232 cp = ac_alloc(5 + i + 1 + MD5TOHEX_SIZE + sizeof(NETNL)-1 +1);
234 memcpy(cp, "APOP ", 5);
235 memcpy(cp + 5, scp->sc_cred.cc_user.s, i);
236 i += 5;
237 cp[i++] = ' ';
238 memcpy(cp + i, hex, MD5TOHEX_SIZE);
239 i += MD5TOHEX_SIZE;
240 memcpy(cp + i, NETNL, sizeof(NETNL));
241 POP3_OUT(rv, cp, MB_COMD, goto jleave);
242 POP3_ANSWER(rv, goto jleave);
244 rv = OKAY;
245 jleave:
246 ac_free(cp);
247 NYD_LEAVE;
248 return rv;
250 #endif /* HAVE_MD5 */
252 static enum okay
253 _pop3_auth_plain(struct mailbox *mp, struct sockconn const *scp)
255 char *cp;
256 enum okay rv = STOP;
257 NYD_ENTER;
259 /* The USER/PASS plain text version */
260 cp = ac_alloc(n_MAX(scp->sc_cred.cc_user.l, scp->sc_cred.cc_pass.l) + 5 +
261 sizeof(NETNL)-1 +1);
263 memcpy(cp, "USER ", 5);
264 memcpy(cp + 5, scp->sc_cred.cc_user.s, scp->sc_cred.cc_user.l);
265 memcpy(cp + 5 + scp->sc_cred.cc_user.l, NETNL, sizeof(NETNL));
266 POP3_OUT(rv, cp, MB_COMD, goto jleave);
267 POP3_ANSWER(rv, goto jleave);
269 memcpy(cp, "PASS ", 5);
270 memcpy(cp + 5, scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
271 memcpy(cp + 5 + scp->sc_cred.cc_pass.l, NETNL, sizeof(NETNL));
272 POP3_OUT(rv, cp, MB_COMD, goto jleave);
273 POP3_ANSWER(rv, goto jleave);
275 rv = OKAY;
276 jleave:
277 ac_free(cp);
278 NYD_LEAVE;
279 return rv;
282 static void
283 pop3_timer_off(void)
285 NYD_ENTER;
286 if (_pop3_keepalive > 0) {
287 alarm(0);
288 safe_signal(SIGALRM, _pop3_savealrm);
290 NYD_LEAVE;
293 static enum okay
294 pop3_answer(struct mailbox *mp)
296 int sz;
297 size_t blen;
298 enum okay rv = STOP;
299 NYD_ENTER;
301 jretry:
302 if ((sz = sgetline(&_pop3_buf, &_pop3_bufsize, &blen, &mp->mb_sock)) > 0) {
303 if ((mp->mb_active & (MB_COMD | MB_MULT)) == MB_MULT)
304 goto jmultiline;
305 if (n_poption & n_PO_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 while (blen > 0 &&
316 (_pop3_buf[blen - 1] == NETNL[0] ||
317 _pop3_buf[blen - 1] == NETNL[1]))
318 _pop3_buf[--blen] = '\0';
319 n_err(_("POP3 error: %s\n"), _pop3_buf);
320 break;
321 default:
322 /* If the answer starts neither with '+' nor with '-', it must be part
323 * of a multiline response. Get lines until a single dot appears */
324 jmultiline:
325 while (_pop3_buf[0] != '.' || _pop3_buf[1] != NETNL[0] ||
326 _pop3_buf[2] != NETNL[1] || _pop3_buf[3] != '\0') {
327 sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock);
328 if (sz <= 0)
329 goto jeof;
331 mp->mb_active &= ~MB_MULT;
332 if (mp->mb_active != MB_NONE)
333 goto jretry;
335 } else {
336 jeof:
337 rv = STOP;
338 mp->mb_active = MB_NONE;
340 NYD_LEAVE;
341 return rv;
344 static enum okay
345 pop3_finish(struct mailbox *mp)
347 NYD_ENTER;
348 while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
349 pop3_answer(mp);
350 NYD_LEAVE;
351 return OKAY;
354 static void
355 pop3catch(int s)
357 NYD_X; /* Signal handler */
358 switch (s) {
359 case SIGINT:
360 /*n_err_sighdl(_("Interrupt during POP3 operation\n"));*/
361 interrupts = 2; /* Force "Interrupt" message shall we onintr(0) */
362 siglongjmp(_pop3_jmp, 1);
363 case SIGPIPE:
364 n_err_sighdl(_("Received SIGPIPE during POP3 operation\n"));
365 break;
369 static void
370 _pop3_maincatch(int s)
372 NYD_X; /* Signal handler */
373 n_UNUSED(s);
374 if (interrupts == 0)
375 n_err_sighdl(_("\n(Interrupt -- one more to abort operation)\n"));
376 else {
377 interrupts = 1;
378 siglongjmp(_pop3_jmp, 1);
382 static enum okay
383 pop3_noop1(struct mailbox *mp)
385 enum okay rv;
386 NYD_ENTER;
388 POP3_OUT(rv, "NOOP" NETNL, MB_COMD, goto jleave);
389 POP3_ANSWER(rv, goto jleave);
390 jleave:
391 NYD_LEAVE;
392 return rv;
395 static void
396 pop3alarm(int s)
398 sighandler_type volatile saveint, savepipe;
399 NYD_X; /* Signal handler */
400 n_UNUSED(s);
402 if (_pop3_lock++ == 0) {
403 hold_all_sigs();
404 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
405 safe_signal(SIGINT, &_pop3_maincatch);
406 savepipe = safe_signal(SIGPIPE, SIG_IGN);
407 if (sigsetjmp(_pop3_jmp, 1)) {
408 interrupts = 0;
409 safe_signal(SIGINT, saveint);
410 safe_signal(SIGPIPE, savepipe);
411 goto jbrk;
413 if (savepipe != SIG_IGN)
414 safe_signal(SIGPIPE, pop3catch);
415 rele_all_sigs();
416 if (pop3_noop1(&mb) != OKAY) {
417 safe_signal(SIGINT, saveint);
418 safe_signal(SIGPIPE, savepipe);
419 goto jleave;
421 safe_signal(SIGINT, saveint);
422 safe_signal(SIGPIPE, savepipe);
424 jbrk:
425 alarm(_pop3_keepalive);
426 jleave:
427 --_pop3_lock;
430 static enum okay
431 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
433 char const *cp;
434 enum okay rv;
435 NYD_ENTER;
437 POP3_OUT(rv, "STAT" NETNL, MB_COMD, goto jleave);
438 POP3_ANSWER(rv, goto jleave);
440 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
442 while (*cp != '\0' && spacechar(*cp))
443 ++cp;
445 rv = STOP;
446 if (*cp != '\0') {
447 size_t i;
449 if(n_idec_uiz_cp(&i, cp, 10, &cp) & n_IDEC_STATE_EMASK)
450 goto jerr;
451 if(i > INT_MAX)
452 goto jerr;
453 *cnt = (int)i;
455 while(*cp != '\0' && !spacechar(*cp))
456 ++cp;
457 while(*cp != '\0' && spacechar(*cp))
458 ++cp;
460 if(*cp == '\0')
461 goto jerr;
462 if(n_idec_uiz_cp(&i, cp, 10, NULL) & n_IDEC_STATE_EMASK)
463 goto jerr;
464 *size = (off_t)i;
465 rv = OKAY;
468 if (rv == STOP)
469 jerr:
470 n_err(_("Invalid POP3 STAT response: %s\n"), _pop3_buf);
471 jleave:
472 NYD_LEAVE;
473 return rv;
476 static enum okay
477 pop3_list(struct mailbox *mp, int n, size_t *size)
479 char o[LINESIZE], *cp;
480 enum okay rv;
481 NYD_ENTER;
483 snprintf(o, sizeof o, "LIST %u" NETNL, n);
484 POP3_OUT(rv, o, MB_COMD, goto jleave);
485 POP3_ANSWER(rv, goto jleave);
487 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
489 while (*cp != '\0' && spacechar(*cp))
490 ++cp;
491 while (*cp != '\0' && !spacechar(*cp))
492 ++cp;
493 while (*cp != '\0' && spacechar(*cp))
494 ++cp;
495 if (*cp != '\0')
496 n_idec_uiz_cp(size, cp, 10, NULL);
497 jleave:
498 NYD_LEAVE;
499 return rv;
502 static void
503 pop3_setptr(struct mailbox *mp, struct sockconn const *scp)
505 size_t i;
506 enum needspec ns;
507 NYD_ENTER;
509 message = scalloc(msgCount + 1, sizeof *message);
510 message[msgCount].m_size = 0;
511 message[msgCount].m_lines = 0;
512 dot = message; /* (Just do it: avoid crash -- shall i now do ointr(0).. */
514 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
515 struct message *m = message + i;
516 m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
517 m->m_block = 0;
518 m->m_offset = 0;
519 m->m_size = m->m_xsize = 0;
522 for (i = 0; UICMP(z, i, <, msgCount); ++i)
523 if (!pop3_list(mp, i + 1, &message[i].m_xsize))
524 goto jleave;
526 /* Force the load of all messages right now */
527 ns = xok_blook(pop3_bulk_load, &scp->sc_url, OXM_ALL)
528 ? NEED_BODY : NEED_HEADER;
529 for (i = 0; UICMP(z, i, <, msgCount); ++i)
530 if (!pop3_get(mp, message + i, ns))
531 goto jleave;
533 srelax_hold();
534 for (i = 0; UICMP(z, i, <, msgCount); ++i) {
535 struct message *m = message + i;
536 char const *cp;
538 if ((cp = hfield1("status", m)) != NULL)
539 while (*cp != '\0') {
540 if (*cp == 'R')
541 m->m_flag |= MREAD;
542 else if (*cp == 'O')
543 m->m_flag &= ~MNEW;
544 ++cp;
547 substdate(m);
548 srelax();
550 srelax_rele();
552 setdot(message);
553 jleave:
554 NYD_LEAVE;
557 static enum okay
558 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
560 char o[LINESIZE], *line, *lp;
561 sighandler_type volatile saveint, savepipe;
562 size_t linesize, linelen, size;
563 int number, lines;
564 int volatile emptyline;
565 off_t offset;
566 enum okay volatile rv;
567 NYD_ENTER;
569 line = NULL; /* TODO line pool */
570 saveint = savepipe = SIG_IGN;
571 linesize = 0;
572 number = (int)PTR2SIZE(m - message + 1);
573 emptyline = 0;
574 rv = STOP;
576 if (mp->mb_sock.s_fd < 0) {
577 n_err(_("POP3 connection already closed\n"));
578 ++_pop3_lock;
579 goto jleave;
582 if (_pop3_lock++ == 0) {
583 hold_all_sigs();
584 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
585 safe_signal(SIGINT, &_pop3_maincatch);
586 savepipe = safe_signal(SIGPIPE, SIG_IGN);
587 if (sigsetjmp(_pop3_jmp, 1))
588 goto jleave;
589 if (savepipe != SIG_IGN)
590 safe_signal(SIGPIPE, pop3catch);
591 rele_all_sigs();
594 fseek(mp->mb_otf, 0L, SEEK_END);
595 offset = ftell(mp->mb_otf);
596 jretry:
597 switch (need) {
598 case NEED_HEADER:
599 snprintf(o, sizeof o, "TOP %u 0" NETNL, number);
600 break;
601 case NEED_BODY:
602 snprintf(o, sizeof o, "RETR %u" NETNL, number);
603 break;
604 case NEED_UNSPEC:
605 abort(); /* XXX */
607 POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
609 if (pop3_answer(mp) == STOP) {
610 if (need == NEED_HEADER) {
611 /* The TOP POP3 command is optional, so retry with entire message */
612 need = NEED_BODY;
613 goto jretry;
615 goto jleave;
618 size = 0;
619 lines = 0;
620 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
621 if (line[0] == '.' && line[1] == NETNL[0] && line[2] == NETNL[1] &&
622 line[3] == '\0') {
623 mp->mb_active &= ~MB_MULT;
624 break;
626 if (line[0] == '.') {
627 lp = line + 1;
628 --linelen;
629 } else
630 lp = line;
631 /* TODO >>
632 * Need to mask 'From ' lines. This cannot be done properly
633 * since some servers pass them as 'From ' and others as
634 * '>From '. Although one could identify the first kind of
635 * server in principle, it is not possible to identify the
636 * second as '>From ' may also come from a server of the
637 * first type as actual data. So do what is absolutely
638 * necessary only - mask 'From '.
640 * If the line is the first line of the message header, it
641 * is likely a real 'From ' line. In this case, it is just
642 * ignored since it violates all standards.
643 * TODO i have *never* seen the latter?!?!?
644 * TODO <<
646 /* Since we simply copy over data without doing any transfer
647 * encoding reclassification/adjustment we *have* to perform
648 * RFC 4155 compliant From_ quoting here */
649 if (emptyline && is_head(lp, linelen, FAL0)) {
650 putc('>', mp->mb_otf);
651 ++size;
653 lines++;
654 if (lp[linelen-1] == NETNL[1] &&
655 (linelen == 1 || lp[linelen-2] == NETNL[0])) {
656 emptyline = linelen <= 2;
657 if (linelen > 2)
658 fwrite(lp, 1, linelen - 2, mp->mb_otf);
659 putc('\n', mp->mb_otf);
660 size += linelen - 1;
661 } else {
662 emptyline = 0;
663 fwrite(lp, 1, linelen, mp->mb_otf);
664 size += linelen;
667 if (!emptyline) {
668 /* TODO This is very ugly; but some POP3 daemons don't end a
669 * TODO message with NETNL NETNL, and we need \n\n for mbox format.
670 * TODO That is to say we do it wrong here in order to get it right
671 * TODO when send.c stuff or with MBOX handling, even though THIS
672 * TODO line is solely a property of the MBOX database format! */
673 putc('\n', mp->mb_otf);
674 ++lines;
675 ++size;
677 m->m_size = size;
678 m->m_lines = lines;
679 m->m_block = mailx_blockof(offset);
680 m->m_offset = mailx_offsetof(offset);
681 fflush(mp->mb_otf);
683 switch (need) {
684 case NEED_HEADER:
685 m->m_content_info |= CI_HAVE_HEADER;
686 break;
687 case NEED_BODY:
688 m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
689 m->m_xlines = m->m_lines;
690 m->m_xsize = m->m_size;
691 break;
692 case NEED_UNSPEC:
693 break;
696 rv = OKAY;
697 jleave:
698 if (line != NULL)
699 free(line);
700 if (saveint != SIG_IGN)
701 safe_signal(SIGINT, saveint);
702 if (savepipe != SIG_IGN)
703 safe_signal(SIGPIPE, savepipe);
704 --_pop3_lock;
705 NYD_LEAVE;
706 if (interrupts)
707 n_raise(SIGINT);
708 return rv;
711 static enum okay
712 pop3_exit(struct mailbox *mp)
714 enum okay rv;
715 NYD_ENTER;
717 POP3_OUT(rv, "QUIT" NETNL, MB_COMD, goto jleave);
718 POP3_ANSWER(rv, goto jleave);
719 jleave:
720 NYD_LEAVE;
721 return rv;
724 static enum okay
725 pop3_delete(struct mailbox *mp, int n)
727 char o[LINESIZE];
728 enum okay rv;
729 NYD_ENTER;
731 snprintf(o, sizeof o, "DELE %u" NETNL, n);
732 POP3_OUT(rv, o, MB_COMD, goto jleave);
733 POP3_ANSWER(rv, goto jleave);
734 jleave:
735 NYD_LEAVE;
736 return rv;
739 static enum okay
740 pop3_update(struct mailbox *mp)
742 struct message *m;
743 int dodel, c, gotcha, held;
744 NYD_ENTER;
746 if (!(n_pstate & n_PS_EDIT)) {
747 holdbits();
748 c = 0;
749 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
750 if (m->m_flag & MBOX)
751 ++c;
752 if (c > 0)
753 makembox();
756 gotcha = held = 0;
757 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
758 if (n_pstate & n_PS_EDIT)
759 dodel = m->m_flag & MDELETED;
760 else
761 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
762 if (dodel) {
763 pop3_delete(mp, PTR2SIZE(m - message + 1));
764 ++gotcha;
765 } else
766 ++held;
769 /* C99 */{
770 char const *dnq;
772 dnq = n_shexp_quote_cp(displayname, FAL0);
774 if (gotcha && (n_pstate & n_PS_EDIT)) {
775 fprintf(n_stdout, _("%s "), dnq);
776 fprintf(n_stdout, (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
777 ? _("complete\n") : _("updated\n"));
778 } else if (held && !(n_pstate & n_PS_EDIT)) {
779 if (held == 1)
780 fprintf(n_stdout, _("Held 1 message in %s\n"), dnq);
781 else
782 fprintf(n_stdout, _("Held %d messages in %s\n"), held, dnq);
785 fflush(n_stdout);
786 NYD_LEAVE;
787 return OKAY;
790 FL enum okay
791 pop3_noop(void)
793 sighandler_type volatile saveint, savepipe;
794 enum okay volatile rv = STOP;
795 NYD_ENTER;
797 _pop3_lock = 1;
798 hold_all_sigs();
799 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
800 safe_signal(SIGINT, &_pop3_maincatch);
801 savepipe = safe_signal(SIGPIPE, SIG_IGN);
802 if (sigsetjmp(_pop3_jmp, 1) == 0) {
803 if (savepipe != SIG_IGN)
804 safe_signal(SIGPIPE, pop3catch);
805 rele_all_sigs();
806 rv = pop3_noop1(&mb);
808 safe_signal(SIGINT, saveint);
809 safe_signal(SIGPIPE, savepipe);
810 _pop3_lock = 0;
811 NYD_LEAVE;
812 return rv;
815 FL int
816 pop3_setfile(char const *server, enum fedit_mode fm)
818 struct sockconn sc;
819 sighandler_type saveint, savepipe;
820 char const *cp;
821 int volatile rv;
822 NYD_ENTER;
824 rv = 1;
825 if (fm & FEDIT_NEWMAIL)
826 goto jleave;
827 rv = -1;
829 if (!url_parse(&sc.sc_url, CPROTO_POP3, server))
830 goto jleave;
831 if (!ok_blook(v15_compat) &&
832 (!(sc.sc_url.url_flags & n_URL_HAD_USER) ||
833 sc.sc_url.url_pass.s != NULL)) {
834 n_err(_("New-style URL used without *v15-compat* being set\n"));
835 goto jleave;
838 if (!(ok_blook(v15_compat) ? ccred_lookup(&sc.sc_cred, &sc.sc_url)
839 : ccred_lookup_old(&sc.sc_cred, CPROTO_POP3,
840 ((sc.sc_url.url_flags & n_URL_HAD_USER) ? sc.sc_url.url_eu_h_p.s
841 : sc.sc_url.url_u_h_p.s))))
842 goto jleave;
844 if (!quit(FAL0))
845 goto jleave;
847 if (!sopen(&sc.sc_sock, &sc.sc_url))
848 goto jleave;
850 rv = 1;
852 if (fm & FEDIT_SYSBOX)
853 n_pstate &= ~n_PS_EDIT;
854 else
855 n_pstate |= n_PS_EDIT;
856 if (mb.mb_sock.s_fd >= 0)
857 sclose(&mb.mb_sock);
858 if (mb.mb_itf) {
859 fclose(mb.mb_itf);
860 mb.mb_itf = NULL;
862 if (mb.mb_otf) {
863 fclose(mb.mb_otf);
864 mb.mb_otf = NULL;
867 initbox(sc.sc_url.url_p_u_h_p);
868 mb.mb_type = MB_VOID;
869 _pop3_lock = 1;
870 mb.mb_sock = sc.sc_sock;
872 saveint = safe_signal(SIGINT, SIG_IGN);
873 savepipe = safe_signal(SIGPIPE, SIG_IGN);
874 if (sigsetjmp(_pop3_jmp, 1)) {
875 sclose(&mb.mb_sock);
876 n_err(_("POP3 connection closed\n"));
877 safe_signal(SIGINT, saveint);
878 safe_signal(SIGPIPE, savepipe);
879 _pop3_lock = 0;
880 rv = -1;
881 if (interrupts > 0)
882 n_raise(SIGINT);
883 goto jleave;
885 if (saveint != SIG_IGN)
886 safe_signal(SIGINT, pop3catch);
887 if (savepipe != SIG_IGN)
888 safe_signal(SIGPIPE, pop3catch);
890 if ((cp = xok_vlook(pop3_keepalive, &sc.sc_url, OXM_ALL)) != NULL) {
891 n_idec_si32_cp(&_pop3_keepalive, cp, 10, NULL);
892 if (_pop3_keepalive > 0) {
893 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
894 alarm(_pop3_keepalive);
898 mb.mb_sock.s_desc = (sc.sc_url.url_flags & n_URL_TLS_REQUIRED)
899 ? "POP3S" : "POP3";
900 mb.mb_sock.s_onclose = pop3_timer_off;
901 if (_pop3_login(&mb, &sc) != OKAY ||
902 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
903 sclose(&mb.mb_sock);
904 pop3_timer_off();
905 safe_signal(SIGINT, saveint);
906 safe_signal(SIGPIPE, savepipe);
907 _pop3_lock = 0;
908 goto jleave;
911 setmsize(msgCount);
912 mb.mb_type = MB_POP3;
913 mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY))
914 ? 0 : MB_DELE;
915 pop3_setptr(&mb, &sc);
917 /*if (!(fm & FEDIT_NEWMAIL)) */{
918 n_pstate &= ~n_PS_SAW_COMMAND;
919 n_pstate |= n_PS_SETFILE_OPENED;
922 safe_signal(SIGINT, saveint);
923 safe_signal(SIGPIPE, savepipe);
924 _pop3_lock = 0;
926 if ((n_poption & (n_PO_EXISTONLY | n_PO_HEADERLIST)) == n_PO_EXISTONLY) {
927 rv = (msgCount == 0);
928 goto jleave;
931 if (!(n_pstate & n_PS_EDIT) && msgCount == 0) {
932 if (!ok_blook(emptystart))
933 n_err(_("No mail at %s\n"), server);
934 goto jleave;
937 rv = 0;
938 jleave:
939 NYD_LEAVE;
940 return rv;
943 FL enum okay
944 pop3_header(struct message *m)
946 enum okay rv;
947 NYD_ENTER;
949 /* TODO no URL here, no OXM possible; (however it is used in setfile()..) */
950 rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
951 NYD_LEAVE;
952 return rv;
955 FL enum okay
956 pop3_body(struct message *m)
958 enum okay rv;
959 NYD_ENTER;
961 rv = pop3_get(&mb, m, NEED_BODY);
962 NYD_LEAVE;
963 return rv;
966 FL bool_t
967 pop3_quit(bool_t hold_sigs_on)
969 sighandler_type volatile saveint, savepipe;
970 bool_t rv;
971 NYD_ENTER;
973 if(hold_sigs_on)
974 rele_sigs();
976 rv = FAL0;
978 if (mb.mb_sock.s_fd < 0) {
979 n_err(_("POP3 connection already closed\n"));
980 rv = TRU1;
981 goto jleave;
984 _pop3_lock = 1;
985 saveint = safe_signal(SIGINT, SIG_IGN);
986 savepipe = safe_signal(SIGPIPE, SIG_IGN);
987 if (sigsetjmp(_pop3_jmp, 1)) {
988 safe_signal(SIGINT, saveint);
989 safe_signal(SIGPIPE, savepipe);
990 _pop3_lock = 0;
991 interrupts = 0;
992 goto jleave;
994 if (saveint != SIG_IGN)
995 safe_signal(SIGINT, pop3catch);
996 if (savepipe != SIG_IGN)
997 safe_signal(SIGPIPE, pop3catch);
998 pop3_update(&mb);
999 pop3_exit(&mb);
1000 sclose(&mb.mb_sock);
1001 safe_signal(SIGINT, saveint);
1002 safe_signal(SIGPIPE, savepipe);
1003 _pop3_lock = 0;
1005 rv = TRU1;
1006 jleave:
1007 if(hold_sigs_on)
1008 hold_sigs();
1009 NYD_LEAVE;
1010 return rv;
1012 #endif /* HAVE_POP3 */
1014 /* s-it-mode */