busybox/mailutils/popmaildir.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * popmaildir: a simple yet powerful POP3 client
   4 * Delivers contents of remote mailboxes to local Maildir
   5 *
   6 * Inspired by original utility by Nikola Vladov
   7 *
   8 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
   9 *
  10 * Licensed under GPLv2, see file LICENSE in this source tree.
  11 */
  12#include "libbb.h"
  13#include "mail.h"
  14
  15static void pop3_checkr(const char *fmt, const char *param, char **ret)
  16{
  17        const char *msg = command(fmt, param);
  18        char *answer = xmalloc_fgetline(stdin);
  19        if (answer && '+' == answer[0]) {
  20                if (timeout)
  21                        alarm(0);
  22                if (ret) {
  23                        // skip "+OK "
  24                        memmove(answer, answer + 4, strlen(answer) - 4);
  25                        *ret = answer;
  26                } else
  27                        free(answer);
  28                return;
  29        }
  30        bb_error_msg_and_die("%s failed: %s", msg, answer);
  31}
  32
  33static void pop3_check(const char *fmt, const char *param)
  34{
  35        pop3_checkr(fmt, param, NULL);
  36}
  37
  38int popmaildir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  39int popmaildir_main(int argc UNUSED_PARAM, char **argv)
  40{
  41        char *buf;
  42        unsigned nmsg;
  43        char *hostname;
  44        pid_t pid;
  45        const char *retr;
  46#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
  47        const char *delivery;
  48#endif
  49        unsigned opt_nlines = 0;
  50
  51        enum {
  52                OPT_b = 1 << 0,         // -b binary mode. Ignored
  53                OPT_d = 1 << 1,         // -d,-dd,-ddd debug. Ignored
  54                OPT_m = 1 << 2,         // -m show used memory. Ignored
  55                OPT_V = 1 << 3,         // -V version. Ignored
  56                OPT_c = 1 << 4,         // -c use tcpclient. Ignored
  57                OPT_a = 1 << 5,         // -a use APOP protocol
  58                OPT_s = 1 << 6,         // -s skip authorization
  59                OPT_T = 1 << 7,         // -T get messages with TOP instead with RETR
  60                OPT_k = 1 << 8,         // -k keep retrieved messages on the server
  61                OPT_t = 1 << 9,         // -t90 set timeout to 90 sec
  62                OPT_R = 1 << 10,        // -R20000 remove old messages on the server >= 20000 bytes (requires -k). Ignored
  63                OPT_Z = 1 << 11,        // -Z11-23 remove messages from 11 to 23 (dangerous). Ignored
  64                OPT_L = 1 << 12,        // -L50000 not retrieve new messages >= 50000 bytes. Ignored
  65                OPT_H = 1 << 13,        // -H30 type first 30 lines of a message; (-L12000 -H30). Ignored
  66                OPT_M = 1 << 14,        // -M\"program arg1 arg2 ...\"; deliver by program. Treated like -F
  67                OPT_F = 1 << 15,        // -F\"program arg1 arg2 ...\"; filter by program. Treated like -M
  68        };
  69
  70        // init global variables
  71        INIT_G();
  72
  73        // parse options
  74        opt_complementary = "-1:dd:t+:R+:L+:H+";
  75        opts = getopt32(argv,
  76                "bdmVcasTkt:" "R:Z:L:H:" IF_FEATURE_POPMAILDIR_DELIVERY("M:F:"),
  77                &timeout, NULL, NULL, NULL, &opt_nlines
  78                IF_FEATURE_POPMAILDIR_DELIVERY(, &delivery, &delivery) // we treat -M and -F the same
  79        );
  80        //argc -= optind;
  81        argv += optind;
  82
  83        // get auth info
  84        if (!(opts & OPT_s))
  85                get_cred_or_die(STDIN_FILENO);
  86
  87        // goto maildir
  88        xchdir(*argv++);
  89
  90        // launch connect helper, if any
  91        if (*argv)
  92                launch_helper((const char **)argv);
  93
  94        // get server greeting
  95        pop3_checkr(NULL, NULL, &buf);
  96
  97        // authenticate (if no -s given)
  98        if (!(opts & OPT_s)) {
  99                // server supports APOP and we want it?
 100                if ('<' == buf[0] && (opts & OPT_a)) {
 101                        union { // save a bit of stack
 102                                md5_ctx_t ctx;
 103                                char hex[16 * 2 + 1];
 104                        } md5;
 105                        uint32_t res[16 / 4];
 106
 107                        char *s = strchr(buf, '>');
 108                        if (s)
 109                                s[1] = '\0';
 110                        // get md5 sum of "<stamp>password" string
 111                        md5_begin(&md5.ctx);
 112                        md5_hash(&md5.ctx, buf, strlen(buf));
 113                        md5_hash(&md5.ctx, G.pass, strlen(G.pass));
 114                        md5_end(&md5.ctx, res);
 115                        *bin2hex(md5.hex, (char*)res, 16) = '\0';
 116                        // APOP
 117                        s = xasprintf("%s %s", G.user, md5.hex);
 118                        pop3_check("APOP %s", s);
 119                        free(s);
 120                        free(buf);
 121                // server ignores APOP -> use simple text authentication
 122                } else {
 123                        // USER
 124                        pop3_check("USER %s", G.user);
 125                        // PASS
 126                        pop3_check("PASS %s", G.pass);
 127                }
 128        }
 129
 130        // get mailbox statistics
 131        pop3_checkr("STAT", NULL, &buf);
 132
 133        // prepare message filename suffix
 134        hostname = safe_gethostname();
 135        pid = getpid();
 136
 137        // get messages counter
 138        // NOTE: we don't use xatou(buf) since buf is "nmsg nbytes"
 139        // we only need nmsg and atoi is just exactly what we need
 140        // if atoi fails to convert buf into number it returns 0
 141        // in this case the following loop simply will not be executed
 142        nmsg = atoi(buf);
 143        free(buf);
 144
 145        // loop through messages
 146        retr = (opts & OPT_T) ? xasprintf("TOP %%u %u", opt_nlines) : "RETR %u";
 147        for (; nmsg; nmsg--) {
 148
 149                char *filename;
 150                char *target;
 151                char *answer;
 152                FILE *fp;
 153#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
 154                int rc;
 155#endif
 156                // generate unique filename
 157                filename  = xasprintf("tmp/%llu.%u.%s",
 158                        monotonic_us(), (unsigned)pid, hostname);
 159
 160                // retrieve message in ./tmp/ unless filter is specified
 161                pop3_check(retr, (const char *)(ptrdiff_t)nmsg);
 162
 163#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
 164                // delivery helper ordered? -> setup pipe
 165                if (opts & (OPT_F|OPT_M)) {
 166                        // helper will have $FILENAME set to filename
 167                        xsetenv("FILENAME", filename);
 168                        fp = popen(delivery, "w");
 169                        unsetenv("FILENAME");
 170                        if (!fp) {
 171                                bb_perror_msg("delivery helper");
 172                                break;
 173                        }
 174                } else
 175#endif
 176                // create and open file filename
 177                fp = xfopen_for_write(filename);
 178
 179                // copy stdin to fp (either filename or delivery helper)
 180                while ((answer = xmalloc_fgets_str(stdin, "\r\n")) != NULL) {
 181                        char *s = answer;
 182                        if ('.' == answer[0]) {
 183                                if ('.' == answer[1])
 184                                        s++;
 185                                else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3])
 186                                        break;
 187                        }
 188                        //*strchrnul(s, '\r') = '\n';
 189                        fputs(s, fp);
 190                        free(answer);
 191                }
 192
 193#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
 194                // analyse delivery status
 195                if (opts & (OPT_F|OPT_M)) {
 196                        rc = pclose(fp);
 197                        if (99 == rc) // 99 means bail out
 198                                break;
 199//                      if (rc) // !0 means skip to the next message
 200                                goto skip;
 201//                      // 0 means continue
 202                } else {
 203                        // close filename
 204                        fclose(fp);
 205                }
 206#endif
 207
 208                // delete message from server
 209                if (!(opts & OPT_k))
 210                        pop3_check("DELE %u", (const char*)(ptrdiff_t)nmsg);
 211
 212                // atomically move message to ./new/
 213                target = xstrdup(filename);
 214                strncpy(target, "new", 3);
 215                // ... or just stop receiving on failure
 216                if (rename_or_warn(filename, target))
 217                        break;
 218                free(target);
 219
 220#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
 221 skip:
 222#endif
 223                free(filename);
 224        }
 225
 226        // Bye
 227        pop3_check("QUIT", NULL);
 228
 229        if (ENABLE_FEATURE_CLEAN_UP) {
 230                free(G.user);
 231                free(G.pass);
 232        }
 233
 234        return EXIT_SUCCESS;
 235}
 236