pop3.c: forgot to HAVE_MD5ize _pop3_lookup_apop_timestamp()
[s-mailx.git] / pop3.c
blobf325486c8c601368f2b82aa5cb5c6a32241f7187
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
47 #define POP3_ANSWER(RV,ACTIONSTOP) \
48 do if (((RV) = pop3_answer(mp)) == STOP) {\
49 ACTIONSTOP;\
50 } while (0)
52 #define POP3_OUT(RV,X,Y,ACTIONSTOP) \
53 do {\
54 if (((RV) = pop3_finish(mp)) == STOP) {\
55 ACTIONSTOP;\
57 if (options & OPT_VERBOSE)\
58 fprintf(stderr, ">>> %s", X);\
59 mp->mb_active |= Y;\
60 if (((RV) = swrite(&mp->mb_sock, X)) == STOP) {\
61 ACTIONSTOP;\
63 } while (0)
65 static char *_pop3_buf;
66 static size_t _pop3_bufsize;
67 static sigjmp_buf _pop3_jmp;
68 static sighandler_type _pop3_savealrm;
69 static int _pop3_keepalive;
70 static int volatile _pop3_lock;
72 /* Perform entire login handshake */
73 static enum okay _pop3_login(struct mailbox *mp, char *xuser, char *pass,
74 char const *uhp, char const *xserver);
76 /* APOP: get greeting credential or NULL */
77 #ifdef HAVE_MD5
78 static char * _pop3_lookup_apop_timestamp(char const *bp);
79 #endif
81 static bool_t _pop3_use_starttls(char const *uhp);
83 /* APOP: shall we use it (for uhp)? */
84 static bool_t _pop3_no_apop(char const *uhp);
86 /* Several authentication methods */
87 #ifdef HAVE_MD5
88 static enum okay _pop3_auth_apop(struct mailbox *mp, char *xuser, char *pass,
89 char const *ts);
90 #endif
91 static enum okay _pop3_auth_plain(struct mailbox *mp, char *xuser, char *pass);
93 static void pop3_timer_off(void);
94 static enum okay pop3_answer(struct mailbox *mp);
95 static enum okay pop3_finish(struct mailbox *mp);
96 static void pop3catch(int s);
97 static void _pop3_maincatch(int s);
98 static enum okay pop3_noop1(struct mailbox *mp);
99 static void pop3alarm(int s);
100 static enum okay pop3_stat(struct mailbox *mp, off_t *size, int *cnt);
101 static enum okay pop3_list(struct mailbox *mp, int n, size_t *size);
102 static void pop3_init(struct mailbox *mp, int n);
103 static void pop3_dates(struct mailbox *mp);
104 static void pop3_setptr(struct mailbox *mp);
105 static enum okay pop3_get(struct mailbox *mp, struct message *m,
106 enum needspec need);
107 static enum okay pop3_exit(struct mailbox *mp);
108 static enum okay pop3_delete(struct mailbox *mp, int n);
109 static enum okay pop3_update(struct mailbox *mp);
111 static enum okay
112 _pop3_login(struct mailbox *mp, char *xuser, char *pass, char const *uhp,
113 char const *xserver)
115 #ifdef HAVE_MD5
116 char *ts;
117 #endif
118 char *cp;
119 enum okay rv;
120 NYD_ENTER;
122 /* Get the greeting, check wether APOP is advertised */
123 POP3_ANSWER(rv, goto jleave);
124 #ifdef HAVE_MD5
125 ts = _pop3_lookup_apop_timestamp(_pop3_buf);
126 #endif
128 if ((cp = strchr(xserver, ':')) != NULL) { /* TODO GENERIC URI PARSE! */
129 size_t l = (size_t)(cp - xserver);
130 char *x = salloc(l + 1);
131 memcpy(x, xserver, l);
132 x[l] = '\0';
133 xserver = x;
136 /* If not yet secured, can we upgrade to TLS? */
137 #ifdef HAVE_SSL
138 if (mp->mb_sock.s_use_ssl == 0 && _pop3_use_starttls(uhp)) {
139 POP3_OUT(rv, "STLS\r\n", MB_COMD, goto jleave);
140 POP3_ANSWER(rv, goto jleave);
141 if ((rv = ssl_open(xserver, &mp->mb_sock, uhp)) != OKAY)
142 goto jleave;
144 #else
145 if (_pop3_use_starttls(uhp)) {
146 fprintf(stderr, "No SSL support compiled in.\n");
147 rv = STOP;
148 goto jleave;
150 #endif
152 /* Use the APOP single roundtrip? */
153 if (!_pop3_no_apop(uhp)) {
154 #ifdef HAVE_MD5
155 if (ts != NULL) {
156 if ((rv = _pop3_auth_apop(mp, xuser, pass, ts)) != OKAY)
157 fprintf(stderr, tr(276,
158 "POP3 `APOP' authentication failed, "
159 "maybe try setting *pop3-no-apop*\n"));
160 goto jleave;
161 } else
162 #endif
163 if (options & OPT_VERBOSE)
164 fprintf(stderr, tr(204, "No POP3 `APOP' support "
165 "available, sending password in clear text\n"));
167 rv = _pop3_auth_plain(mp, xuser, pass);
168 jleave:
169 NYD_LEAVE;
170 return rv;
173 #ifdef HAVE_MD5
174 static char *
175 _pop3_lookup_apop_timestamp(char const *bp)
177 /* RFC 1939:
178 * A POP3 server which implements the APOP command will include
179 * a timestamp in its banner greeting. The syntax of the timestamp
180 * corresponds to the `msg-id' in [RFC822]
181 * RFC 822:
182 * msg-id = "<" addr-spec ">"
183 * addr-spec = local-part "@" domain */
184 char const *cp, *ep;
185 size_t tl;
186 char *rp = NULL;
187 bool_t hadat = FAL0;
188 NYD_ENTER;
190 if ((cp = strchr(bp, '<')) == NULL)
191 goto jleave;
193 /* xxx What about malformed APOP timestamp (<@>) here? */
194 for (ep = cp; *ep; ep++) {
195 if (spacechar(*ep))
196 goto jleave;
197 else if (*ep == '@')
198 hadat = TRU1;
199 else if (*ep == '>') {
200 if (!hadat)
201 goto jleave;
202 break;
205 if (*ep != '>')
206 goto jleave;
208 tl = PTR2SIZE(++ep - cp);
209 rp = salloc(tl +1);
210 memcpy(rp, cp, tl);
211 rp[tl] = '\0';
212 jleave:
213 NYD_LEAVE;
214 return rp;
216 #endif
218 static bool_t
219 _pop3_use_starttls(char const *uhp)
221 bool_t rv;
222 NYD_ENTER;
224 if (!(rv = ok_blook(pop3_use_starttls)))
225 rv = vok_blook(savecat("pop3-use-starttls-", uhp));
226 NYD_LEAVE;
227 return rv;
230 static bool_t
231 _pop3_no_apop(char const *uhp)
233 bool_t rv;
234 NYD_ENTER;
236 if (!(rv = ok_blook(pop3_no_apop)))
237 rv = vok_blook(savecat("pop3-no-apop-", uhp));
238 NYD_LEAVE;
239 return rv;
242 #ifdef HAVE_MD5
243 static enum okay
244 _pop3_auth_apop(struct mailbox *mp, char *xuser, char *pass, char const *ts)
246 unsigned char digest[16];
247 char hex[MD5TOHEX_SIZE];
248 md5_ctx ctx;
249 size_t tl, i;
250 char *user, *cp;
251 enum okay rv = STOP;
252 NYD_ENTER;
254 for (tl = strlen(ts);;) {
255 user = xuser;
256 if (!getcredentials(&user, &pass))
257 break;
259 md5_init(&ctx);
260 md5_update(&ctx, (unsigned char*)UNCONST(ts), tl);
261 md5_update(&ctx, (unsigned char*)pass, strlen(pass));
262 md5_final(digest, &ctx);
263 md5tohex(hex, digest);
265 i = strlen(user);
266 cp = ac_alloc(5 + i + 1 + MD5TOHEX_SIZE + 2 +1);
268 memcpy(cp, "APOP ", 5);
269 memcpy(cp + 5, user, i);
270 i += 5;
271 cp[i++] = ' ';
272 memcpy(cp + i, hex, MD5TOHEX_SIZE);
273 i += MD5TOHEX_SIZE;
274 memcpy(cp + i, "\r\n\0", 3);
275 POP3_OUT(rv, cp, MB_COMD, goto jcont);
276 POP3_ANSWER(rv, goto jcont);
277 rv = OKAY;
278 jcont:
279 ac_free(cp);
280 if (rv == OKAY)
281 break;
282 pass = NULL;
284 NYD_LEAVE;
285 return rv;
287 #endif /* HAVE_MD5 */
289 static enum okay
290 _pop3_auth_plain(struct mailbox *mp, char *xuser, char *pass)
292 char *user, *cp;
293 size_t ul, pl;
294 enum okay rv = STOP;
295 NYD_ENTER;
297 /* The USER/PASS plain text version */
298 for (;;) {
299 user = xuser;
300 if (!getcredentials(&user, &pass))
301 break;
303 ul = strlen(user);
304 pl = strlen(pass);
305 cp = ac_alloc(MAX(ul, pl) + 5 + 2 +1);
307 memcpy(cp, "USER ", 5);
308 memcpy(cp + 5, user, ul);
309 memcpy(cp + 5 + ul, "\r\n\0", 3);
310 POP3_OUT(rv, cp, MB_COMD, goto jcont);
311 POP3_ANSWER(rv, goto jcont);
313 memcpy(cp, "PASS ", 5);
314 memcpy(cp + 5, pass, pl);
315 memcpy(cp + 5 + pl, "\r\n\0", 3);
316 POP3_OUT(rv, cp, MB_COMD, goto jcont);
317 POP3_ANSWER(rv, goto jcont);
318 rv = OKAY;
319 jcont:
320 ac_free(cp);
321 if (rv == OKAY)
322 break;
323 pass = NULL;
325 NYD_LEAVE;
326 return rv;
329 static void
330 pop3_timer_off(void)
332 NYD_ENTER;
333 if (_pop3_keepalive > 0) {
334 alarm(0);
335 safe_signal(SIGALRM, _pop3_savealrm);
337 NYD_LEAVE;
340 static enum okay
341 pop3_answer(struct mailbox *mp)
343 int sz;
344 enum okay rv = STOP;
345 NYD_ENTER;
347 jretry:
348 if ((sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock)) > 0) {
349 if ((mp->mb_active & (MB_COMD | MB_MULT)) == MB_MULT)
350 goto jmultiline;
351 if (options & OPT_VERBOSE)
352 fputs(_pop3_buf, stderr);
353 switch (*_pop3_buf) {
354 case '+':
355 rv = OKAY;
356 mp->mb_active &= ~MB_COMD;
357 break;
358 case '-':
359 rv = STOP;
360 mp->mb_active = MB_NONE;
361 fprintf(stderr, tr(218, "POP3 error: %s"), _pop3_buf);
362 break;
363 default:
364 /* If the answer starts neither with '+' nor with
365 * '-', it must be part of a multiline response,
366 * e. g. because the user interrupted a file
367 * download. Get lines until a single dot appears */
368 jmultiline:
369 while (_pop3_buf[0] != '.' || _pop3_buf[1] != '\r' ||
370 _pop3_buf[2] != '\n' || _pop3_buf[3] != '\0') {
371 sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock);
372 if (sz <= 0)
373 goto jeof;
375 mp->mb_active &= ~MB_MULT;
376 if (mp->mb_active != MB_NONE)
377 goto jretry;
379 } else {
380 jeof:
381 rv = STOP;
382 mp->mb_active = MB_NONE;
384 NYD_LEAVE;
385 return rv;
388 static enum okay
389 pop3_finish(struct mailbox *mp)
391 NYD_ENTER;
392 while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
393 pop3_answer(mp);
394 NYD_LEAVE;
395 return OKAY;
398 static void
399 pop3catch(int s)
401 NYD_X; /* Signal handler */
402 termios_state_reset();
403 switch (s) {
404 case SIGINT:
405 fprintf(stderr, tr(102, "Interrupt\n"));
406 siglongjmp(_pop3_jmp, 1);
407 break;
408 case SIGPIPE:
409 fprintf(stderr, "Received SIGPIPE during POP3 operation\n");
410 break;
414 static void
415 _pop3_maincatch(int s)
417 NYD_X; /* Signal handler */
418 UNUSED(s);
420 if (interrupts++ == 0)
421 fprintf(stderr, tr(102, "Interrupt\n"));
422 else
423 onintr(0);
426 static enum okay
427 pop3_noop1(struct mailbox *mp)
429 enum okay rv;
430 NYD_ENTER;
432 POP3_OUT(rv, "NOOP\r\n", MB_COMD, goto jleave);
433 POP3_ANSWER(rv, goto jleave);
434 jleave:
435 NYD_LEAVE;
436 return rv;
439 static void
440 pop3alarm(int s)
442 sighandler_type volatile saveint, savepipe;
443 NYD_X; /* Signal handler */
444 UNUSED(s);
446 if (_pop3_lock++ == 0) {
447 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
448 safe_signal(SIGINT, &_pop3_maincatch);
449 savepipe = safe_signal(SIGPIPE, SIG_IGN);
450 if (sigsetjmp(_pop3_jmp, 1)) {
451 safe_signal(SIGINT, saveint);
452 safe_signal(SIGPIPE, savepipe);
453 goto jbrk;
455 if (savepipe != SIG_IGN)
456 safe_signal(SIGPIPE, pop3catch);
457 if (pop3_noop1(&mb) != OKAY) {
458 safe_signal(SIGINT, saveint);
459 safe_signal(SIGPIPE, savepipe);
460 goto jleave;
462 safe_signal(SIGINT, saveint);
463 safe_signal(SIGPIPE, savepipe);
465 jbrk:
466 alarm(_pop3_keepalive);
467 jleave:
468 --_pop3_lock;
471 static enum okay
472 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
474 char *cp;
475 enum okay rv;
476 NYD_ENTER;
478 POP3_OUT(rv, "STAT\r\n", MB_COMD, goto jleave);
479 POP3_ANSWER(rv, goto jleave);
481 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
483 while (*cp != '\0' && spacechar(*cp))
484 ++cp;
486 if (*cp != '\0') {
487 *cnt = (int)strtol(cp, NULL, 10);
488 while (*cp != '\0' && !spacechar(*cp))
489 ++cp;
490 while (*cp != '\0' && spacechar(*cp))
491 ++cp;
492 if (*cp != '\0')
493 *size = (int)strtol(cp, NULL, 10);
494 else
495 rv = STOP;
496 } else
497 rv = STOP;
499 if (rv == STOP)
500 fprintf(stderr, tr(260, "invalid POP3 STAT response: %s\n"),
501 _pop3_buf);
502 jleave:
503 NYD_LEAVE;
504 return rv;
507 static enum okay
508 pop3_list(struct mailbox *mp, int n, size_t *size)
510 char o[LINESIZE], *cp;
511 enum okay rv;
512 NYD_ENTER;
514 snprintf(o, sizeof o, "LIST %u\r\n", n);
515 POP3_OUT(rv, o, MB_COMD, goto jleave);
516 POP3_ANSWER(rv, goto jleave);
518 for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
520 while (*cp != '\0' && spacechar(*cp))
521 ++cp;
522 while (*cp != '\0' && !spacechar(*cp))
523 ++cp;
524 while (*cp != '\0' && spacechar(*cp))
525 ++cp;
526 if (*cp != '\0')
527 *size = (size_t)strtol(cp, NULL, 10);
528 else
529 *size = 0;
530 jleave:
531 NYD_LEAVE;
532 return rv;
535 static void
536 pop3_init(struct mailbox *mp, int n)
538 struct message *m;
539 char *cp;
540 NYD_ENTER;
542 m = message + n;
543 m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
544 m->m_block = 0;
545 m->m_offset = 0;
546 pop3_list(mp, PTR2SIZE(m - message + 1), &m->m_xsize);
548 if ((cp = hfield1("status", m)) != NULL) {
549 while (*cp != '\0') {
550 if (*cp == 'R')
551 m->m_flag |= MREAD;
552 else if (*cp == 'O')
553 m->m_flag &= ~MNEW;
554 ++cp;
557 NYD_LEAVE;
560 static void
561 pop3_dates(struct mailbox *mp)
563 size_t i;
564 NYD_ENTER;
565 UNUSED(mp);
567 for (i = 0; UICMP(z, i, <, msgCount); ++i)
568 substdate(message + i);
569 NYD_LEAVE;
572 static void
573 pop3_setptr(struct mailbox *mp)
575 size_t i;
576 NYD_ENTER;
578 message = scalloc(msgCount + 1, sizeof *message);
579 for (i = 0; UICMP(z, i, <, msgCount); ++i)
580 pop3_init(mp, i);
582 setdot(message);
583 message[msgCount].m_size = 0;
584 message[msgCount].m_lines = 0;
585 pop3_dates(mp);
586 NYD_LEAVE;
589 static enum okay
590 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
592 char o[LINESIZE], *line = NULL, *lp;
593 sighandler_type volatile saveint = SIG_IGN, savepipe = SIG_IGN;
594 size_t linesize = 0, linelen, size;
595 int number = m - message + 1, emptyline = 0, lines;
596 off_t offset;
597 enum okay rv = STOP;
598 NYD_ENTER;
600 if (mp->mb_sock.s_fd < 0) {
601 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
602 ++_pop3_lock;
603 goto jleave;
606 if (_pop3_lock++ == 0) {
607 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
608 safe_signal(SIGINT, &_pop3_maincatch);
609 savepipe = safe_signal(SIGPIPE, SIG_IGN);
610 if (sigsetjmp(_pop3_jmp, 1)) {
611 safe_signal(SIGINT, saveint);
612 safe_signal(SIGPIPE, savepipe);
613 --_pop3_lock;
614 goto jleave;
616 if (savepipe != SIG_IGN)
617 safe_signal(SIGPIPE, pop3catch);
620 fseek(mp->mb_otf, 0L, SEEK_END);
621 offset = ftell(mp->mb_otf);
622 jretry:
623 switch (need) {
624 case NEED_HEADER:
625 snprintf(o, sizeof o, "TOP %u 0\r\n", number);
626 break;
627 case NEED_BODY:
628 snprintf(o, sizeof o, "RETR %u\r\n", number);
629 break;
630 case NEED_UNSPEC:
631 abort(); /* XXX */
633 POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
635 if (pop3_answer(mp) == STOP) {
636 if (need == NEED_HEADER) {
637 /* The TOP POP3 command is optional, so retry with entire message */
638 need = NEED_BODY;
639 goto jretry;
641 goto jleave;
644 size = 0;
645 lines = 0;
646 while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
647 if (line[0] == '.' && line[1] == '\r' && line[2] == '\n' &&
648 line[3] == '\0') {
649 mp->mb_active &= ~MB_MULT;
650 break;
652 if (line[0] == '.') {
653 lp = &line[1];
654 linelen--;
655 } else
656 lp = line;
657 /* TODO >>
658 * Need to mask 'From ' lines. This cannot be done properly
659 * since some servers pass them as 'From ' and others as
660 * '>From '. Although one could identify the first kind of
661 * server in principle, it is not possible to identify the
662 * second as '>From ' may also come from a server of the
663 * first type as actual data. So do what is absolutely
664 * necessary only - mask 'From '.
666 * If the line is the first line of the message header, it
667 * is likely a real 'From ' line. In this case, it is just
668 * ignored since it violates all standards.
669 * TODO i have *never* seen the latter?!?!?
670 * TODO <<
672 /* Since we simply copy over data without doing any transfer
673 * encoding reclassification/adjustment we *have* to perform
674 * RFC 4155 compliant From_ quoting here */
675 if (is_head(lp, linelen)) {
676 if (lines == 0)
677 continue;
678 fputc('>', mp->mb_otf);
679 ++size;
681 lines++;
682 if (lp[linelen-1] == '\n' && (linelen == 1 ||
683 lp[linelen-2] == '\r')) {
684 emptyline = linelen <= 2;
685 if (linelen > 2)
686 fwrite(lp, 1, linelen - 2, mp->mb_otf);
687 fputc('\n', mp->mb_otf);
688 size += linelen - 1;
689 } else {
690 emptyline = 0;
691 fwrite(lp, 1, linelen, mp->mb_otf);
692 size += linelen;
695 if (!emptyline) {
696 /* This is very ugly; but some POP3 daemons don't end a
697 * message with \r\n\r\n, and we need \n\n for mbox format */
698 fputc('\n', mp->mb_otf);
699 lines++;
700 size++;
702 m->m_size = size;
703 m->m_lines = lines;
704 m->m_block = mailx_blockof(offset);
705 m->m_offset = mailx_offsetof(offset);
706 fflush(mp->mb_otf);
708 switch (need) {
709 case NEED_HEADER:
710 m->m_have |= HAVE_HEADER;
711 break;
712 case NEED_BODY:
713 m->m_have |= HAVE_HEADER | HAVE_BODY;
714 m->m_xlines = m->m_lines;
715 m->m_xsize = m->m_size;
716 break;
717 case NEED_UNSPEC:
718 break;
721 rv = OKAY;
722 jleave:
723 if (line != NULL)
724 free(line);
725 if (saveint != SIG_IGN)
726 safe_signal(SIGINT, saveint);
727 if (savepipe != SIG_IGN)
728 safe_signal(SIGPIPE, savepipe);
729 --_pop3_lock;
730 NYD_LEAVE;
731 if (interrupts)
732 onintr(0);
733 return rv;
736 static enum okay
737 pop3_exit(struct mailbox *mp)
739 enum okay rv;
740 NYD_ENTER;
742 POP3_OUT(rv, "QUIT\r\n", MB_COMD, goto jleave);
743 POP3_ANSWER(rv, goto jleave);
744 jleave:
745 NYD_LEAVE;
746 return rv;
749 static enum okay
750 pop3_delete(struct mailbox *mp, int n)
752 char o[LINESIZE];
753 enum okay rv;
754 NYD_ENTER;
756 snprintf(o, sizeof o, "DELE %u\r\n", n);
757 POP3_OUT(rv, o, MB_COMD, goto jleave);
758 POP3_ANSWER(rv, goto jleave);
759 jleave:
760 NYD_LEAVE;
761 return rv;
764 static enum okay
765 pop3_update(struct mailbox *mp)
767 struct message *m;
768 int dodel, c, gotcha, held;
769 NYD_ENTER;
771 if (!edit) {
772 holdbits();
773 c = 0;
774 for (m = message; PTRCMP(m, <, message + msgCount); ++m)
775 if (m->m_flag & MBOX)
776 ++c;
777 if (c > 0)
778 makembox();
781 gotcha = held = 0;
782 for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
783 if (edit)
784 dodel = m->m_flag & MDELETED;
785 else
786 dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
787 if (dodel) {
788 pop3_delete(mp, PTR2SIZE(m - message + 1));
789 ++gotcha;
790 } else
791 ++held;
793 if (gotcha && edit) {
794 printf(tr(168, "\"%s\" "), displayname);
795 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
796 ? tr(170, "complete\n") : tr(212, "updated.\n"));
797 } else if (held && !edit) {
798 if (held == 1)
799 printf(tr(155, "Held 1 message in %s\n"), displayname);
800 else
801 printf(tr(156, "Held %d messages in %s\n"), held, displayname);
803 fflush(stdout);
804 NYD_LEAVE;
805 return OKAY;
808 FL enum okay
809 pop3_noop(void)
811 sighandler_type volatile saveint, savepipe;
812 enum okay rv = STOP;
813 NYD_ENTER;
815 _pop3_lock = 1;
816 if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
817 safe_signal(SIGINT, &_pop3_maincatch);
818 savepipe = safe_signal(SIGPIPE, SIG_IGN);
819 if (sigsetjmp(_pop3_jmp, 1) == 0) {
820 if (savepipe != SIG_IGN)
821 safe_signal(SIGPIPE, pop3catch);
822 rv = pop3_noop1(&mb);
824 safe_signal(SIGINT, saveint);
825 safe_signal(SIGPIPE, savepipe);
826 _pop3_lock = 0;
827 NYD_LEAVE;
828 return rv;
831 FL int
832 pop3_setfile(char const *server, int nmail, int isedit)
834 struct sock so;
835 sighandler_type saveint, savepipe;
836 char * volatile user, * volatile pass;
837 char const *cp, *uhp, * volatile sp = server;
838 int use_ssl = 0, rv = 1;
839 NYD_ENTER;
841 if (nmail)
842 goto jleave;
844 if (strncmp(sp, "pop3://", 7) == 0) {
845 sp = &sp[7];
846 use_ssl = 0;
847 #ifdef HAVE_SSL
848 } else if (strncmp(sp, "pop3s://", 8) == 0) {
849 sp = &sp[8];
850 use_ssl = 1;
851 #endif
853 uhp = sp;
854 pass = lookup_password_for_token(uhp);
856 if ((cp = last_at_before_slash(sp)) != NULL) {
857 user = salloc(cp - sp +1);
858 memcpy(user, sp, cp - sp);
859 user[cp - sp] = '\0';
860 sp = &cp[1];
861 user = urlxdec(user);
862 } else
863 user = NULL;
865 if (sopen(sp, &so, use_ssl, uhp, (use_ssl ? "pop3s" : "pop3")) != OKAY) {
866 rv = -1;
867 goto jleave;
869 quit();
870 edit = (isedit != 0);
871 if (mb.mb_sock.s_fd >= 0)
872 sclose(&mb.mb_sock);
873 if (mb.mb_itf) {
874 fclose(mb.mb_itf);
875 mb.mb_itf = NULL;
877 if (mb.mb_otf) {
878 fclose(mb.mb_otf);
879 mb.mb_otf = NULL;
881 initbox(server);
882 mb.mb_type = MB_VOID;
883 _pop3_lock = 1;
884 mb.mb_sock = so;
886 saveint = safe_signal(SIGINT, SIG_IGN);
887 savepipe = safe_signal(SIGPIPE, SIG_IGN);
888 if (sigsetjmp(_pop3_jmp, 1)) {
889 sclose(&mb.mb_sock);
890 safe_signal(SIGINT, saveint);
891 safe_signal(SIGPIPE, savepipe);
892 _pop3_lock = 0;
893 goto jleave;
895 if (saveint != SIG_IGN)
896 safe_signal(SIGINT, pop3catch);
897 if (savepipe != SIG_IGN)
898 safe_signal(SIGPIPE, pop3catch);
900 if ((cp = ok_vlook(pop3_keepalive)) != NULL) {
901 if ((_pop3_keepalive = (int)strtol(cp, NULL, 10)) > 0) {
902 _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
903 alarm(_pop3_keepalive);
907 mb.mb_sock.s_desc = "POP3";
908 mb.mb_sock.s_onclose = pop3_timer_off;
909 if (_pop3_login(&mb, user, pass, uhp, sp) != OKAY ||
910 pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
911 sclose(&mb.mb_sock);
912 pop3_timer_off();
913 safe_signal(SIGINT, saveint);
914 safe_signal(SIGPIPE, savepipe);
915 _pop3_lock = 0;
916 goto jleave;
918 mb.mb_type = MB_POP3;
919 mb.mb_perm = (options & OPT_R_FLAG) ? 0 : MB_DELE;
920 pop3_setptr(&mb);
921 setmsize(msgCount);
922 sawcom = FAL0;
924 safe_signal(SIGINT, saveint);
925 safe_signal(SIGPIPE, savepipe);
926 _pop3_lock = 0;
927 if (!edit && msgCount == 0) {
928 if (mb.mb_type == MB_POP3 && !ok_blook(emptystart))
929 fprintf(stderr, tr(258, "No mail at %s\n"), server);
930 goto jleave;
932 rv = 0;
933 jleave:
934 NYD_LEAVE;
935 return rv;
938 FL enum okay
939 pop3_header(struct message *m)
941 enum okay rv;
942 NYD_ENTER;
944 rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
945 NYD_LEAVE;
946 return rv;
949 FL enum okay
950 pop3_body(struct message *m)
952 enum okay rv;
953 NYD_ENTER;
955 rv = pop3_get(&mb, m, NEED_BODY);
956 NYD_LEAVE;
957 return rv;
960 FL void
961 pop3_quit(void)
963 sighandler_type volatile saveint, savepipe;
964 NYD_ENTER;
966 if (mb.mb_sock.s_fd < 0) {
967 fprintf(stderr, tr(219, "POP3 connection already closed.\n"));
968 goto jleave;
971 _pop3_lock = 1;
972 saveint = safe_signal(SIGINT, SIG_IGN);
973 savepipe = safe_signal(SIGPIPE, SIG_IGN);
974 if (sigsetjmp(_pop3_jmp, 1)) {
975 safe_signal(SIGINT, saveint);
976 safe_signal(SIGPIPE, saveint);
977 _pop3_lock = 0;
978 goto jleave;
980 if (saveint != SIG_IGN)
981 safe_signal(SIGINT, pop3catch);
982 if (savepipe != SIG_IGN)
983 safe_signal(SIGPIPE, pop3catch);
984 pop3_update(&mb);
985 pop3_exit(&mb);
986 sclose(&mb.mb_sock);
987 safe_signal(SIGINT, saveint);
988 safe_signal(SIGPIPE, savepipe);
989 _pop3_lock = 0;
990 jleave:
991 NYD_LEAVE;
993 #endif /* HAVE_POP3 */
995 /* vim:set fenc=utf-8:s-it-mode */