"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/pop3.c" (8 Aug 2018, 26599 Bytes) of package /linux/misc/s-nail-14.9.11.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "pop3.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.10_vs_14.9.11.

    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...
    4  *
    5  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
    6  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    7  * SPDX-License-Identifier: BSD-4-Clause
    8  */
    9 /*
   10  * Copyright (c) 2002
   11  * Gunnar Ritter.  All rights reserved.
   12  *
   13  * Redistribution and use in source and binary forms, with or without
   14  * modification, are permitted provided that the following conditions
   15  * are met:
   16  * 1. Redistributions of source code must retain the above copyright
   17  *    notice, this list of conditions and the following disclaimer.
   18  * 2. Redistributions in binary form must reproduce the above copyright
   19  *    notice, this list of conditions and the following disclaimer in the
   20  *    documentation and/or other materials provided with the distribution.
   21  * 3. All advertising materials mentioning features or use of this software
   22  *    must display the following acknowledgement:
   23  *    This product includes software developed by Gunnar Ritter
   24  *    and his contributors.
   25  * 4. Neither the name of Gunnar Ritter nor the names of his contributors
   26  *    may be used to endorse or promote products derived from this software
   27  *    without specific prior written permission.
   28  *
   29  * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
   30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   32  * ARE DISCLAIMED.  IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
   33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   39  * SUCH DAMAGE.
   40  */
   41 #undef n_FILE
   42 #define n_FILE pop3
   43 
   44 #ifndef HAVE_AMALGAMATION
   45 # include "nail.h"
   46 #endif
   47 
   48 EMPTY_FILE()
   49 #ifdef HAVE_POP3
   50 
   51 #define POP3_ANSWER(RV,ACTIONSTOP) \
   52 do if (((RV) = pop3_answer(mp)) == STOP) {\
   53    ACTIONSTOP;\
   54 } while (0)
   55 
   56 #define POP3_OUT(RV,X,Y,ACTIONSTOP) \
   57 do {\
   58    if (((RV) = pop3_finish(mp)) == STOP) {\
   59       ACTIONSTOP;\
   60    }\
   61    if (n_poption & n_PO_VERBVERB)\
   62       n_err(">>> %s", X);\
   63    mp->mb_active |= Y;\
   64    if (((RV) = swrite(&mp->mb_sock, X)) == STOP) {\
   65       ACTIONSTOP;\
   66    }\
   67 } while (0)
   68 
   69 static char             *_pop3_buf;
   70 static size_t           _pop3_bufsize;
   71 static sigjmp_buf       _pop3_jmp;
   72 static sighandler_type  _pop3_savealrm;
   73 static si32_t           _pop3_keepalive;
   74 static int volatile     _pop3_lock;
   75 
   76 /* Perform entire login handshake */
   77 static enum okay  _pop3_login(struct mailbox *mp, struct sockconn *scp);
   78 
   79 /* APOP: get greeting credential or NULL */
   80 #ifdef HAVE_MD5
   81 static char *     _pop3_lookup_apop_timestamp(char const *bp);
   82 #endif
   83 
   84 /* Several authentication methods */
   85 #ifdef HAVE_MD5
   86 static enum okay  _pop3_auth_apop(struct mailbox *mp,
   87                      struct sockconn const *scp, char const *ts);
   88 #endif
   89 static enum okay  _pop3_auth_plain(struct mailbox *mp,
   90                      struct sockconn const *scp);
   91 
   92 static void       pop3_timer_off(void);
   93 static enum okay  pop3_answer(struct mailbox *mp);
   94 static enum okay  pop3_finish(struct mailbox *mp);
   95 static void       pop3catch(int s);
   96 static void       _pop3_maincatch(int s);
   97 static enum okay  pop3_noop1(struct mailbox *mp);
   98 static void       pop3alarm(int s);
   99 static enum okay  pop3_stat(struct mailbox *mp, off_t *size, int *cnt);
  100 static enum okay  pop3_list(struct mailbox *mp, int n, size_t *size);
  101 static void       pop3_setptr(struct mailbox *mp,
  102                      struct sockconn const *scp);
  103 static enum okay  pop3_get(struct mailbox *mp, struct message *m,
  104                      enum needspec need);
  105 static enum okay  pop3_exit(struct mailbox *mp);
  106 static enum okay  pop3_delete(struct mailbox *mp, int n);
  107 static enum okay  pop3_update(struct mailbox *mp);
  108 
  109 static enum okay
  110 _pop3_login(struct mailbox *mp, struct sockconn *scp)
  111 {
  112 #ifdef HAVE_MD5
  113    char *ts;
  114 #endif
  115    enum okey_xlook_mode oxm;
  116    enum okay rv;
  117    NYD_ENTER;
  118 
  119    oxm = ok_blook(v15_compat) ? OXM_ALL : OXM_PLAIN | OXM_U_H_P;
  120 
  121    /* Get the greeting, check whether APOP is advertised */
  122    POP3_ANSWER(rv, goto jleave);
  123 #ifdef HAVE_MD5
  124    ts = _pop3_lookup_apop_timestamp(_pop3_buf);
  125 #endif
  126 
  127    /* If not yet secured, can we upgrade to TLS? */
  128 #ifdef HAVE_TLS
  129    if (!(scp->sc_url.url_flags & n_URL_TLS_REQUIRED) &&
  130          xok_blook(pop3_use_starttls, &scp->sc_url, oxm)) {
  131       POP3_OUT(rv, "STLS" NETNL, MB_COMD, goto jleave);
  132       POP3_ANSWER(rv, goto jleave);
  133       if(!n_tls_open(&scp->sc_url, &scp->sc_sock)){
  134          rv = STOP;
  135          goto jleave;
  136       }
  137    }
  138 #else
  139    if (xok_blook(pop3_use_starttls, &scp->sc_url, oxm)) {
  140       n_err(_("No TLS support compiled in\n"));
  141       rv = STOP;
  142       goto jleave;
  143    }
  144 #endif
  145 
  146    /* Use the APOP single roundtrip? */
  147 #ifdef HAVE_MD5
  148    if (ts != NULL && !xok_blook(pop3_no_apop, &scp->sc_url, oxm)) {
  149       if ((rv = _pop3_auth_apop(mp, scp, ts)) != OKAY) {
  150          char const *ccp;
  151 
  152 # ifdef HAVE_TLS
  153          if (scp->sc_sock.s_use_tls)
  154             ccp = _("over an encrypted connection");
  155          else
  156 # endif
  157             ccp = _("(unsafe clear text!)");
  158          n_err(_("POP3 APOP authentication failed!\n"
  159             "  Server indicated support..  Set *pop3-no-apop*\n"
  160             "  for plain text authentication %s\n"), ccp);
  161       }
  162       goto jleave;
  163    }
  164 #endif
  165 
  166    rv = _pop3_auth_plain(mp, scp);
  167 jleave:
  168    NYD_LEAVE;
  169    return rv;
  170 }
  171 
  172 #ifdef HAVE_MD5
  173 static char *
  174 _pop3_lookup_apop_timestamp(char const *bp)
  175 {
  176    /* RFC 1939:
  177     * A POP3 server which implements the APOP command will include
  178     * a timestamp in its banner greeting.  The syntax of the timestamp
  179     * corresponds to the "msg-id" in [RFC822]
  180     * RFC 822:
  181     * msg-id   = "<" addr-spec ">"
  182     * addr-spec   = local-part "@" domain */
  183    char const *cp, *ep;
  184    size_t tl;
  185    char *rp = NULL;
  186    bool_t hadat = FAL0;
  187    NYD_ENTER;
  188 
  189    if ((cp = strchr(bp, '<')) == NULL)
  190       goto jleave;
  191 
  192    /* xxx What about malformed APOP timestamp (<@>) here? */
  193    for (ep = cp; *ep != '\0'; ++ep) {
  194       if (spacechar(*ep))
  195          goto jleave;
  196       else if (*ep == '@')
  197          hadat = TRU1;
  198       else if (*ep == '>') {
  199          if (!hadat)
  200             goto jleave;
  201          break;
  202       }
  203    }
  204    if (*ep != '>')
  205       goto jleave;
  206 
  207    tl = PTR2SIZE(++ep - cp);
  208    rp = n_autorec_alloc(tl +1);
  209    memcpy(rp, cp, tl);
  210    rp[tl] = '\0';
  211 jleave:
  212    NYD_LEAVE;
  213    return rp;
  214 }
  215 #endif
  216 
  217 #ifdef HAVE_MD5
  218 static enum okay
  219 _pop3_auth_apop(struct mailbox *mp, struct sockconn const *scp, char const *ts)
  220 {
  221    unsigned char digest[16];
  222    char hex[MD5TOHEX_SIZE], *cp;
  223    md5_ctx ctx;
  224    size_t i;
  225    enum okay rv = STOP;
  226    NYD_ENTER;
  227 
  228    md5_init(&ctx);
  229    md5_update(&ctx, (uc_i*)n_UNCONST(ts), strlen(ts));
  230    md5_update(&ctx, (uc_i*)scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
  231    md5_final(digest, &ctx);
  232    md5tohex(hex, digest);
  233 
  234    i = scp->sc_cred.cc_user.l;
  235    cp = n_lofi_alloc(5 + i + 1 + MD5TOHEX_SIZE + sizeof(NETNL)-1 +1);
  236 
  237    memcpy(cp, "APOP ", 5);
  238    memcpy(cp + 5, scp->sc_cred.cc_user.s, i);
  239    i += 5;
  240    cp[i++] = ' ';
  241    memcpy(cp + i, hex, MD5TOHEX_SIZE);
  242    i += MD5TOHEX_SIZE;
  243    memcpy(cp + i, NETNL, sizeof(NETNL));
  244    POP3_OUT(rv, cp, MB_COMD, goto jleave);
  245    POP3_ANSWER(rv, goto jleave);
  246 
  247    rv = OKAY;
  248 jleave:
  249    n_lofi_free(cp);
  250    NYD_LEAVE;
  251    return rv;
  252 }
  253 #endif /* HAVE_MD5 */
  254 
  255 static enum okay
  256 _pop3_auth_plain(struct mailbox *mp, struct sockconn const *scp)
  257 {
  258    char *cp;
  259    enum okay rv = STOP;
  260    NYD_ENTER;
  261 
  262    /* The USER/PASS plain text version */
  263    cp = n_lofi_alloc(n_MAX(scp->sc_cred.cc_user.l, scp->sc_cred.cc_pass.l) +
  264          5 + sizeof(NETNL)-1 +1);
  265 
  266    memcpy(cp, "USER ", 5);
  267    memcpy(cp + 5, scp->sc_cred.cc_user.s, scp->sc_cred.cc_user.l);
  268    memcpy(cp + 5 + scp->sc_cred.cc_user.l, NETNL, sizeof(NETNL));
  269    POP3_OUT(rv, cp, MB_COMD, goto jleave);
  270    POP3_ANSWER(rv, goto jleave);
  271 
  272    memcpy(cp, "PASS ", 5);
  273    memcpy(cp + 5, scp->sc_cred.cc_pass.s, scp->sc_cred.cc_pass.l);
  274    memcpy(cp + 5 + scp->sc_cred.cc_pass.l, NETNL, sizeof(NETNL));
  275    POP3_OUT(rv, cp, MB_COMD, goto jleave);
  276    POP3_ANSWER(rv, goto jleave);
  277 
  278    rv = OKAY;
  279 jleave:
  280    n_lofi_free(cp);
  281    NYD_LEAVE;
  282    return rv;
  283 }
  284 
  285 static void
  286 pop3_timer_off(void)
  287 {
  288    NYD_ENTER;
  289    if (_pop3_keepalive > 0) {
  290       alarm(0);
  291       safe_signal(SIGALRM, _pop3_savealrm);
  292    }
  293    NYD_LEAVE;
  294 }
  295 
  296 static enum okay
  297 pop3_answer(struct mailbox *mp)
  298 {
  299    int sz;
  300    size_t blen;
  301    enum okay rv = STOP;
  302    NYD_ENTER;
  303 
  304 jretry:
  305    if ((sz = sgetline(&_pop3_buf, &_pop3_bufsize, &blen, &mp->mb_sock)) > 0) {
  306       if ((mp->mb_active & (MB_COMD | MB_MULT)) == MB_MULT)
  307          goto jmultiline;
  308       if (n_poption & n_PO_VERBVERB)
  309          n_err(_pop3_buf);
  310       switch (*_pop3_buf) {
  311       case '+':
  312          rv = OKAY;
  313          mp->mb_active &= ~MB_COMD;
  314          break;
  315       case '-':
  316          rv = STOP;
  317          mp->mb_active = MB_NONE;
  318          while (blen > 0 &&
  319                (_pop3_buf[blen - 1] == NETNL[0] ||
  320                 _pop3_buf[blen - 1] == NETNL[1]))
  321             _pop3_buf[--blen] = '\0';
  322          n_err(_("POP3 error: %s\n"), _pop3_buf);
  323          break;
  324       default:
  325          /* If the answer starts neither with '+' nor with '-', it must be part
  326           * of a multiline response.  Get lines until a single dot appears */
  327 jmultiline:
  328          while (_pop3_buf[0] != '.' || _pop3_buf[1] != NETNL[0] ||
  329                _pop3_buf[2] != NETNL[1] || _pop3_buf[3] != '\0') {
  330             sz = sgetline(&_pop3_buf, &_pop3_bufsize, NULL, &mp->mb_sock);
  331             if (sz <= 0)
  332                goto jeof;
  333          }
  334          mp->mb_active &= ~MB_MULT;
  335          if (mp->mb_active != MB_NONE)
  336             goto jretry;
  337       }
  338    } else {
  339 jeof:
  340       rv = STOP;
  341       mp->mb_active = MB_NONE;
  342    }
  343    NYD_LEAVE;
  344    return rv;
  345 }
  346 
  347 static enum okay
  348 pop3_finish(struct mailbox *mp)
  349 {
  350    NYD_ENTER;
  351    while (mp->mb_sock.s_fd > 0 && mp->mb_active != MB_NONE)
  352       pop3_answer(mp);
  353    NYD_LEAVE;
  354    return OKAY;
  355 }
  356 
  357 static void
  358 pop3catch(int s)
  359 {
  360    NYD_X; /* Signal handler */
  361    switch (s) {
  362    case SIGINT:
  363       /*n_err_sighdl(_("Interrupt during POP3 operation\n"));*/
  364       interrupts = 2; /* Force "Interrupt" message shall we onintr(0) */
  365       siglongjmp(_pop3_jmp, 1);
  366    case SIGPIPE:
  367       n_err_sighdl(_("Received SIGPIPE during POP3 operation\n"));
  368       break;
  369    }
  370 }
  371 
  372 static void
  373 _pop3_maincatch(int s)
  374 {
  375    NYD_X; /* Signal handler */
  376    n_UNUSED(s);
  377    if (interrupts == 0)
  378       n_err_sighdl(_("\n(Interrupt -- one more to abort operation)\n"));
  379    else {
  380       interrupts = 1;
  381       siglongjmp(_pop3_jmp, 1);
  382    }
  383 }
  384 
  385 static enum okay
  386 pop3_noop1(struct mailbox *mp)
  387 {
  388    enum okay rv;
  389    NYD_ENTER;
  390 
  391    POP3_OUT(rv, "NOOP" NETNL, MB_COMD, goto jleave);
  392    POP3_ANSWER(rv, goto jleave);
  393 jleave:
  394    NYD_LEAVE;
  395    return rv;
  396 }
  397 
  398 static void
  399 pop3alarm(int s)
  400 {
  401    sighandler_type volatile saveint, savepipe;
  402    NYD_X; /* Signal handler */
  403    n_UNUSED(s);
  404 
  405    if (_pop3_lock++ == 0) {
  406       hold_all_sigs();
  407       if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
  408          safe_signal(SIGINT, &_pop3_maincatch);
  409       savepipe = safe_signal(SIGPIPE, SIG_IGN);
  410       if (sigsetjmp(_pop3_jmp, 1)) {
  411          interrupts = 0;
  412          safe_signal(SIGINT, saveint);
  413          safe_signal(SIGPIPE, savepipe);
  414          goto jbrk;
  415       }
  416       if (savepipe != SIG_IGN)
  417          safe_signal(SIGPIPE, pop3catch);
  418       rele_all_sigs();
  419       if (pop3_noop1(&mb) != OKAY) {
  420          safe_signal(SIGINT, saveint);
  421          safe_signal(SIGPIPE, savepipe);
  422          goto jleave;
  423       }
  424       safe_signal(SIGINT, saveint);
  425       safe_signal(SIGPIPE, savepipe);
  426    }
  427 jbrk:
  428    alarm(_pop3_keepalive);
  429 jleave:
  430    --_pop3_lock;
  431 }
  432 
  433 static enum okay
  434 pop3_stat(struct mailbox *mp, off_t *size, int *cnt)
  435 {
  436    char const *cp;
  437    enum okay rv;
  438    NYD_ENTER;
  439 
  440    POP3_OUT(rv, "STAT" NETNL, MB_COMD, goto jleave);
  441    POP3_ANSWER(rv, goto jleave);
  442 
  443    for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
  444       ;
  445    while (*cp != '\0' && spacechar(*cp))
  446       ++cp;
  447 
  448    rv = STOP;
  449    if (*cp != '\0') {
  450       size_t i;
  451 
  452       if(n_idec_uiz_cp(&i, cp, 10, &cp) & n_IDEC_STATE_EMASK)
  453          goto jerr;
  454       if(i > INT_MAX)
  455          goto jerr;
  456       *cnt = (int)i;
  457 
  458       while(*cp != '\0' && !spacechar(*cp))
  459          ++cp;
  460       while(*cp != '\0' && spacechar(*cp))
  461          ++cp;
  462 
  463       if(*cp == '\0')
  464          goto jerr;
  465       if(n_idec_uiz_cp(&i, cp, 10, NULL) & n_IDEC_STATE_EMASK)
  466          goto jerr;
  467       *size = (off_t)i;
  468       rv = OKAY;
  469    }
  470 
  471    if (rv == STOP)
  472 jerr:
  473       n_err(_("Invalid POP3 STAT response: %s\n"), _pop3_buf);
  474 jleave:
  475    NYD_LEAVE;
  476    return rv;
  477 }
  478 
  479 static enum okay
  480 pop3_list(struct mailbox *mp, int n, size_t *size)
  481 {
  482    char o[LINESIZE], *cp;
  483    enum okay rv;
  484    NYD_ENTER;
  485 
  486    snprintf(o, sizeof o, "LIST %u" NETNL, n);
  487    POP3_OUT(rv, o, MB_COMD, goto jleave);
  488    POP3_ANSWER(rv, goto jleave);
  489 
  490    for (cp = _pop3_buf; *cp != '\0' && !spacechar(*cp); ++cp)
  491       ;
  492    while (*cp != '\0' && spacechar(*cp))
  493       ++cp;
  494    while (*cp != '\0' && !spacechar(*cp))
  495       ++cp;
  496    while (*cp != '\0' && spacechar(*cp))
  497       ++cp;
  498    if (*cp != '\0')
  499       n_idec_uiz_cp(size, cp, 10, NULL);
  500 jleave:
  501    NYD_LEAVE;
  502    return rv;
  503 }
  504 
  505 static void
  506 pop3_setptr(struct mailbox *mp, struct sockconn const *scp)
  507 {
  508    size_t i;
  509    enum needspec ns;
  510    NYD_ENTER;
  511 
  512    message = n_calloc(msgCount + 1, sizeof *message);
  513    message[msgCount].m_size = 0;
  514    message[msgCount].m_lines = 0;
  515    dot = message; /* (Just do it: avoid crash -- shall i now do ointr(0).. */
  516 
  517    for (i = 0; UICMP(z, i, <, msgCount); ++i) {
  518       struct message *m = message + i;
  519       m->m_flag = MUSED | MNEW | MNOFROM | MNEWEST;
  520       m->m_block = 0;
  521       m->m_offset = 0;
  522       m->m_size = m->m_xsize = 0;
  523    }
  524 
  525    for (i = 0; UICMP(z, i, <, msgCount); ++i)
  526       if (!pop3_list(mp, i + 1, &message[i].m_xsize))
  527          goto jleave;
  528 
  529    /* Force the load of all messages right now */
  530    ns = xok_blook(pop3_bulk_load, &scp->sc_url, OXM_ALL)
  531          ? NEED_BODY : NEED_HEADER;
  532    for (i = 0; UICMP(z, i, <, msgCount); ++i)
  533       if (!pop3_get(mp, message + i, ns))
  534          goto jleave;
  535 
  536    srelax_hold();
  537    for (i = 0; UICMP(z, i, <, msgCount); ++i) {
  538       struct message *m = message + i;
  539       char const *cp;
  540 
  541       if ((cp = hfield1("status", m)) != NULL)
  542          while (*cp != '\0') {
  543             if (*cp == 'R')
  544                m->m_flag |= MREAD;
  545             else if (*cp == 'O')
  546                m->m_flag &= ~MNEW;
  547             ++cp;
  548          }
  549 
  550       substdate(m);
  551       srelax();
  552    }
  553    srelax_rele();
  554 
  555    setdot(message);
  556 jleave:
  557    NYD_LEAVE;
  558 }
  559 
  560 static enum okay
  561 pop3_get(struct mailbox *mp, struct message *m, enum needspec volatile need)
  562 {
  563    char o[LINESIZE], *line, *lp;
  564    sighandler_type volatile saveint, savepipe;
  565    size_t linesize, linelen, size;
  566    int number, lines;
  567    int volatile emptyline;
  568    off_t offset;
  569    enum okay volatile rv;
  570    NYD_ENTER;
  571 
  572    line = NULL; /* TODO line pool */
  573    saveint = savepipe = SIG_IGN;
  574    linesize = 0;
  575    number = (int)PTR2SIZE(m - message + 1);
  576    emptyline = 0;
  577    rv = STOP;
  578 
  579    if (mp->mb_sock.s_fd < 0) {
  580       n_err(_("POP3 connection already closed\n"));
  581       ++_pop3_lock;
  582       goto jleave;
  583    }
  584 
  585    if (_pop3_lock++ == 0) {
  586       hold_all_sigs();
  587       if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
  588          safe_signal(SIGINT, &_pop3_maincatch);
  589       savepipe = safe_signal(SIGPIPE, SIG_IGN);
  590       if (sigsetjmp(_pop3_jmp, 1))
  591          goto jleave;
  592       if (savepipe != SIG_IGN)
  593          safe_signal(SIGPIPE, pop3catch);
  594       rele_all_sigs();
  595    }
  596 
  597    fseek(mp->mb_otf, 0L, SEEK_END);
  598    offset = ftell(mp->mb_otf);
  599 jretry:
  600    switch (need) {
  601    case NEED_HEADER:
  602       snprintf(o, sizeof o, "TOP %u 0" NETNL, number);
  603       break;
  604    case NEED_BODY:
  605       snprintf(o, sizeof o, "RETR %u" NETNL, number);
  606       break;
  607    case NEED_UNSPEC:
  608       abort(); /* XXX */
  609    }
  610    POP3_OUT(rv, o, MB_COMD | MB_MULT, goto jleave);
  611 
  612    if (pop3_answer(mp) == STOP) {
  613       if (need == NEED_HEADER) {
  614          /* The TOP POP3 command is optional, so retry with entire message */
  615          need = NEED_BODY;
  616          goto jretry;
  617       }
  618       goto jleave;
  619    }
  620 
  621    size = 0;
  622    lines = 0;
  623    while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
  624       if (line[0] == '.' && line[1] == NETNL[0] && line[2] == NETNL[1] &&
  625             line[3] == '\0') {
  626          mp->mb_active &= ~MB_MULT;
  627          break;
  628       }
  629       if (line[0] == '.') {
  630          lp = line + 1;
  631          --linelen;
  632       } else
  633          lp = line;
  634       /* TODO >>
  635        * Need to mask 'From ' lines. This cannot be done properly
  636        * since some servers pass them as 'From ' and others as
  637        * '>From '. Although one could identify the first kind of
  638        * server in principle, it is not possible to identify the
  639        * second as '>From ' may also come from a server of the
  640        * first type as actual data. So do what is absolutely
  641        * necessary only - mask 'From '.
  642        *
  643        * If the line is the first line of the message header, it
  644        * is likely a real 'From ' line. In this case, it is just
  645        * ignored since it violates all standards.
  646        * TODO i have *never* seen the latter?!?!?
  647        * TODO <<
  648        */
  649       /* Since we simply copy over data without doing any transfer
  650        * encoding reclassification/adjustment we *have* to perform
  651        * RFC 4155 compliant From_ quoting here */
  652       if (emptyline && is_head(lp, linelen, FAL0)) {
  653          putc('>', mp->mb_otf);
  654          ++size;
  655       }
  656       lines++;
  657       if (lp[linelen-1] == NETNL[1] &&
  658             (linelen == 1 || lp[linelen-2] == NETNL[0])) {
  659          emptyline = linelen <= 2;
  660          if (linelen > 2)
  661             fwrite(lp, 1, linelen - 2, mp->mb_otf);
  662          putc('\n', mp->mb_otf);
  663          size += linelen - 1;
  664       } else {
  665          emptyline = 0;
  666          fwrite(lp, 1, linelen, mp->mb_otf);
  667          size += linelen;
  668       }
  669    }
  670    if (!emptyline) {
  671       /* TODO This is very ugly; but some POP3 daemons don't end a
  672        * TODO message with NETNL NETNL, and we need \n\n for mbox format.
  673        * TODO That is to say we do it wrong here in order to get it right
  674        * TODO when send.c stuff or with MBOX handling, even though THIS
  675        * TODO line is solely a property of the MBOX database format! */
  676       putc('\n', mp->mb_otf);
  677       ++lines;
  678       ++size;
  679    }
  680    m->m_size = size;
  681    m->m_lines = lines;
  682    m->m_block = mailx_blockof(offset);
  683    m->m_offset = mailx_offsetof(offset);
  684    fflush(mp->mb_otf);
  685 
  686    switch (need) {
  687    case NEED_HEADER:
  688       m->m_content_info |= CI_HAVE_HEADER;
  689       break;
  690    case NEED_BODY:
  691       m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY;
  692       m->m_xlines = m->m_lines;
  693       m->m_xsize = m->m_size;
  694       break;
  695    case NEED_UNSPEC:
  696       break;
  697    }
  698 
  699    rv = OKAY;
  700 jleave:
  701    if (line != NULL)
  702       n_free(line);
  703    if (saveint != SIG_IGN)
  704       safe_signal(SIGINT, saveint);
  705    if (savepipe != SIG_IGN)
  706       safe_signal(SIGPIPE, savepipe);
  707    --_pop3_lock;
  708    NYD_LEAVE;
  709    if (interrupts)
  710       n_raise(SIGINT);
  711    return rv;
  712 }
  713 
  714 static enum okay
  715 pop3_exit(struct mailbox *mp)
  716 {
  717    enum okay rv;
  718    NYD_ENTER;
  719 
  720    POP3_OUT(rv, "QUIT" NETNL, MB_COMD, goto jleave);
  721    POP3_ANSWER(rv, goto jleave);
  722 jleave:
  723    NYD_LEAVE;
  724    return rv;
  725 }
  726 
  727 static enum okay
  728 pop3_delete(struct mailbox *mp, int n)
  729 {
  730    char o[LINESIZE];
  731    enum okay rv;
  732    NYD_ENTER;
  733 
  734    snprintf(o, sizeof o, "DELE %u" NETNL, n);
  735    POP3_OUT(rv, o, MB_COMD, goto jleave);
  736    POP3_ANSWER(rv, goto jleave);
  737 jleave:
  738    NYD_LEAVE;
  739    return rv;
  740 }
  741 
  742 static enum okay
  743 pop3_update(struct mailbox *mp)
  744 {
  745    struct message *m;
  746    int dodel, c, gotcha, held;
  747    NYD_ENTER;
  748 
  749    if (!(n_pstate & n_PS_EDIT)) {
  750       holdbits();
  751       c = 0;
  752       for (m = message; PTRCMP(m, <, message + msgCount); ++m)
  753          if (m->m_flag & MBOX)
  754             ++c;
  755       if (c > 0)
  756          makembox();
  757    }
  758 
  759    gotcha = held = 0;
  760    for (m = message; PTRCMP(m, <, message + msgCount); ++m) {
  761       if (n_pstate & n_PS_EDIT)
  762          dodel = m->m_flag & MDELETED;
  763       else
  764          dodel = !((m->m_flag & MPRESERVE) || !(m->m_flag & MTOUCH));
  765       if (dodel) {
  766          pop3_delete(mp, PTR2SIZE(m - message + 1));
  767          ++gotcha;
  768       } else
  769          ++held;
  770    }
  771 
  772    /* C99 */{
  773       char const *dnq;
  774 
  775       dnq = n_shexp_quote_cp(displayname, FAL0);
  776 
  777       if (gotcha && (n_pstate & n_PS_EDIT)) {
  778          fprintf(n_stdout, _("%s "), dnq);
  779          fprintf(n_stdout, (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
  780             ? _("complete\n") : _("updated\n"));
  781       } else if (held && !(n_pstate & n_PS_EDIT)) {
  782          if (held == 1)
  783             fprintf(n_stdout, _("Held 1 message in %s\n"), dnq);
  784          else
  785             fprintf(n_stdout, _("Held %d messages in %s\n"), held, dnq);
  786       }
  787    }
  788    fflush(n_stdout);
  789    NYD_LEAVE;
  790    return OKAY;
  791 }
  792 
  793 FL enum okay
  794 pop3_noop(void)
  795 {
  796    sighandler_type volatile saveint, savepipe;
  797    enum okay volatile rv = STOP;
  798    NYD_ENTER;
  799 
  800    _pop3_lock = 1;
  801    hold_all_sigs();
  802    if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
  803       safe_signal(SIGINT, &_pop3_maincatch);
  804    savepipe = safe_signal(SIGPIPE, SIG_IGN);
  805    if (sigsetjmp(_pop3_jmp, 1) == 0) {
  806       if (savepipe != SIG_IGN)
  807          safe_signal(SIGPIPE, pop3catch);
  808       rele_all_sigs();
  809       rv = pop3_noop1(&mb);
  810    }
  811    safe_signal(SIGINT, saveint);
  812    safe_signal(SIGPIPE, savepipe);
  813    _pop3_lock = 0;
  814    NYD_LEAVE;
  815    return rv;
  816 }
  817 
  818 FL int
  819 pop3_setfile(char const *who, char const *server, enum fedit_mode fm)
  820 {
  821    struct sockconn sc;
  822    sighandler_type saveint, savepipe;
  823    char const *cp;
  824    int volatile rv;
  825    NYD_ENTER;
  826 
  827    rv = 1;
  828    if (fm & FEDIT_NEWMAIL)
  829       goto jleave;
  830    rv = -1;
  831 
  832    if (!url_parse(&sc.sc_url, CPROTO_POP3, server))
  833       goto jleave;
  834    if (!ok_blook(v15_compat) &&
  835          (!(sc.sc_url.url_flags & n_URL_HAD_USER) ||
  836             sc.sc_url.url_pass.s != NULL)) {
  837       n_err(_("New-style URL used without *v15-compat* being set\n"));
  838       goto jleave;
  839    }
  840 
  841    if (!(ok_blook(v15_compat) ? ccred_lookup(&sc.sc_cred, &sc.sc_url)
  842          : ccred_lookup_old(&sc.sc_cred, CPROTO_POP3,
  843             ((sc.sc_url.url_flags & n_URL_HAD_USER) ? sc.sc_url.url_eu_h_p.s
  844              : sc.sc_url.url_u_h_p.s))))
  845       goto jleave;
  846 
  847    if (!quit(FAL0))
  848       goto jleave;
  849 
  850    if (!sopen(&sc.sc_sock, &sc.sc_url))
  851       goto jleave;
  852 
  853    rv = 1;
  854 
  855    if (fm & FEDIT_SYSBOX)
  856       n_pstate &= ~n_PS_EDIT;
  857    else
  858       n_pstate |= n_PS_EDIT;
  859    if (mb.mb_sock.s_fd >= 0)
  860       sclose(&mb.mb_sock);
  861    if (mb.mb_itf) {
  862       fclose(mb.mb_itf);
  863       mb.mb_itf = NULL;
  864    }
  865    if (mb.mb_otf) {
  866       fclose(mb.mb_otf);
  867       mb.mb_otf = NULL;
  868    }
  869 
  870    initbox(sc.sc_url.url_p_u_h_p);
  871    mb.mb_type = MB_VOID;
  872    _pop3_lock = 1;
  873    mb.mb_sock = sc.sc_sock;
  874 
  875    saveint = safe_signal(SIGINT, SIG_IGN);
  876    savepipe = safe_signal(SIGPIPE, SIG_IGN);
  877    if (sigsetjmp(_pop3_jmp, 1)) {
  878       sclose(&mb.mb_sock);
  879       n_err(_("POP3 connection closed\n"));
  880       safe_signal(SIGINT, saveint);
  881       safe_signal(SIGPIPE, savepipe);
  882       _pop3_lock = 0;
  883       rv = -1;
  884       if (interrupts > 0)
  885          n_raise(SIGINT);
  886       goto jleave;
  887    }
  888    if (saveint != SIG_IGN)
  889       safe_signal(SIGINT, pop3catch);
  890    if (savepipe != SIG_IGN)
  891       safe_signal(SIGPIPE, pop3catch);
  892 
  893    if ((cp = xok_vlook(pop3_keepalive, &sc.sc_url, OXM_ALL)) != NULL) {
  894       n_idec_si32_cp(&_pop3_keepalive, cp, 10, NULL);
  895       if (_pop3_keepalive > 0) {
  896          _pop3_savealrm = safe_signal(SIGALRM, pop3alarm);
  897          alarm(_pop3_keepalive);
  898       }
  899    }
  900 
  901    mb.mb_sock.s_desc = (sc.sc_url.url_flags & n_URL_TLS_REQUIRED)
  902          ? "POP3S" : "POP3";
  903    mb.mb_sock.s_onclose = pop3_timer_off;
  904    if (_pop3_login(&mb, &sc) != OKAY ||
  905          pop3_stat(&mb, &mailsize, &msgCount) != OKAY) {
  906       sclose(&mb.mb_sock);
  907       pop3_timer_off();
  908       safe_signal(SIGINT, saveint);
  909       safe_signal(SIGPIPE, savepipe);
  910       _pop3_lock = 0;
  911       goto jleave;
  912    }
  913 
  914    setmsize(msgCount);
  915    mb.mb_type = MB_POP3;
  916    mb.mb_perm = ((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY))
  917          ? 0 : MB_DELE;
  918    pop3_setptr(&mb, &sc);
  919 
  920    /*if (!(fm & FEDIT_NEWMAIL)) */{
  921       n_pstate &= ~n_PS_SAW_COMMAND;
  922       n_pstate |= n_PS_SETFILE_OPENED;
  923    }
  924 
  925    safe_signal(SIGINT, saveint);
  926    safe_signal(SIGPIPE, savepipe);
  927    _pop3_lock = 0;
  928 
  929    if ((n_poption & (n_PO_EXISTONLY | n_PO_HEADERLIST)) == n_PO_EXISTONLY) {
  930       rv = (msgCount == 0);
  931       goto jleave;
  932    }
  933 
  934    if (!(n_pstate & n_PS_EDIT) && msgCount == 0) {
  935       if (!ok_blook(emptystart))
  936          n_err(_("No mail for %s at %s\n"), who, sc.sc_url.url_p_eu_h_p);
  937       goto jleave;
  938    }
  939 
  940    rv = 0;
  941 jleave:
  942    NYD_LEAVE;
  943    return rv;
  944 }
  945 
  946 FL enum okay
  947 pop3_header(struct message *m)
  948 {
  949    enum okay rv;
  950    NYD_ENTER;
  951 
  952    /* TODO no URL here, no OXM possible; (however it is used in setfile()..) */
  953    rv = pop3_get(&mb, m, (ok_blook(pop3_bulk_load) ? NEED_BODY : NEED_HEADER));
  954    NYD_LEAVE;
  955    return rv;
  956 }
  957 
  958 FL enum okay
  959 pop3_body(struct message *m)
  960 {
  961    enum okay rv;
  962    NYD_ENTER;
  963 
  964    rv = pop3_get(&mb, m, NEED_BODY);
  965    NYD_LEAVE;
  966    return rv;
  967 }
  968 
  969 FL bool_t
  970 pop3_quit(bool_t hold_sigs_on)
  971 {
  972    sighandler_type volatile saveint, savepipe;
  973    bool_t rv;
  974    NYD_ENTER;
  975 
  976    if(hold_sigs_on)
  977       rele_sigs();
  978 
  979    rv = FAL0;
  980 
  981    if (mb.mb_sock.s_fd < 0) {
  982       n_err(_("POP3 connection already closed\n"));
  983       rv = TRU1;
  984       goto jleave;
  985    }
  986 
  987    _pop3_lock = 1;
  988    saveint = safe_signal(SIGINT, SIG_IGN);
  989    savepipe = safe_signal(SIGPIPE, SIG_IGN);
  990    if (sigsetjmp(_pop3_jmp, 1)) {
  991       safe_signal(SIGINT, saveint);
  992       safe_signal(SIGPIPE, savepipe);
  993       _pop3_lock = 0;
  994       interrupts = 0;
  995       goto jleave;
  996    }
  997    if (saveint != SIG_IGN)
  998       safe_signal(SIGINT, pop3catch);
  999    if (savepipe != SIG_IGN)
 1000       safe_signal(SIGPIPE, pop3catch);
 1001    pop3_update(&mb);
 1002    pop3_exit(&mb);
 1003    sclose(&mb.mb_sock);
 1004    safe_signal(SIGINT, saveint);
 1005    safe_signal(SIGPIPE, savepipe);
 1006    _pop3_lock = 0;
 1007 
 1008    rv = TRU1;
 1009 jleave:
 1010    if(hold_sigs_on)
 1011       hold_sigs();
 1012    NYD_LEAVE;
 1013    return rv;
 1014 }
 1015 #endif /* HAVE_POP3 */
 1016 
 1017 /* s-it-mode */