"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/pop3.c" (16 Feb 2018, 26180 Bytes) of package /linux/misc/s-nail-14.9.7.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.6_vs_14.9.7.

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