"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/pop3.c" (25 Mar 2018, 26243 Bytes) of package /linux/misc/s-nail-14.9.10.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.9_vs_14.9.10.

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