Build system: detect/set CC and CFLAGS, _buh -> devel, plus..
[s-mailx.git] / pop3.c
blobf636f7b1ae910530b86f60f9186ad15275788d1e
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 - 2013 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 #include "config.h"
42 #ifndef HAVE_POP3
43 typedef int avoid_empty_file_compiler_warning;
44 #else
45 #include "nail.h"
47 #ifdef HAVE_MD5
48 # include "md5.h"
49 #endif
51 #define POP3_ANSWER() POP3_XANSWER(return STOP);
52 #define POP3_XANSWER(ACTIONSTOP) \
54 if (pop3_answer(mp) == STOP) {\
55 ACTIONSTOP;\
59 #define POP3_OUT(X,Y) POP3_XOUT(X, Y, return STOP);
60 #define POP3_XOUT(X,Y,ACTIONSTOP) \
62 if (pop3_finish(mp) == STOP) {\
63 ACTIONSTOP;\
65 if (options & OPT_VERBOSE)\
66 fprintf(stderr, ">>> %s", X);\
67 mp->mb_active |= Y;\
68 if (swrite(&mp->mb_sock, X) == STOP) {\
69 ACTIONSTOP;\
73 static char *pop3buf;
74 static size_t pop3bufsize;
75 static sigjmp_buf pop3jmp;
76 static sighandler_type savealrm;
77 static int pop3keepalive;
78 static volatile int pop3lock;
80 /* Perform entire login handshake */
81 static enum okay _pop3_login(struct mailbox *mp, char *xuser, char *pass,
82 char const *uhp, char const *xserver);
84 /* APOP: get greeting credential or NULL */
85 #ifdef HAVE_MD5
86 static char * _pop3_lookup_apop_timestamp(char const *bp);
87 #endif
89 static bool_t _pop3_use_starttls(char const *uhp);
91 /* APOP: shall we use it (for *uhp*)? */
92 static bool_t _pop3_no_apop(char const *uhp);
94 /* Several authentication methods */
95 static enum okay _pop3_auth_plain(struct mailbox *mp, char *xuser,
96 char *pass);
97 #ifdef HAVE_MD5
98 static enum okay _pop3_auth_apop(struct mailbox *mp, char *xuser,
99 char *pass, char const *ts);
100 #endif
102 static void pop3_timer_off(void);
103 static enum okay pop3_answer(struct mailbox *mp);
104 static enum okay pop3_finish(struct mailbox *mp);
105 static void pop3catch(int s);
106 static void maincatch(int s);
107 static enum okay pop3_noop1(struct mailbox *mp);
108 static void pop3alarm(int s);
109 static enum okay pop3_stat(struct mailbox *mp, off_t *size, int *cnt);
110 static enum okay pop3_list(struct mailbox *mp, int n, size_t *size);
111 static void pop3_init(struct mailbox *mp, int n);
112 static void pop3_dates(struct mailbox *mp);
113 static void pop3_setptr(struct mailbox *mp);
114 static enum okay pop3_get(struct mailbox *mp, struct message *m,
115 enum needspec need);
116 static enum okay pop3_exit(struct mailbox *mp);
117 static enum okay pop3_delete(struct mailbox *mp, int n);
118 static enum okay pop3_update(struct mailbox *mp);
120 static enum okay
121 _pop3_login(struct mailbox *mp, char *xuser, char *pass, char const *uhp,
122 char const *xserver)
124 #ifdef HAVE_MD5
125 char *ts;
126 #endif
127 char *cp;
128 enum okay rv = STOP;
130 /* Get the greeting, check wether APOP is advertised */
131 POP3_XANSWER(goto jleave);
132 #ifdef HAVE_MD5
133 ts = _pop3_lookup_apop_timestamp(pop3buf);
134 #endif
136 if ((cp = strchr(xserver, ':')) != NULL) { /* TODO GENERIC URI PARSE! */
137 size_t l = (size_t)(cp - xserver);
138 char *x = salloc(l + 1);
139 memcpy(x, xserver, l);
140 x[l] = '\0';
141 xserver = x;
144 /* If not yet secured, can we upgrade to TLS? */
145 #ifdef HAVE_SSL
146 if (mp->mb_sock.s_use_ssl == 0 && _pop3_use_starttls(uhp)) {
147 POP3_XOUT("STLS\r\n", MB_COMD, goto jleave);
148 POP3_XANSWER(goto jleave);
149 if (ssl_open(xserver, &mp->mb_sock, uhp) != OKAY)
150 goto jleave;
152 #else
153 if (_pop3_use_starttls(uhp)) {
154 fprintf(stderr, "No SSL support compiled in.\n");
155 goto jleave;
157 #endif
159 /* Use the APOP single roundtrip? */
160 if (! _pop3_no_apop(uhp)) {
161 #ifdef HAVE_MD5
162 if (ts != NULL) {
163 rv = _pop3_auth_apop(mp, xuser, pass, ts);
164 if (rv != OKAY)
165 fprintf(stderr, tr(276,
166 "POP3 `APOP' authentication failed, "
167 "maybe try setting *pop3-no-apop*\n"));
168 goto jleave;
169 } else
170 #endif
171 if (options & OPT_VERBOSE)
172 fprintf(stderr, tr(204, "No POP3 `APOP' support "
173 "available, sending password in clear text\n"));
175 rv = _pop3_auth_plain(mp, xuser, pass);
176 jleave:
177 return rv;
180 static char *
181 _pop3_lookup_apop_timestamp(char const *bp)
184 * RFC 1939:
185 * A POP3 server which implements the APOP command will include
186 * a timestamp in its banner greeting. The syntax of the timestamp
187 * corresponds to the `msg-id' in [RFC822]
188 * RFC 822:
189 * msg-id = "<" addr-spec ">"
190 * addr-spec = local-part "@" domain
192 char const *cp, *ep;
193 size_t tl;
194 char *rp = NULL;
195 bool_t hadat = FAL0;
197 if ((cp = strchr(bp, '<')) == NULL)
198 goto jleave;
200 /* xxx What about malformed APOP timestamp (<@>) here? */
201 for (ep = cp; *ep; ep++) {
202 if (spacechar(*ep))
203 goto jleave;
204 else if (*ep == '@')
205 hadat = TRU1;
206 else if (*ep == '>') {
207 if (! hadat)
208 goto jleave;
209 break;
212 if (*ep != '>')
213 goto jleave;
215 tl = (size_t)(++ep - cp);
216 rp = salloc(tl + 1);
217 memcpy(rp, cp, tl);
218 rp[tl] = '\0';
219 jleave:
220 return rp;
223 static bool_t
224 _pop3_use_starttls(char const *uhp)
226 char *var;
228 if (value("pop3-use-starttls"))
229 return TRU1;
230 var = savecat("pop3-use-starttls-", uhp);
231 return value(var) != NULL;
234 static bool_t
235 _pop3_no_apop(char const *uhp)
237 bool_t ret;
239 if (! (ret = boption("pop3-no-apop"))) {
240 #define __S "pop3-no-apop-"
241 #define __SL sizeof(__S)
242 size_t i = strlen(uhp);
243 char *var = ac_alloc(i + __SL);
244 memcpy(var, __S, __SL - 1);
245 memcpy(var + __SL - 1, uhp, i + 1);
246 ret = boption(var);
247 ac_free(var);
248 #undef __SL
249 #undef __S
251 return ret;
254 #ifdef HAVE_MD5
255 static enum okay
256 _pop3_auth_apop(struct mailbox *mp, char *xuser, char *pass, char const *ts)
258 enum okay rv = STOP;
259 unsigned char digest[16];
260 char hex[MD5TOHEX_SIZE];
261 MD5_CTX ctx;
262 size_t tl, i;
263 char *user, *cp;
265 for (tl = strlen(ts);;) {
266 user = xuser;
267 if (! getcredentials(&user, &pass))
268 break;
270 MD5Init(&ctx);
271 MD5Update(&ctx, (unsigned char*)UNCONST(ts), tl);
272 MD5Update(&ctx, (unsigned char*)pass, strlen(pass));
273 MD5Final(digest, &ctx);
274 md5tohex(hex, digest);
276 i = strlen(user);
277 cp = ac_alloc(5 + i + 1 + MD5TOHEX_SIZE + 3);
279 memcpy(cp, "APOP ", 5);
280 memcpy(cp + 5, user, i);
281 i += 5;
282 cp[i++] = ' ';
283 memcpy(cp + i, hex, MD5TOHEX_SIZE);
284 i += MD5TOHEX_SIZE;
285 memcpy(cp + i, "\r\n\0", 3);
286 POP3_XOUT(cp, MB_COMD, goto jcont);
287 POP3_XANSWER(goto jcont);
288 rv = OKAY;
289 jcont:
290 ac_free(cp);
291 if (rv == OKAY)
292 break;
293 pass = NULL;
295 return rv;
297 #endif /* HAVE_MD5 */
299 static enum okay
300 _pop3_auth_plain(struct mailbox *mp, char *xuser, char *pass)
302 enum okay rv = STOP;
303 char *user, *cp;
304 size_t ul, pl;
306 /* The USER/PASS plain text version */
307 for (;;) {
308 user = xuser;
309 if (! getcredentials(&user, &pass))
310 break;
312 ul = strlen(user);
313 pl = strlen(pass);
314 cp = ac_alloc(MAX(ul, pl) + 5 + 2 +1);
316 memcpy(cp, "USER ", 5);
317 memcpy(cp + 5, user, ul);
318 memcpy(cp + 5 + ul, "\r\n\0", 3);
319 POP3_XOUT(cp, MB_COMD, goto jcont);
320 POP3_XANSWER(goto jcont);
322 memcpy(cp, "PASS ", 5);
323 memcpy(cp + 5, pass, pl);
324 memcpy(cp + 5 + pl, "\r\n\0", 3);
325 POP3_XOUT(cp, MB_COMD, goto jcont);
326 POP3_XANSWER(goto jcont);
327 rv = OKAY;
328 jcont:
329 ac_free(cp);
330 if (rv == OKAY)
331 break;
332 pass = NULL;
334 return rv;
337 static void
338 pop3_timer_off(void)
340 if (pop3keepalive > 0) {
341 alarm(0);
342 safe_signal(SIGALRM, savealrm);
346 static enum okay
347 pop3_answer(struct mailbox *mp)
349 int sz;
350 enum okay ok = STOP;
352 retry: if ((sz = sgetline(&pop3buf, &pop3bufsize, NULL, &mp->mb_sock)) > 0) {
353 if ((mp->mb_active & (MB_COMD|MB_MULT)) == MB_MULT)
354 goto multiline;
355 if (options & OPT_VERBOSE)
356 fputs(pop3buf, stderr);
357 switch (*pop3buf) {
358 case '+':
359 ok = OKAY;
360 mp->mb_active &= ~MB_COMD;
361 break;
362 case '-':
363 ok = STOP;
364 mp->mb_active = MB_NONE;
365 fprintf(stderr, tr(218, "POP3 error: %s"), pop3buf);
366 break;
367 default:
369 * If the answer starts neither with '+' nor with
370 * '-', it must be part of a multiline response,
371 * e. g. because the user interrupted a file
372 * download. Get lines until a single dot appears.
374 multiline: while (pop3buf[0] != '.' || pop3buf[1] != '\r' ||
375 pop3buf[2] != '\n' ||
376 pop3buf[3] != '\0') {
377 sz = sgetline(&pop3buf, &pop3bufsize,
378 NULL, &mp->mb_sock);
379 if (sz <= 0)
380 goto eof;
382 mp->mb_active &= ~MB_MULT;
383 if (mp->mb_active != MB_NONE)
384 goto retry;
386 } else {
387 eof: ok = STOP;
388 mp->mb_active = MB_NONE;
390 return ok;
393 static enum okay
394 pop3_finish(struct mailbox *mp)
396 while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
397 (void)pop3_answer(mp);
398 return OKAY;
401 static void
402 pop3catch(int s)
404 termios_state_reset();
405 switch (s) {
406 case SIGINT:
407 fprintf(stderr, tr(102, "Interrupt\n"));
408 siglongjmp(pop3jmp, 1);
409 break;
410 case SIGPIPE:
411 fprintf(stderr, "Received SIGPIPE during POP3 operation\n");
412 break;
416 static void
417 maincatch(int s)
419 (void)s;
420 if (interrupts++ == 0) {
421 fprintf(stderr, tr(102, "Interrupt\n"));
422 return;
424 onintr(0);
427 static enum okay
428 pop3_noop1(struct mailbox *mp)
430 POP3_OUT("NOOP\r\n", MB_COMD)
431 POP3_ANSWER()
432 return OKAY;
435 enum okay
436 pop3_noop(void)
438 enum okay ok = STOP;
439 sighandler_type saveint, savepipe;
441 (void)&saveint;
442 (void)&savepipe;
443 (void)&ok;
444 pop3lock = 1;
445 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
446 safe_signal(SIGINT, maincatch);
447 savepipe = safe_signal(SIGPIPE, SIG_IGN);
448 if (sigsetjmp(pop3jmp, 1) == 0) {
449 if (savepipe != SIG_IGN)
450 safe_signal(SIGPIPE, pop3catch);
451 ok = pop3_noop1(&mb);
453 safe_signal(SIGINT, saveint);
454 safe_signal(SIGPIPE, savepipe);
455 pop3lock = 0;
456 return ok;
459 /*ARGSUSED*/
460 static void
461 pop3alarm(int s)
463 sighandler_type saveint;
464 sighandler_type savepipe;
465 (void)s;
467 if (pop3lock++ == 0) {
468 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
469 safe_signal(SIGINT, maincatch);
470 savepipe = safe_signal(SIGPIPE, SIG_IGN);
471 if (sigsetjmp(pop3jmp, 1)) {
472 safe_signal(SIGINT, saveint);
473 safe_signal(SIGPIPE, savepipe);
474 goto brk;
476 if (savepipe != SIG_IGN)
477 safe_signal(SIGPIPE, pop3catch);
478 if (pop3_noop1(&mb) != OKAY) {
479 safe_signal(SIGINT, saveint);
480 safe_signal(SIGPIPE, savepipe);
481 goto out;
483 safe_signal(SIGINT, saveint);
484 safe_signal(SIGPIPE, savepipe);
486 brk: alarm(pop3keepalive);
487 out: pop3lock--;
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 = pop3buf; *cp && !spacechar(*cp & 0377); cp++);
499 while (*cp && spacechar(*cp & 0377))
500 cp++;
501 if (*cp) {
502 *cnt = (int)strtol(cp, NULL, 10);
503 while (*cp && !spacechar(*cp & 0377))
504 cp++;
505 while (*cp && spacechar(*cp & 0377))
506 cp++;
507 if (*cp)
508 *size = (int)strtol(cp, NULL, 10);
509 else
510 ok = STOP;
511 } else
512 ok = STOP;
513 if (ok == STOP)
514 fprintf(stderr, tr(260, "invalid POP3 STAT response: %s\n"),
515 pop3buf);
516 return ok;
519 static enum okay
520 pop3_list(struct mailbox *mp, int n, size_t *size)
522 char o[LINESIZE], *cp;
524 snprintf(o, sizeof o, "LIST %u\r\n", n);
525 POP3_OUT(o, MB_COMD)
526 POP3_ANSWER()
527 for (cp = pop3buf; *cp && !spacechar(*cp & 0377); cp++);
528 while (*cp && spacechar(*cp & 0377))
529 cp++;
530 while (*cp && !spacechar(*cp & 0377))
531 cp++;
532 while (*cp && spacechar(*cp & 0377))
533 cp++;
534 if (*cp)
535 *size = (size_t)strtol(cp, NULL, 10);
536 else
537 *size = 0;
538 return OKAY;
541 static void
542 pop3_init(struct mailbox *mp, int n)
544 struct message *m = &message[n];
545 char *cp;
547 m->m_flag = MUSED|MNEW|MNOFROM|MNEWEST;
548 m->m_block = 0;
549 m->m_offset = 0;
550 pop3_list(mp, m - message + 1, &m->m_xsize);
551 if ((cp = hfield1("status", m)) != NULL) {
552 while (*cp != '\0') {
553 if (*cp == 'R')
554 m->m_flag |= MREAD;
555 else if (*cp == 'O')
556 m->m_flag &= ~MNEW;
557 cp++;
562 /*ARGSUSED*/
563 static void
564 pop3_dates(struct mailbox *mp)
566 int i;
567 (void)mp;
569 for (i = 0; i < msgCount; i++)
570 substdate(&message[i]);
573 static void
574 pop3_setptr(struct mailbox *mp)
576 int i;
578 message = scalloc(msgCount + 1, sizeof *message);
579 for (i = 0; i < msgCount; i++)
580 pop3_init(mp, i);
581 setdot(message);
582 message[msgCount].m_size = 0;
583 message[msgCount].m_lines = 0;
584 pop3_dates(mp);
588 pop3_setfile(const char *server, int nmail, int isedit)
590 struct sock so;
591 sighandler_type saveint;
592 sighandler_type savepipe;
593 char *volatile user, *pass;
594 const char *cp, *uhp, *volatile sp = server;
595 int use_ssl = 0;
597 if (nmail)
598 return 1;
599 if (strncmp(sp, "pop3://", 7) == 0) {
600 sp = &sp[7];
601 use_ssl = 0;
602 #ifdef HAVE_SSL
603 } else if (strncmp(sp, "pop3s://", 8) == 0) {
604 sp = &sp[8];
605 use_ssl = 1;
606 #endif
608 uhp = sp;
609 pass = lookup_password_for_token(uhp);
610 if ((cp = last_at_before_slash(sp)) != NULL) {
611 user = salloc(cp - sp + 1);
612 memcpy(user, sp, cp - sp);
613 user[cp - sp] = '\0';
614 sp = &cp[1];
615 user = urlxdec(user);
616 } else
617 user = NULL;
618 if (sopen(sp, &so, use_ssl, uhp, use_ssl ? "pop3s" : "pop3",
619 (options & OPT_VERBOSE) != 0) != OKAY) {
620 return -1;
622 quit();
623 edit = (isedit != 0);
624 if (mb.mb_sock.s_fd >= 0)
625 sclose(&mb.mb_sock);
626 if (mb.mb_itf) {
627 fclose(mb.mb_itf);
628 mb.mb_itf = NULL;
630 if (mb.mb_otf) {
631 fclose(mb.mb_otf);
632 mb.mb_otf = NULL;
634 initbox(server);
635 mb.mb_type = MB_VOID;
636 pop3lock = 1;
637 mb.mb_sock = so;
638 saveint = safe_signal(SIGINT, SIG_IGN);
639 savepipe = safe_signal(SIGPIPE, SIG_IGN);
640 if (sigsetjmp(pop3jmp, 1)) {
641 sclose(&mb.mb_sock);
642 safe_signal(SIGINT, saveint);
643 safe_signal(SIGPIPE, savepipe);
644 pop3lock = 0;
645 return 1;
647 if (saveint != SIG_IGN)
648 safe_signal(SIGINT, pop3catch);
649 if (savepipe != SIG_IGN)
650 safe_signal(SIGPIPE, pop3catch);
651 if ((cp = value("pop3-keepalive")) != NULL) {
652 if ((pop3keepalive = strtol(cp, NULL, 10)) > 0) {
653 savealrm = safe_signal(SIGALRM, pop3alarm);
654 alarm(pop3keepalive);
657 mb.mb_sock.s_desc = "POP3";
658 mb.mb_sock.s_onclose = pop3_timer_off;
659 if (_pop3_login(&mb, user, pass, uhp, sp) != OKAY ||
660 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
661 sclose(&mb.mb_sock);
662 pop3_timer_off();
663 safe_signal(SIGINT, saveint);
664 safe_signal(SIGPIPE, savepipe);
665 pop3lock = 0;
666 return 1;
668 mb.mb_type = MB_POP3;
669 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
670 pop3_setptr(&mb);
671 setmsize(msgCount);
672 sawcom = FAL0;
673 safe_signal(SIGINT, saveint);
674 safe_signal(SIGPIPE, savepipe);
675 pop3lock = 0;
676 if (!edit && msgCount == 0) {
677 if (mb.mb_type == MB_POP3 && value("emptystart") == NULL)
678 fprintf(stderr, tr(258, "No mail at %s\n"), server);
679 return 1;
681 return 0;
684 static enum okay
685 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
687 sighandler_type volatile saveint = SIG_IGN, savepipe = SIG_IGN;
688 off_t offset;
689 char o[LINESIZE], *line = NULL, *lp;
690 size_t linesize = 0, linelen, size;
691 int number = m - message + 1, emptyline = 0, lines;
693 (void)&saveint;
694 (void)&savepipe;
695 (void)&number;
696 (void)&emptyline;
697 (void)&need;
698 if (mp->mb_sock.s_fd < 0) {
699 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
700 return STOP;
702 if (pop3lock++ == 0) {
703 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
704 safe_signal(SIGINT, maincatch);
705 savepipe = safe_signal(SIGPIPE, SIG_IGN);
706 if (sigsetjmp(pop3jmp, 1)) {
707 safe_signal(SIGINT, saveint);
708 safe_signal(SIGPIPE, savepipe);
709 pop3lock--;
710 return STOP;
712 if (savepipe != SIG_IGN)
713 safe_signal(SIGPIPE, pop3catch);
715 fseek(mp->mb_otf, 0L, SEEK_END);
716 offset = ftell(mp->mb_otf);
717 retry: switch (need) {
718 case NEED_HEADER:
719 snprintf(o, sizeof o, "TOP %u 0\r\n", number);
720 break;
721 case NEED_BODY:
722 snprintf(o, sizeof o, "RETR %u\r\n", number);
723 break;
724 case NEED_UNSPEC:
725 abort();
727 POP3_OUT(o, MB_COMD|MB_MULT)
728 if (pop3_answer(mp) == STOP) {
729 if (need == NEED_HEADER) {
731 * The TOP POP3 command is optional, so retry
732 * with the entire message.
734 need = NEED_BODY;
735 goto retry;
737 if (interrupts)
738 onintr(0);
739 return STOP;
741 size = 0;
742 lines = 0;
743 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
744 if (line[0] == '.' && line[1] == '\r' && line[2] == '\n' &&
745 line[3] == '\0') {
746 mp->mb_active &= ~MB_MULT;
747 break;
749 if (line[0] == '.') {
750 lp = &line[1];
751 linelen--;
752 } else
753 lp = line;
754 /* TODO >>
755 * Need to mask 'From ' lines. This cannot be done properly
756 * since some servers pass them as 'From ' and others as
757 * '>From '. Although one could identify the first kind of
758 * server in principle, it is not possible to identify the
759 * second as '>From ' may also come from a server of the
760 * first type as actual data. So do what is absolutely
761 * necessary only - mask 'From '.
763 * If the line is the first line of the message header, it
764 * is likely a real 'From ' line. In this case, it is just
765 * ignored since it violates all standards.
766 * TODO i have *never* seen the latter?!?!?
767 * TODO <<
770 * Since we simply copy over data without doing any transfer
771 * encoding reclassification/adjustment we *have* to perform
772 * RFC 4155 compliant From_ quoting here
774 if (is_head(lp, linelen)) {
775 if (lines == 0)
776 continue;
777 fputc('>', mp->mb_otf);
778 ++size;
780 lines++;
781 if (lp[linelen-1] == '\n' && (linelen == 1 ||
782 lp[linelen-2] == '\r')) {
783 emptyline = linelen <= 2;
784 if (linelen > 2)
785 fwrite(lp, 1, linelen - 2, mp->mb_otf);
786 fputc('\n', mp->mb_otf);
787 size += linelen - 1;
788 } else {
789 emptyline = 0;
790 fwrite(lp, 1, linelen, mp->mb_otf);
791 size += linelen;
794 if (!emptyline) {
796 * This is very ugly; but some POP3 daemons don't end a
797 * message with \r\n\r\n, and we need \n\n for mbox format.
799 fputc('\n', mp->mb_otf);
800 lines++;
801 size++;
803 m->m_size = size;
804 m->m_lines = lines;
805 m->m_block = mailx_blockof(offset);
806 m->m_offset = mailx_offsetof(offset);
807 fflush(mp->mb_otf);
808 switch (need) {
809 case NEED_HEADER:
810 m->m_have |= HAVE_HEADER;
811 break;
812 case NEED_BODY:
813 m->m_have |= HAVE_HEADER|HAVE_BODY;
814 m->m_xlines = m->m_lines;
815 m->m_xsize = m->m_size;
816 break;
817 case NEED_UNSPEC:
818 break;
820 if (line)
821 free(line);
822 if (saveint != SIG_IGN)
823 safe_signal(SIGINT, saveint);
824 if (savepipe != SIG_IGN)
825 safe_signal(SIGPIPE, savepipe);
826 pop3lock--;
827 if (interrupts)
828 onintr(0);
829 return OKAY;
832 enum okay
833 pop3_header(struct message *m)
835 return pop3_get(&mb, m,
836 boption("pop3-bulk-load") ? NEED_BODY : NEED_HEADER);
839 enum okay
840 pop3_body(struct message *m)
842 return pop3_get(&mb, m, NEED_BODY);
845 static enum okay
846 pop3_exit(struct mailbox *mp)
848 POP3_OUT("QUIT\r\n", MB_COMD)
849 POP3_ANSWER()
850 return OKAY;
853 static enum okay
854 pop3_delete(struct mailbox *mp, int n)
856 char o[LINESIZE];
858 snprintf(o, sizeof o, "DELE %u\r\n", n);
859 POP3_OUT(o, MB_COMD)
860 POP3_ANSWER()
861 return OKAY;
864 static enum okay
865 pop3_update(struct mailbox *mp)
867 struct message *m;
868 int dodel, c, gotcha, held;
870 if (!edit) {
871 holdbits();
872 for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
873 if (m->m_flag & MBOX)
874 c++;
876 if (c > 0)
877 makembox();
879 for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
880 if (edit) {
881 dodel = m->m_flag & MDELETED;
882 } else {
883 dodel = !((m->m_flag&MPRESERVE) ||
884 (m->m_flag&MTOUCH) == 0);
886 if (dodel) {
887 pop3_delete(mp, m - message + 1);
888 gotcha++;
889 } else
890 held++;
892 if (gotcha && edit) {
893 printf(tr(168, "\"%s\" "), displayname);
894 printf((value("bsdcompat") || value("bsdmsgs"))
895 ? tr(170, "complete\n") : tr(212, "updated.\n"));
896 } else if (held && !edit) {
897 if (held == 1)
898 printf(tr(155, "Held 1 message in %s\n"), displayname);
899 else
900 printf(tr(156, "Held %d messages in %s\n"), held,
901 displayname);
903 fflush(stdout);
904 return OKAY;
907 void
908 pop3_quit(void)
910 sighandler_type saveint;
911 sighandler_type savepipe;
913 if (mb.mb_sock.s_fd < 0) {
914 fprintf(stderr, tr(219,
915 "POP3 connection already closed.\n"));
916 return;
918 pop3lock = 1;
919 saveint = safe_signal(SIGINT, SIG_IGN);
920 savepipe = safe_signal(SIGPIPE, SIG_IGN);
921 if (sigsetjmp(pop3jmp, 1)) {
922 safe_signal(SIGINT, saveint);
923 safe_signal(SIGPIPE, saveint);
924 pop3lock = 0;
925 return;
927 if (saveint != SIG_IGN)
928 safe_signal(SIGINT, pop3catch);
929 if (savepipe != SIG_IGN)
930 safe_signal(SIGPIPE, pop3catch);
931 pop3_update(&mb);
932 pop3_exit(&mb);
933 sclose(&mb.mb_sock);
934 safe_signal(SIGINT, saveint);
935 safe_signal(SIGPIPE, savepipe);
936 pop3lock = 0;
938 #endif /* HAVE_POP3 */