NYD: maildir.c
[s-mailx.git] / pop3.c
blobcea5b8c896796aec6492dbed17aefb66229fb2f0
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 - 2014 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2002
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 EMPTY_FILE(pop3)
45 #ifdef HAVE_POP3
46 #ifdef HAVE_MD5
47 # include "md5.h"
48 #endif
50 #define POP3_ANSWER() POP3_XANSWER(return STOP);
51 #define POP3_XANSWER(ACTIONSTOP) \
53 if (pop3_answer(mp) == STOP) {\
54 ACTIONSTOP;\
58 #define POP3_OUT(X,Y) POP3_XOUT(X, Y, return STOP);
59 #define POP3_XOUT(X,Y,ACTIONSTOP) \
61 if (pop3_finish(mp) == STOP) {\
62 ACTIONSTOP;\
64 if (options & OPT_VERBOSE)\
65 fprintf(stderr, ">>> %s", X);\
66 mp->mb_active |= Y;\
67 if (swrite(&mp->mb_sock, X) == STOP) {\
68 ACTIONSTOP;\
72 static char *_pop3_buf;
73 static size_t _pop3_bufsize;
74 static sigjmp_buf _pop3_jmp;
75 static sighandler_type _pop3_savealrm;
76 static int _pop3_keepalive;
77 static int volatile _pop3_lock;
79 /* Perform entire login handshake */
80 static enum okay _pop3_login(struct mailbox *mp, char *xuser, char *pass,
81 char const *uhp, char const *xserver);
83 /* APOP: get greeting credential or NULL */
84 #ifdef HAVE_MD5
85 static char * _pop3_lookup_apop_timestamp(char const *bp);
86 #endif
88 static bool_t _pop3_use_starttls(char const *uhp);
90 /* APOP: shall we use it (for *uhp*)? */
91 static bool_t _pop3_no_apop(char const *uhp);
93 /* Several authentication methods */
94 static enum okay _pop3_auth_plain(struct mailbox *mp, char *xuser,
95 char *pass);
96 #ifdef HAVE_MD5
97 static enum okay _pop3_auth_apop(struct mailbox *mp, char *xuser,
98 char *pass, char const *ts);
99 #endif
101 static void pop3_timer_off(void);
102 static enum okay pop3_answer(struct mailbox *mp);
103 static enum okay pop3_finish(struct mailbox *mp);
104 static void pop3catch(int s);
105 static void _pop3_maincatch(int s);
106 static enum okay pop3_noop1(struct mailbox *mp);
107 static void pop3alarm(int s);
108 static enum okay pop3_stat(struct mailbox *mp, off_t *size, int *cnt);
109 static enum okay pop3_list(struct mailbox *mp, int n, size_t *size);
110 static void pop3_init(struct mailbox *mp, int n);
111 static void pop3_dates(struct mailbox *mp);
112 static void pop3_setptr(struct mailbox *mp);
113 static enum okay pop3_get(struct mailbox *mp, struct message *m,
114 enum needspec need);
115 static enum okay pop3_exit(struct mailbox *mp);
116 static enum okay pop3_delete(struct mailbox *mp, int n);
117 static enum okay pop3_update(struct mailbox *mp);
119 static enum okay
120 _pop3_login(struct mailbox *mp, char *xuser, char *pass, char const *uhp,
121 char const *xserver)
123 #ifdef HAVE_MD5
124 char *ts;
125 #endif
126 char *cp;
127 enum okay rv = STOP;
129 /* Get the greeting, check wether APOP is advertised */
130 POP3_XANSWER(goto jleave);
131 #ifdef HAVE_MD5
132 ts = _pop3_lookup_apop_timestamp(_pop3_buf);
133 #endif
135 if ((cp = strchr(xserver, ':')) != NULL) { /* TODO GENERIC URI PARSE! */
136 size_t l = (size_t)(cp - xserver);
137 char *x = salloc(l + 1);
138 memcpy(x, xserver, l);
139 x[l] = '\0';
140 xserver = x;
143 /* If not yet secured, can we upgrade to TLS? */
144 #ifdef HAVE_SSL
145 if (mp->mb_sock.s_use_ssl == 0 && _pop3_use_starttls(uhp)) {
146 POP3_XOUT("STLS\r\n", MB_COMD, goto jleave);
147 POP3_XANSWER(goto jleave);
148 if (ssl_open(xserver, &mp->mb_sock, uhp) != OKAY)
149 goto jleave;
151 #else
152 if (_pop3_use_starttls(uhp)) {
153 fprintf(stderr, "No SSL support compiled in.\n");
154 goto jleave;
156 #endif
158 /* Use the APOP single roundtrip? */
159 if (! _pop3_no_apop(uhp)) {
160 #ifdef HAVE_MD5
161 if (ts != NULL) {
162 rv = _pop3_auth_apop(mp, xuser, pass, ts);
163 if (rv != OKAY)
164 fprintf(stderr, tr(276,
165 "POP3 `APOP' authentication failed, "
166 "maybe try setting *pop3-no-apop*\n"));
167 goto jleave;
168 } else
169 #endif
170 if (options & OPT_VERBOSE)
171 fprintf(stderr, tr(204, "No POP3 `APOP' support "
172 "available, sending password in clear text\n"));
174 rv = _pop3_auth_plain(mp, xuser, pass);
175 jleave:
176 return rv;
179 static char *
180 _pop3_lookup_apop_timestamp(char const *bp)
183 * RFC 1939:
184 * A POP3 server which implements the APOP command will include
185 * a timestamp in its banner greeting. The syntax of the timestamp
186 * corresponds to the `msg-id' in [RFC822]
187 * RFC 822:
188 * msg-id = "<" addr-spec ">"
189 * addr-spec = local-part "@" domain
191 char const *cp, *ep;
192 size_t tl;
193 char *rp = NULL;
194 bool_t hadat = FAL0;
196 if ((cp = strchr(bp, '<')) == NULL)
197 goto jleave;
199 /* xxx What about malformed APOP timestamp (<@>) here? */
200 for (ep = cp; *ep; ep++) {
201 if (spacechar(*ep))
202 goto jleave;
203 else if (*ep == '@')
204 hadat = TRU1;
205 else if (*ep == '>') {
206 if (! hadat)
207 goto jleave;
208 break;
211 if (*ep != '>')
212 goto jleave;
214 tl = (size_t)(++ep - cp);
215 rp = salloc(tl + 1);
216 memcpy(rp, cp, tl);
217 rp[tl] = '\0';
218 jleave:
219 return rp;
222 static bool_t
223 _pop3_use_starttls(char const *uhp)
225 char *var;
227 if (ok_blook(pop3_use_starttls))
228 return TRU1;
229 var = savecat("pop3-use-starttls-", uhp);
230 return vok_blook(var);
233 static bool_t
234 _pop3_no_apop(char const *uhp)
236 bool_t ret;
238 if (!(ret = ok_blook(pop3_no_apop))) {
239 #define __S "pop3-no-apop-"
240 #define __SL sizeof(__S)
241 size_t i = strlen(uhp);
242 char *var = ac_alloc(i + __SL);
243 memcpy(var, __S, __SL - 1);
244 memcpy(var + __SL - 1, uhp, i + 1);
245 ret = vok_blook(var);
246 ac_free(var);
247 #undef __SL
248 #undef __S
250 return ret;
253 #ifdef HAVE_MD5
254 static enum okay
255 _pop3_auth_apop(struct mailbox *mp, char *xuser, char *pass, char const *ts)
257 enum okay rv = STOP;
258 unsigned char digest[16];
259 char hex[MD5TOHEX_SIZE];
260 md5_ctx ctx;
261 size_t tl, i;
262 char *user, *cp;
264 for (tl = strlen(ts);;) {
265 user = xuser;
266 if (! getcredentials(&user, &pass))
267 break;
269 md5_init(&ctx);
270 md5_update(&ctx, (unsigned char*)UNCONST(ts), tl);
271 md5_update(&ctx, (unsigned char*)pass, strlen(pass));
272 md5_final(digest, &ctx);
273 md5tohex(hex, digest);
275 i = strlen(user);
276 cp = ac_alloc(5 + i + 1 + MD5TOHEX_SIZE + 3);
278 memcpy(cp, "APOP ", 5);
279 memcpy(cp + 5, user, i);
280 i += 5;
281 cp[i++] = ' ';
282 memcpy(cp + i, hex, MD5TOHEX_SIZE);
283 i += MD5TOHEX_SIZE;
284 memcpy(cp + i, "\r\n\0", 3);
285 POP3_XOUT(cp, MB_COMD, goto jcont);
286 POP3_XANSWER(goto jcont);
287 rv = OKAY;
288 jcont:
289 ac_free(cp);
290 if (rv == OKAY)
291 break;
292 pass = NULL;
294 return rv;
296 #endif /* HAVE_MD5 */
298 static enum okay
299 _pop3_auth_plain(struct mailbox *mp, char *xuser, char *pass)
301 enum okay rv = STOP;
302 char *user, *cp;
303 size_t ul, pl;
305 /* The USER/PASS plain text version */
306 for (;;) {
307 user = xuser;
308 if (! getcredentials(&user, &pass))
309 break;
311 ul = strlen(user);
312 pl = strlen(pass);
313 cp = ac_alloc(MAX(ul, pl) + 5 + 2 +1);
315 memcpy(cp, "USER ", 5);
316 memcpy(cp + 5, user, ul);
317 memcpy(cp + 5 + ul, "\r\n\0", 3);
318 POP3_XOUT(cp, MB_COMD, goto jcont);
319 POP3_XANSWER(goto jcont);
321 memcpy(cp, "PASS ", 5);
322 memcpy(cp + 5, pass, pl);
323 memcpy(cp + 5 + pl, "\r\n\0", 3);
324 POP3_XOUT(cp, MB_COMD, goto jcont);
325 POP3_XANSWER(goto jcont);
326 rv = OKAY;
327 jcont:
328 ac_free(cp);
329 if (rv == OKAY)
330 break;
331 pass = NULL;
333 return rv;
336 static void
337 pop3_timer_off(void)
339 if (_pop3_keepalive > 0) {
340 alarm(0);
341 safe_signal(SIGALRM, _pop3_savealrm);
345 static enum okay
346 pop3_answer(struct mailbox *mp)
348 int sz;
349 enum okay ok = STOP;
351 retry:
352 if ((sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock))
353 > 0) {
354 if ((mp->mb_active & (MB_COMD|MB_MULT)) == MB_MULT)
355 goto multiline;
356 if (options & OPT_VERBOSE)
357 fputs(_pop3_buf, stderr);
358 switch (*_pop3_buf) {
359 case '+':
360 ok = OKAY;
361 mp->mb_active &= ~MB_COMD;
362 break;
363 case '-':
364 ok = STOP;
365 mp->mb_active = MB_NONE;
366 fprintf(stderr, tr(218, "POP3 error: %s"), _pop3_buf);
367 break;
368 default:
370 * If the answer starts neither with '+' nor with
371 * '-', it must be part of a multiline response,
372 * e. g. because the user interrupted a file
373 * download. Get lines until a single dot appears.
375 multiline:
376 while (_pop3_buf[0] != '.' || _pop3_buf[1] != '\r' ||
377 _pop3_buf[2] != '\n' ||
378 _pop3_buf[3] != '\0') {
379 sz = sgetline(&_pop3_buf, &_pop3_bufsize,
380 NULL, &mp->mb_sock);
381 if (sz <= 0)
382 goto eof;
384 mp->mb_active &= ~MB_MULT;
385 if (mp->mb_active != MB_NONE)
386 goto retry;
388 } else {
389 eof: ok = STOP;
390 mp->mb_active = MB_NONE;
392 return ok;
395 static enum okay
396 pop3_finish(struct mailbox *mp)
398 while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
399 (void)pop3_answer(mp);
400 return OKAY;
403 static void
404 pop3catch(int s)
406 termios_state_reset();
407 switch (s) {
408 case SIGINT:
409 fprintf(stderr, tr(102, "Interrupt\n"));
410 siglongjmp(_pop3_jmp, 1);
411 break;
412 case SIGPIPE:
413 fprintf(stderr, "Received SIGPIPE during POP3 operation\n");
414 break;
418 static void
419 _pop3_maincatch(int s)
421 (void)s;
422 if (interrupts++ == 0) {
423 fprintf(stderr, tr(102, "Interrupt\n"));
424 return;
426 onintr(0);
429 static enum okay
430 pop3_noop1(struct mailbox *mp)
432 POP3_OUT("NOOP\r\n", MB_COMD)
433 POP3_ANSWER()
434 return OKAY;
437 FL enum okay
438 pop3_noop(void)
440 sighandler_type volatile saveint, savepipe;
441 enum okay ok = STOP;
443 _pop3_lock = 1;
444 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
445 safe_signal(SIGINT, &_pop3_maincatch);
446 savepipe = safe_signal(SIGPIPE, SIG_IGN);
447 if (sigsetjmp(_pop3_jmp, 1) == 0) {
448 if (savepipe != SIG_IGN)
449 safe_signal(SIGPIPE, pop3catch);
450 ok = pop3_noop1(&mb);
452 safe_signal(SIGINT, saveint);
453 safe_signal(SIGPIPE, savepipe);
454 _pop3_lock = 0;
455 return ok;
458 /*ARGSUSED*/
459 static void
460 pop3alarm(int s)
462 sighandler_type volatile saveint, savepipe;
463 UNUSED(s);
465 if (_pop3_lock++ == 0) {
466 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
467 safe_signal(SIGINT, &_pop3_maincatch);
468 savepipe = safe_signal(SIGPIPE, SIG_IGN);
469 if (sigsetjmp(_pop3_jmp, 1)) {
470 safe_signal(SIGINT, saveint);
471 safe_signal(SIGPIPE, savepipe);
472 goto brk;
474 if (savepipe != SIG_IGN)
475 safe_signal(SIGPIPE, pop3catch);
476 if (pop3_noop1(&mb) != OKAY) {
477 safe_signal(SIGINT, saveint);
478 safe_signal(SIGPIPE, savepipe);
479 goto out;
481 safe_signal(SIGINT, saveint);
482 safe_signal(SIGPIPE, savepipe);
484 brk:
485 alarm(_pop3_keepalive);
486 out:
487 --_pop3_lock;
490 static enum okay
491 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
493 char *cp;
494 enum okay ok = OKAY;
496 POP3_OUT("STAT\r\n", MB_COMD)
497 POP3_ANSWER()
498 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
500 while (*cp != '\0' && spacechar(*cp))
501 ++cp;
503 if (*cp != '\0') {
504 *cnt = (int)strtol(cp, NULL, 10);
505 while (*cp != '\0' && !spacechar(*cp))
506 ++cp;
507 while (*cp != '\0' && spacechar(*cp))
508 ++cp;
509 if (*cp != '\0')
510 *size = (int)strtol(cp, NULL, 10);
511 else
512 ok = STOP;
513 } else
514 ok = STOP;
516 if (ok == STOP)
517 fprintf(stderr, tr(260, "invalid POP3 STAT response: %s\n"),
518 _pop3_buf);
519 return ok;
522 static enum okay
523 pop3_list(struct mailbox *mp, int n, size_t *size)
525 char o[LINESIZE], *cp;
527 snprintf(o, sizeof o, "LIST %u\r\n", n);
528 POP3_OUT(o, MB_COMD)
529 POP3_ANSWER()
530 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
532 while (*cp != '\0' && spacechar(*cp))
533 ++cp;
534 while (*cp != '\0' && !spacechar(*cp))
535 ++cp;
536 while (*cp != '\0' && spacechar(*cp))
537 ++cp;
538 if (*cp != '\0')
539 *size = (size_t)strtol(cp, NULL, 10);
540 else
541 *size = 0;
542 return OKAY;
545 static void
546 pop3_init(struct mailbox *mp, int n)
548 struct message *m = &message[n];
549 char *cp;
551 m->m_flag = MUSED|MNEW|MNOFROM|MNEWEST;
552 m->m_block = 0;
553 m->m_offset = 0;
554 pop3_list(mp, m - message + 1, &m->m_xsize);
555 if ((cp = hfield1("status", m)) != NULL) {
556 while (*cp != '\0') {
557 if (*cp == 'R')
558 m->m_flag |= MREAD;
559 else if (*cp == 'O')
560 m->m_flag &= ~MNEW;
561 cp++;
566 /*ARGSUSED*/
567 static void
568 pop3_dates(struct mailbox *mp)
570 int i;
571 (void)mp;
573 for (i = 0; i < msgCount; i++)
574 substdate(&message[i]);
577 static void
578 pop3_setptr(struct mailbox *mp)
580 int i;
582 message = scalloc(msgCount + 1, sizeof *message);
583 for (i = 0; i < msgCount; i++)
584 pop3_init(mp, i);
585 setdot(message);
586 message[msgCount].m_size = 0;
587 message[msgCount].m_lines = 0;
588 pop3_dates(mp);
591 FL int
592 pop3_setfile(const char *server, int nmail, int isedit)
594 struct sock so;
595 sighandler_type saveint;
596 sighandler_type savepipe;
597 char * volatile user, * volatile pass;
598 const char *cp, *uhp, * volatile sp = server;
599 int use_ssl = 0;
601 if (nmail)
602 return 1;
603 if (strncmp(sp, "pop3://", 7) == 0) {
604 sp = &sp[7];
605 use_ssl = 0;
606 #ifdef HAVE_SSL
607 } else if (strncmp(sp, "pop3s://", 8) == 0) {
608 sp = &sp[8];
609 use_ssl = 1;
610 #endif
612 uhp = sp;
613 pass = lookup_password_for_token(uhp);
614 if ((cp = last_at_before_slash(sp)) != NULL) {
615 user = salloc(cp - sp + 1);
616 memcpy(user, sp, cp - sp);
617 user[cp - sp] = '\0';
618 sp = &cp[1];
619 user = urlxdec(user);
620 } else
621 user = NULL;
622 if (sopen(sp, &so, use_ssl, uhp, (use_ssl ? "pop3s" : "pop3"))
623 != OKAY) {
624 return -1;
626 quit();
627 edit = (isedit != 0);
628 if (mb.mb_sock.s_fd >= 0)
629 sclose(&mb.mb_sock);
630 if (mb.mb_itf) {
631 fclose(mb.mb_itf);
632 mb.mb_itf = NULL;
634 if (mb.mb_otf) {
635 fclose(mb.mb_otf);
636 mb.mb_otf = NULL;
638 initbox(server);
639 mb.mb_type = MB_VOID;
640 _pop3_lock = 1;
641 mb.mb_sock = so;
642 saveint = safe_signal(SIGINT, SIG_IGN);
643 savepipe = safe_signal(SIGPIPE, SIG_IGN);
644 if (sigsetjmp(_pop3_jmp, 1)) {
645 sclose(&mb.mb_sock);
646 safe_signal(SIGINT, saveint);
647 safe_signal(SIGPIPE, savepipe);
648 _pop3_lock = 0;
649 return 1;
651 if (saveint != SIG_IGN)
652 safe_signal(SIGINT, pop3catch);
653 if (savepipe != SIG_IGN)
654 safe_signal(SIGPIPE, pop3catch);
655 if ((cp = ok_vlook(pop3_keepalive)) != NULL) {
656 if ((_pop3_keepalive = (int)strtol(cp, NULL, 10)) > 0) {
657 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
658 alarm(_pop3_keepalive);
661 mb.mb_sock.s_desc = "POP3";
662 mb.mb_sock.s_onclose = pop3_timer_off;
663 if (_pop3_login(&mb, user, pass, uhp, sp) != OKAY ||
664 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
665 sclose(&mb.mb_sock);
666 pop3_timer_off();
667 safe_signal(SIGINT, saveint);
668 safe_signal(SIGPIPE, savepipe);
669 _pop3_lock = 0;
670 return 1;
672 mb.mb_type = MB_POP3;
673 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
674 pop3_setptr(&mb);
675 setmsize(msgCount);
676 sawcom = FAL0;
677 safe_signal(SIGINT, saveint);
678 safe_signal(SIGPIPE, savepipe);
679 _pop3_lock = 0;
680 if (!edit && msgCount == 0) {
681 if (mb.mb_type == MB_POP3 && !ok_blook(emptystart))
682 fprintf(stderr, tr(258, "No mail at %s\n"), server);
683 return 1;
685 return 0;
688 static enum okay
689 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
691 sighandler_type volatile saveint = SIG_IGN, savepipe = SIG_IGN;
692 off_t offset;
693 char o[LINESIZE], *line = NULL, *lp;
694 size_t linesize = 0, linelen, size;
695 int number = m - message + 1, emptyline = 0, lines;
697 if (mp->mb_sock.s_fd < 0) {
698 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
699 return STOP;
701 if (_pop3_lock++ == 0) {
702 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
703 safe_signal(SIGINT, &_pop3_maincatch);
704 savepipe = safe_signal(SIGPIPE, SIG_IGN);
705 if (sigsetjmp(_pop3_jmp, 1)) {
706 safe_signal(SIGINT, saveint);
707 safe_signal(SIGPIPE, savepipe);
708 _pop3_lock--;
709 return STOP;
711 if (savepipe != SIG_IGN)
712 safe_signal(SIGPIPE, pop3catch);
714 fseek(mp->mb_otf, 0L, SEEK_END);
715 offset = ftell(mp->mb_otf);
716 retry: switch (need) {
717 case NEED_HEADER:
718 snprintf(o, sizeof o, "TOP %u 0\r\n", number);
719 break;
720 case NEED_BODY:
721 snprintf(o, sizeof o, "RETR %u\r\n", number);
722 break;
723 case NEED_UNSPEC:
724 abort();
726 POP3_OUT(o, MB_COMD|MB_MULT)
727 if (pop3_answer(mp) == STOP) {
728 if (need == NEED_HEADER) {
730 * The TOP POP3 command is optional, so retry
731 * with the entire message.
733 need = NEED_BODY;
734 goto retry;
736 if (interrupts)
737 onintr(0);
738 return STOP;
740 size = 0;
741 lines = 0;
742 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
743 if (line[0] == '.' && line[1] == '\r' && line[2] == '\n' &&
744 line[3] == '\0') {
745 mp->mb_active &= ~MB_MULT;
746 break;
748 if (line[0] == '.') {
749 lp = &line[1];
750 linelen--;
751 } else
752 lp = line;
753 /* TODO >>
754 * Need to mask 'From ' lines. This cannot be done properly
755 * since some servers pass them as 'From ' and others as
756 * '>From '. Although one could identify the first kind of
757 * server in principle, it is not possible to identify the
758 * second as '>From ' may also come from a server of the
759 * first type as actual data. So do what is absolutely
760 * necessary only - mask 'From '.
762 * If the line is the first line of the message header, it
763 * is likely a real 'From ' line. In this case, it is just
764 * ignored since it violates all standards.
765 * TODO i have *never* seen the latter?!?!?
766 * TODO <<
769 * Since we simply copy over data without doing any transfer
770 * encoding reclassification/adjustment we *have* to perform
771 * RFC 4155 compliant From_ quoting here
773 if (is_head(lp, linelen)) {
774 if (lines == 0)
775 continue;
776 fputc('>', mp->mb_otf);
777 ++size;
779 lines++;
780 if (lp[linelen-1] == '\n' && (linelen == 1 ||
781 lp[linelen-2] == '\r')) {
782 emptyline = linelen <= 2;
783 if (linelen > 2)
784 fwrite(lp, 1, linelen - 2, mp->mb_otf);
785 fputc('\n', mp->mb_otf);
786 size += linelen - 1;
787 } else {
788 emptyline = 0;
789 fwrite(lp, 1, linelen, mp->mb_otf);
790 size += linelen;
793 if (!emptyline) {
795 * This is very ugly; but some POP3 daemons don't end a
796 * message with \r\n\r\n, and we need \n\n for mbox format.
798 fputc('\n', mp->mb_otf);
799 lines++;
800 size++;
802 m->m_size = size;
803 m->m_lines = lines;
804 m->m_block = mailx_blockof(offset);
805 m->m_offset = mailx_offsetof(offset);
806 fflush(mp->mb_otf);
807 switch (need) {
808 case NEED_HEADER:
809 m->m_have |= HAVE_HEADER;
810 break;
811 case NEED_BODY:
812 m->m_have |= HAVE_HEADER|HAVE_BODY;
813 m->m_xlines = m->m_lines;
814 m->m_xsize = m->m_size;
815 break;
816 case NEED_UNSPEC:
817 break;
819 if (line)
820 free(line);
821 if (saveint != SIG_IGN)
822 safe_signal(SIGINT, saveint);
823 if (savepipe != SIG_IGN)
824 safe_signal(SIGPIPE, savepipe);
825 _pop3_lock--;
826 if (interrupts)
827 onintr(0);
828 return OKAY;
831 FL enum okay
832 pop3_header(struct message *m)
834 return pop3_get(&mb, m,
835 ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER);
838 FL enum okay
839 pop3_body(struct message *m)
841 return pop3_get(&mb, m, NEED_BODY);
844 static enum okay
845 pop3_exit(struct mailbox *mp)
847 POP3_OUT("QUIT\r\n", MB_COMD)
848 POP3_ANSWER()
849 return OKAY;
852 static enum okay
853 pop3_delete(struct mailbox *mp, int n)
855 char o[LINESIZE];
857 snprintf(o, sizeof o, "DELE %u\r\n", n);
858 POP3_OUT(o, MB_COMD)
859 POP3_ANSWER()
860 return OKAY;
863 static enum okay
864 pop3_update(struct mailbox *mp)
866 struct message *m;
867 int dodel, c, gotcha, held;
869 if (!edit) {
870 holdbits();
871 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
872 if (m->m_flag & MBOX)
873 c++;
875 if (c > 0)
876 makembox();
878 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
879 if (edit) {
880 dodel = m->m_flag & MDELETED;
881 } else {
882 dodel = !((m->m_flag&MPRESERVE) ||
883 (m->m_flag&MTOUCH) == 0);
885 if (dodel) {
886 pop3_delete(mp, m - message + 1);
887 gotcha++;
888 } else
889 held++;
891 if (gotcha && edit) {
892 printf(tr(168, "\"%s\" "), displayname);
893 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
894 ? tr(170, "complete\n") : tr(212, "updated.\n"));
895 } else if (held && !edit) {
896 if (held == 1)
897 printf(tr(155, "Held 1 message in %s\n"), displayname);
898 else
899 printf(tr(156, "Held %d messages in %s\n"), held,
900 displayname);
902 fflush(stdout);
903 return OKAY;
906 FL void
907 pop3_quit(void)
909 sighandler_type volatile saveint, savepipe;
911 if (mb.mb_sock.s_fd < 0) {
912 fprintf(stderr, tr(219,
913 "POP3 connection already closed.\n"));
914 return;
916 _pop3_lock = 1;
917 saveint = safe_signal(SIGINT, SIG_IGN);
918 savepipe = safe_signal(SIGPIPE, SIG_IGN);
919 if (sigsetjmp(_pop3_jmp, 1)) {
920 safe_signal(SIGINT, saveint);
921 safe_signal(SIGPIPE, saveint);
922 _pop3_lock = 0;
923 return;
925 if (saveint != SIG_IGN)
926 safe_signal(SIGINT, pop3catch);
927 if (savepipe != SIG_IGN)
928 safe_signal(SIGPIPE, pop3catch);
929 pop3_update(&mb);
930 pop3_exit(&mb);
931 sclose(&mb.mb_sock);
932 safe_signal(SIGINT, saveint);
933 safe_signal(SIGPIPE, savepipe);
934 _pop3_lock = 0;
936 #endif /* HAVE_POP3 */