/*
* Heirloom mailx - a mail user agent derived from Berkeley Mail.
*
* Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
*/
/*
* Copyright (c) 2004
* Gunnar Ritter. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Gunnar Ritter
* and his contributors.
* 4. Neither the name of Gunnar Ritter nor the names of his contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
#ifdef DOSCCS
static char sccsid[] = "@(#)imap.c 1.218 (gritter) 3/17/07";
#endif
#endif /* not lint */
#include "config.h"
/*
* Mail -- a mail program
*
* IMAP v4r1 client following RFC 2060.
*/
#include "rcv.h"
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#ifdef HAVE_SOCKETS
#include "md5.h"
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif /* HAVE_ARPA_INET_H */
#include "extern.h"
static int verbose;
#define IMAP_ANSWER() { \
if (mp->mb_type != MB_CACHE) { \
enum okay ok = OKAY; \
while (mp->mb_active & MB_COMD) \
ok = imap_answer(mp, 1); \
if (ok == STOP) \
return STOP; \
} \
}
#define IMAP_OUT(x, y, action) \
{ \
if (mp->mb_type != MB_CACHE) { \
if (imap_finish(mp) == STOP) \
return STOP; \
if (verbose) \
fprintf(stderr, ">>> %s", x); \
mp->mb_active |= (y); \
if (swrite(&mp->mb_sock, x) == STOP) \
action; \
} else { \
if (queuefp != NULL) \
fputs(x, queuefp); \
} \
}
static struct record {
struct record *rec_next;
unsigned long rec_count;
enum rec_type {
REC_EXISTS,
REC_EXPUNGE
} rec_type;
} *record, *recend;
static enum {
RESPONSE_TAGGED,
RESPONSE_DATA,
RESPONSE_FATAL,
RESPONSE_CONT,
RESPONSE_ILLEGAL
} response_type;
static enum {
RESPONSE_OK,
RESPONSE_NO,
RESPONSE_BAD,
RESPONSE_PREAUTH,
RESPONSE_BYE,
RESPONSE_OTHER,
RESPONSE_UNKNOWN
} response_status;
static char *responded_tag;
static char *responded_text;
static char *responded_other_text;
static long responded_other_number;
static enum {
MAILBOX_DATA_FLAGS,
MAILBOX_DATA_LIST,
MAILBOX_DATA_LSUB,
MAILBOX_DATA_MAILBOX,
MAILBOX_DATA_SEARCH,
MAILBOX_DATA_STATUS,
MAILBOX_DATA_EXISTS,
MAILBOX_DATA_RECENT,
MESSAGE_DATA_EXPUNGE,
MESSAGE_DATA_FETCH,
CAPABILITY_DATA,
RESPONSE_OTHER_UNKNOWN
} response_other;
static enum list_attributes {
LIST_NONE = 000,
LIST_NOINFERIORS = 001,
LIST_NOSELECT = 002,
LIST_MARKED = 004,
LIST_UNMARKED = 010
} list_attributes;
static int list_hierarchy_delimiter;
static char *list_name;
struct list_item {
struct list_item *l_next;
char *l_name;
char *l_base;
enum list_attributes l_attr;
int l_delim;
int l_level;
int l_has_children;
};
static char *imapbuf;
static size_t imapbufsize;
static sigjmp_buf imapjmp;
static sighandler_type savealrm;
static int reset_tio;
static struct termios otio;
static int imapkeepalive;
static long had_exists = -1;
static long had_expunge = -1;
static long expunged_messages;
static volatile int imaplock;
static int same_imap_account;
static void imap_other_get(char *pp);
static void imap_response_get(const char **cp);
static void imap_response_parse(void);
static enum okay imap_answer(struct mailbox *mp, int errprnt);
static enum okay imap_parse_list(void);
static enum okay imap_finish(struct mailbox *mp);
static void imap_timer_off(void);
static void imapcatch(int s);
static void maincatch(int s);
static enum okay imap_noop1(struct mailbox *mp);
static void rec_queue(enum rec_type type, unsigned long count);
static enum okay rec_dequeue(void);
static void rec_rmqueue(void);
static void imapalarm(int s);
static int imap_use_starttls(const char *uhp);
static enum okay imap_preauth(struct mailbox *mp, const char *xserver,
const char *uhp);
static enum okay imap_capability(struct mailbox *mp);
static enum okay imap_auth(struct mailbox *mp, const char *uhp,
char *xuser, const char *pass);
static enum okay imap_cram_md5(struct mailbox *mp,
char *xuser, const char *xpass);
static enum okay imap_login(struct mailbox *mp, char *xuser, const char *xpass);
#ifdef USE_GSSAPI
static enum okay imap_gss(struct mailbox *mp, char *user);
#endif /* USE_GSSAPI */
static enum okay imap_flags(struct mailbox *mp, unsigned X, unsigned Y);
static void imap_init(struct mailbox *mp, int n);
static void imap_setptr(struct mailbox *mp, int newmail, int transparent,
int *prevcount);
static char *imap_have_password(const char *server);
static void imap_split(char **server, const char **sp, int *use_ssl,
const char **cp, char **uhp, char **mbx,
const char **pass, char **user);
static int imap_setfile1(const char *xserver, int newmail, int isedit,
int transparent);
static int imap_fetchdata(struct mailbox *mp, struct message *m,
size_t expected, int need,
const char *head, size_t headsize, long headlines);
static void imap_putstr(struct mailbox *mp, struct message *m,
const char *str,
const char *head, size_t headsize, long headlines);
static enum okay imap_get(struct mailbox *mp, struct message *m,
enum needspec need);
static void commitmsg(struct mailbox *mp, struct message *to,
struct message from, enum havespec have);
static enum okay imap_fetchheaders(struct mailbox *mp, struct message *m,
int bot, int top);
static enum okay imap_exit(struct mailbox *mp);
static enum okay imap_delete(struct mailbox *mp, int n, struct message *m, int
needstat);
static enum okay imap_close(struct mailbox *mp);
static enum okay imap_update(struct mailbox *mp);
static enum okay imap_store(struct mailbox *mp, struct message *m,
int n, int c, const char *sp, int needstat);
static enum okay imap_unstore(struct message *m, int n, const char *flag);
static const char *tag(int new);
static char *imap_putflags(int f);
static void imap_getflags(const char *cp, char **xp, enum mflag *f);
static enum okay imap_append1(struct mailbox *mp, const char *name, FILE *fp,
off_t off1, long xsize, enum mflag flag, time_t t);
static enum okay imap_append0(struct mailbox *mp, const char *name, FILE *fp);
static enum okay imap_list1(struct mailbox *mp, const char *base,
struct list_item **list, struct list_item **lend, int level);
static enum okay imap_list(struct mailbox *mp, const char *base,
int strip, FILE *fp);
static void dopr(FILE *fp);
static enum okay imap_copy1(struct mailbox *mp, struct message *m, int n,
const char *name);
static enum okay imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
unsigned long *olduid, unsigned long *newuid);
static enum okay imap_appenduid_parse(const char *cp,
unsigned long *uidvalidity, unsigned long *uid);
static enum okay imap_copyuid(struct mailbox *mp, struct message *m,
const char *name);
static enum okay imap_appenduid(struct mailbox *mp, FILE *fp, time_t t,
long off1, long xsize, long size, long lines,
int flag, const char *name);
static enum okay imap_appenduid_cached(struct mailbox *mp, FILE *fp);
static enum okay imap_search2(struct mailbox *mp, struct message *m,
int count, const char *spec, int f);
static enum okay imap_remove1(struct mailbox *mp, const char *name);
static enum okay imap_rename1(struct mailbox *mp, const char *old,
const char *new);
static char *imap_strex(const char *cp, char **xp);
static enum okay check_expunged(void);
static void
imap_other_get(char *pp)
{
char *xp;
if (ascncasecmp(pp, "FLAGS ", 6) == 0) {
pp += 6;
response_other = MAILBOX_DATA_FLAGS;
} else if (ascncasecmp(pp, "LIST ", 5) == 0) {
pp += 5;
response_other = MAILBOX_DATA_LIST;
} else if (ascncasecmp(pp, "LSUB ", 5) == 0) {
pp += 5;
response_other = MAILBOX_DATA_LSUB;
} else if (ascncasecmp(pp, "MAILBOX ", 8) == 0) {
pp += 8;
response_other = MAILBOX_DATA_MAILBOX;
} else if (ascncasecmp(pp, "SEARCH ", 7) == 0) {
pp += 7;
response_other = MAILBOX_DATA_SEARCH;
} else if (ascncasecmp(pp, "STATUS ", 7) == 0) {
pp += 7;
response_other = MAILBOX_DATA_STATUS;
} else if (ascncasecmp(pp, "CAPABILITY ", 11) == 0) {
pp += 11;
response_other = CAPABILITY_DATA;
} else {
responded_other_number = strtol(pp, &xp, 10);
while (*xp == ' ')
xp++;
if (ascncasecmp(xp, "EXISTS\r\n", 8) == 0) {
response_other = MAILBOX_DATA_EXISTS;
} else if (ascncasecmp(xp, "RECENT\r\n", 8) == 0) {
response_other = MAILBOX_DATA_RECENT;
} else if (ascncasecmp(xp, "EXPUNGE\r\n", 9) == 0) {
response_other = MESSAGE_DATA_EXPUNGE;
} else if (ascncasecmp(xp, "FETCH ", 6) == 0) {
pp = &xp[6];
response_other = MESSAGE_DATA_FETCH;
} else
response_other = RESPONSE_OTHER_UNKNOWN;
}
responded_other_text = pp;
}
static void
imap_response_get(const char **cp)
{
if (ascncasecmp(*cp, "OK ", 3) == 0) {
*cp += 3;
response_status = RESPONSE_OK;
} else if (ascncasecmp(*cp, "NO ", 3) == 0) {
*cp += 3;
response_status = RESPONSE_NO;
} else if (ascncasecmp(*cp, "BAD ", 4) == 0) {
*cp += 4;
response_status = RESPONSE_BAD;
} else if (ascncasecmp(*cp, "PREAUTH ", 8) == 0) {
*cp += 8;
response_status = RESPONSE_PREAUTH;
} else if (ascncasecmp(*cp, "BYE ", 4) == 0) {
*cp += 4;
response_status = RESPONSE_BYE;
} else
response_status = RESPONSE_OTHER;
}
static void
imap_response_parse(void)
{
static char *parsebuf;
static size_t parsebufsize;
const char *ip = imapbuf;
char *pp;
if (parsebufsize < imapbufsize) {
free(parsebuf);
parsebuf = smalloc(parsebufsize = imapbufsize);
}
strcpy(parsebuf, imapbuf);
pp = parsebuf;
switch (*ip) {
case '+':
response_type = RESPONSE_CONT;
ip++;
pp++;
while (*ip == ' ') {
ip++;
pp++;
}
break;
case '*':
ip++;
pp++;
while (*ip == ' ') {
ip++;
pp++;
}
imap_response_get(&ip);
pp = &parsebuf[ip - imapbuf];
switch (response_status) {
case RESPONSE_BYE:
response_type = RESPONSE_FATAL;
break;
default:
response_type = RESPONSE_DATA;
}
break;
default:
responded_tag = parsebuf;
while (*pp && *pp != ' ')
pp++;
if (*pp == '\0') {
response_type = RESPONSE_ILLEGAL;
break;
}
*pp++ = '\0';
while (*pp && *pp == ' ')
pp++;
if (*pp == '\0') {
response_type = RESPONSE_ILLEGAL;
break;
}
ip = &imapbuf[pp - parsebuf];
response_type = RESPONSE_TAGGED;
imap_response_get(&ip);
pp = &parsebuf[ip - imapbuf];
}
responded_text = pp;
if (response_type != RESPONSE_CONT &&
response_type != RESPONSE_ILLEGAL &&
response_status == RESPONSE_OTHER)
imap_other_get(pp);
}
static enum okay
imap_answer(struct mailbox *mp, int errprnt)
{
int i, complete;
enum okay ok = STOP;
if (mp->mb_type == MB_CACHE)
return OKAY;
again: if (sgetline(&imapbuf, &imapbufsize, NULL, &mp->mb_sock) > 0) {
if (verbose)
fputs(imapbuf, stderr);
imap_response_parse();
if (response_type == RESPONSE_ILLEGAL)
goto again;
if (response_type == RESPONSE_CONT)
return OKAY;
if (response_status == RESPONSE_OTHER) {
if (response_other == MAILBOX_DATA_EXISTS) {
had_exists = responded_other_number;
rec_queue(REC_EXISTS, responded_other_number);
if (had_expunge > 0)
had_expunge = 0;
} else if (response_other == MESSAGE_DATA_EXPUNGE) {
rec_queue(REC_EXPUNGE, responded_other_number);
if (had_expunge < 0)
had_expunge = 0;
had_expunge++;
expunged_messages++;
}
}
complete = 0;
if (response_type == RESPONSE_TAGGED) {
if (asccasecmp(responded_tag, tag(0)) == 0)
complete |= 1;
else
goto again;
}
switch (response_status) {
case RESPONSE_PREAUTH:
mp->mb_active &= ~MB_PREAUTH;
/*FALLTHRU*/
case RESPONSE_OK:
okay: ok = OKAY;
complete |= 2;
break;
case RESPONSE_NO:
case RESPONSE_BAD:
stop: ok = STOP;
complete |= 2;
if (errprnt)
fprintf(stderr, catgets(catd, CATSET, 218,
"IMAP error: %s"), responded_text);
break;
case RESPONSE_UNKNOWN: /* does not happen */
case RESPONSE_BYE:
i = mp->mb_active;
mp->mb_active = MB_NONE;
if (i & MB_BYE)
goto okay;
else
goto stop;
case RESPONSE_OTHER:
ok = OKAY;
}
if (response_status != RESPONSE_OTHER &&
ascncasecmp(responded_text, "[ALERT] ", 8) == 0)
fprintf(stderr, "IMAP alert: %s", &responded_text[8]);
if (complete == 3)
mp->mb_active &= ~MB_COMD;
} else {
ok = STOP;
mp->mb_active = MB_NONE;
}
return ok;
}
static enum okay
imap_parse_list(void)
{
char *cp;
cp = responded_other_text;
list_attributes = LIST_NONE;
if (*cp == '(') {
while (*cp && *cp != ')') {
if (*cp == '\\') {
if (ascncasecmp(&cp[1], "Noinferiors ", 12)
== 0) {
list_attributes |= LIST_NOINFERIORS;
cp += 12;
} else if (ascncasecmp(&cp[1], "Noselect ", 9)
== 0) {
list_attributes |= LIST_NOSELECT;
cp += 9;
} else if (ascncasecmp(&cp[1], "Marked ", 7)
== 0) {
list_attributes |= LIST_MARKED;
cp += 7;
} else if (ascncasecmp(&cp[1], "Unmarked ", 9)
== 0) {
list_attributes |= LIST_UNMARKED;
cp += 9;
}
}
cp++;
}
if (*++cp != ' ')
return STOP;
while (*cp == ' ')
cp++;
}
list_hierarchy_delimiter = EOF;
if (*cp == '"') {
if (*++cp == '\\')
cp++;
list_hierarchy_delimiter = *cp++ & 0377;
if (cp[0] != '"' || cp[1] != ' ')
return STOP;
cp++;
} else if (cp[0] == 'N' && cp[1] == 'I' && cp[2] == 'L' &&
cp[3] == ' ') {
list_hierarchy_delimiter = EOF;
cp += 3;
}
while (*cp == ' ')
cp++;
list_name = cp;
while (*cp && *cp != '\r')
cp++;
*cp = '\0';
return OKAY;
}
static enum okay
imap_finish(struct mailbox *mp)
{
while (mp->mb_sock.s_fd > 0 && mp->mb_active & MB_COMD)
imap_answer(mp, 1);
return OKAY;
}
static void
imap_timer_off(void)
{
if (imapkeepalive > 0) {
alarm(0);
safe_signal(SIGALRM, savealrm);
}
}
static void
imapcatch(int s)
{
if (reset_tio)
tcsetattr(0, TCSADRAIN, &otio);
switch (s) {
case SIGINT:
fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
siglongjmp(imapjmp, 1);
/*NOTREACHED*/
case SIGPIPE:
fprintf(stderr, "Received SIGPIPE during IMAP operation\n");
break;
}
}
static void
maincatch(int s)
{
if (interrupts++ == 0) {
fprintf(stderr, catgets(catd, CATSET, 102, "Interrupt\n"));
return;
}
onintr(0);
}
static enum okay
imap_noop1(struct mailbox *mp)
{
char o[LINESIZE];
FILE *queuefp = NULL;
snprintf(o, sizeof o, "%s NOOP\r\n", tag(1));
IMAP_OUT(o, MB_COMD, return STOP)
IMAP_ANSWER()
return OKAY;
}
enum okay
imap_noop(void)
{
sighandler_type saveint, savepipe;
enum okay ok = STOP;
(void)&saveint;
(void)&savepipe;
(void)&ok;
if (mb.mb_type != MB_IMAP)
return STOP;
verbose = value("verbose") != NULL;
imaplock = 1;
if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
safe_signal(SIGINT, maincatch);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1) == 0) {
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
ok = imap_noop1(&mb);
}
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
if (interrupts)
onintr(0);
return ok;
}
static void
rec_queue(enum rec_type type, unsigned long count)
{
struct record *rp;
rp = scalloc(1, sizeof *rp);
rp->rec_type = type;
rp->rec_count = count;
if (record && recend) {
recend->rec_next = rp;
recend = rp;
} else
record = recend = rp;
}
static enum okay
rec_dequeue(void)
{
struct message *omessage;
enum okay ok = OKAY;
struct record *rp = record, *rq = NULL;
unsigned long exists = 0;
long i;
if (record == NULL)
return STOP;
omessage = message;
message = smalloc((msgCount+1) * sizeof *message);
if (msgCount)
memcpy(message, omessage, msgCount * sizeof *message);
memset(&message[msgCount], 0, sizeof *message);
while (rp) {
switch (rp->rec_type) {
case REC_EXISTS:
exists = rp->rec_count;
break;
case REC_EXPUNGE:
if (rp->rec_count == 0) {
ok = STOP;
break;
}
if (rp->rec_count > msgCount) {
if (exists == 0 || rp->rec_count > exists--)
ok = STOP;
break;
}
if (exists > 0)
exists--;
delcache(&mb, &message[rp->rec_count-1]);
memmove(&message[rp->rec_count-1],
&message[rp->rec_count],
(msgCount - rp->rec_count + 1) *
sizeof *message);
msgCount--;
/*
* If the message was part of a collapsed thread,
* the m_collapsed field of one of its ancestors
* should be incremented. It seems hardly possible
* to do this with the current message structure,
* though. The result is that a '+' may be shown
* in the header summary even if no collapsed
* children exists.
*/
break;
}
free(rq);
rq = rp;
rp = rp->rec_next;
}
free(rq);
record = recend = NULL;
if (ok == OKAY && exists > msgCount) {
message = srealloc(message,
(exists + 1) * sizeof *message);
memset(&message[msgCount], 0,
(exists - msgCount + 1) * sizeof *message);
for (i = msgCount; i < exists; i++)
imap_init(&mb, i);
imap_flags(&mb, msgCount+1, exists);
msgCount = exists;
}
if (ok == STOP) {
free(message);
message = omessage;
}
return ok;
}
static void
rec_rmqueue(void)
{
struct record *rp, *rq = NULL;
for (rp = record; rp; rp = rp->rec_next) {
free(rq);
rq = rp;
}
free(rq);
record = recend = NULL;
}
/*ARGSUSED*/
static void
imapalarm(int s)
{
sighandler_type saveint;
sighandler_type savepipe;
if (imaplock++ == 0) {
if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
safe_signal(SIGINT, maincatch);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1)) {
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
goto brk;
}
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
if (imap_noop1(&mb) != OKAY) {
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
goto out;
}
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
}
brk: alarm(imapkeepalive);
out: imaplock--;
}
static int
imap_use_starttls(const char *uhp)
{
char *var;
if (value("imap-use-starttls"))
return 1;
var = savecat("imap-use-starttls-", uhp);
return value(var) != NULL;
}
static enum okay
imap_preauth(struct mailbox *mp, const char *xserver, const char *uhp)
{
char *server, *cp;
mp->mb_active |= MB_PREAUTH;
imap_answer(mp, 1);
if ((cp = strchr(xserver, ':')) != NULL) {
server = salloc(cp - xserver + 1);
memcpy(server, xserver, cp - xserver);
server[cp - xserver] = '\0';
} else
server = (char *)xserver;
#ifdef USE_SSL
if (mp->mb_sock.s_use_ssl == 0 && imap_use_starttls(uhp)) {
FILE *queuefp = NULL;
char o[LINESIZE];
snprintf(o, sizeof o, "%s STARTTLS\r\n", tag(1));
IMAP_OUT(o, MB_COMD, return STOP);
IMAP_ANSWER()
if (ssl_open(server, &mp->mb_sock, uhp) != OKAY)
return STOP;
}
#else /* !USE_SSL */
if (imap_use_starttls(uhp)) {
fprintf(stderr, "No SSL support compiled in.\n");
return STOP;
}
#endif /* !USE_SSL */
imap_capability(mp);
return OKAY;
}
static enum okay
imap_capability(struct mailbox *mp)
{
char o[LINESIZE];
FILE *queuefp = NULL;
enum okay ok = STOP;
const char *cp;
snprintf(o, sizeof o, "%s CAPABILITY\r\n", tag(1));
IMAP_OUT(o, MB_COMD, return STOP)
while (mp->mb_active & MB_COMD) {
ok = imap_answer(mp, 0);
if (response_status == RESPONSE_OTHER &&
response_other == CAPABILITY_DATA) {
cp = responded_other_text;
while (*cp) {
while (spacechar(*cp&0377))
cp++;
if (strncmp(cp, "UIDPLUS", 7) == 0 &&
spacechar(cp[7]&0377))
/* RFC 2359 */
mp->mb_flags |= MB_UIDPLUS;
while (*cp && !spacechar(*cp&0377))
cp++;
}
}
}
return ok;
}
static enum okay
imap_auth(struct mailbox *mp, const char *uhp, char *xuser, const char *pass)
{
char *var;
char *auth;
if (!(mp->mb_active & MB_PREAUTH))
return OKAY;
if ((auth = value("imap-auth")) == NULL) {
var = ac_alloc(strlen(uhp) + 11);
strcpy(var, "imap-auth-");
strcpy(&var[10], uhp);
auth = value(var);
ac_free(var);
}
if (auth == NULL || strcmp(auth, "login") == 0)
return imap_login(mp, xuser, pass);
if (strcmp(auth, "cram-md5") == 0)
return imap_cram_md5(mp, xuser, pass);
if (strcmp(auth, "gssapi") == 0) {
#ifdef USE_GSSAPI
return imap_gss(mp, xuser);
#else /* !USE_GSSAPI */
fprintf(stderr, "No GSSAPI support compiled in.\n");
return STOP;
#endif /* !USE_GSSAPI */
}
fprintf(stderr, "Unknown IMAP authentication method: \"%s\"\n", auth);
return STOP;
}
/*
* Implementation of RFC 2194.
*/
static enum okay
imap_cram_md5(struct mailbox *mp, char *xuser, const char *xpass)
{
char o[LINESIZE];
const char *user, *pass;
char *cp;
FILE *queuefp = NULL;
enum okay ok = STOP;
retry: if (xuser == NULL) {
if ((user = getuser()) == NULL)
return STOP;
} else
user = xuser;
if (xpass == NULL) {
if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
return STOP;
} else
pass = xpass;
snprintf(o, sizeof o, "%s AUTHENTICATE CRAM-MD5\r\n", tag(1));
IMAP_OUT(o, 0, return STOP)
imap_answer(mp, 1);
if (response_type != RESPONSE_CONT)
return STOP;
cp = cram_md5_string(user, pass, responded_text);
IMAP_OUT(cp, MB_COMD, return STOP)
while (mp->mb_active & MB_COMD)
ok = imap_answer(mp, 1);
if (ok == STOP) {
xpass = NULL;
goto retry;
}
return ok;
}
static enum okay
imap_login(struct mailbox *mp, char *xuser, const char *xpass)
{
char o[LINESIZE];
const char *user, *pass;
FILE *queuefp = NULL;
enum okay ok = STOP;
retry: if (xuser == NULL) {
if ((user = getuser()) == NULL)
return STOP;
} else
user = xuser;
if (xpass == NULL) {
if ((pass = getpassword(&otio, &reset_tio, NULL)) == NULL)
return STOP;
} else
pass = xpass;
snprintf(o, sizeof o, "%s LOGIN %s %s\r\n",
tag(1), imap_quotestr(user), imap_quotestr(pass));
IMAP_OUT(o, MB_COMD, return STOP)
while (mp->mb_active & MB_COMD)
ok = imap_answer(mp, 1);
if (ok == STOP) {
xpass = NULL;
goto retry;
}
return OKAY;
}
#ifdef USE_GSSAPI
#include "imap_gssapi.c"
#endif /* USE_GSSAPI */
enum okay
imap_select(struct mailbox *mp, off_t *size, int *count, const char *mbx)
{
enum okay ok = OKAY;
char *cp;
char o[LINESIZE];
FILE *queuefp = NULL;
mp->mb_uidvalidity = 0;
snprintf(o, sizeof o, "%s SELECT %s\r\n", tag(1), imap_quotestr(mbx));
IMAP_OUT(o, MB_COMD, return STOP)
while (mp->mb_active & MB_COMD) {
ok = imap_answer(mp, 1);
if (response_status != RESPONSE_OTHER &&
(cp = asccasestr(responded_text,
"[UIDVALIDITY ")) != NULL)
mp->mb_uidvalidity = atol(&cp[13]);
}
*count = had_exists > 0 ? had_exists : 0;
if (response_status != RESPONSE_OTHER &&
ascncasecmp(responded_text, "[READ-ONLY] ", 12)
== 0)
mp->mb_perm = 0;
return ok;
}
static enum okay
imap_flags(struct mailbox *mp, unsigned X, unsigned Y)
{
char o[LINESIZE];
FILE *queuefp = NULL;
char *cp;
struct message *m;
unsigned x = X, y = Y;
int n;
snprintf(o, sizeof o, "%s FETCH %u:%u (FLAGS UID)\r\n", tag(1), x, y);
IMAP_OUT(o, MB_COMD, return STOP)
while (mp->mb_active & MB_COMD) {
imap_answer(mp, 1);
if (response_status == RESPONSE_OTHER &&
response_other == MESSAGE_DATA_FETCH) {
n = responded_other_number;
if (n < x || n > y)
continue;
m = &message[n-1];
m->m_xsize = 0;
} else
continue;
if ((cp = asccasestr(responded_other_text, "FLAGS ")) != NULL) {
cp += 5;
while (*cp == ' ')
cp++;
if (*cp == '(')
imap_getflags(cp, &cp, &m->m_flag);
}
if ((cp = asccasestr(responded_other_text, "UID ")) != NULL)
m->m_uid = strtoul(&cp[4], NULL, 10);
getcache1(mp, m, NEED_UNSPEC, 1);
m->m_flag &= ~MHIDDEN;
}
while (x <= y && message[x-1].m_xsize && message[x-1].m_time)
x++;
while (y > x && message[y-1].m_xsize && message[y-1].m_time)
y--;
if (x <= y) {
snprintf(o, sizeof o,
"%s FETCH %u:%u (RFC822.SIZE INTERNALDATE)\r\n",
tag(1), x, y);
IMAP_OUT(o, MB_COMD, return STOP)
while (mp->mb_active & MB_COMD) {
imap_answer(mp, 1);
if (response_status == RESPONSE_OTHER &&
response_other == MESSAGE_DATA_FETCH) {
n = responded_other_number;
if (n < x || n > y)
continue;
m = &message[n-1];
} else
continue;
if ((cp = asccasestr(responded_other_text,
"RFC822.SIZE ")) != NULL)
m->m_xsize = strtol(&cp[12], NULL, 10);
if ((cp = asccasestr(responded_other_text,
"INTERNALDATE ")) != NULL)
m->m_time = imap_read_date_time(&cp[13]);
}
}
for (n = X; n <= Y; n++)
putcache(mp, &message[n-1]);
return OKAY;
}
static void
imap_init(struct mailbox *mp, int n)
{
struct message *m = &message[n];
m->m_flag = MUSED|MNOFROM;
m->m_block = 0;
m->m_offset = 0;
}
static void
imap_setptr(struct mailbox *mp, int newmail, int transparent, int *prevcount)
{
struct message *omessage = 0;
int i, omsgCount = 0;
enum okay dequeued = STOP;
if (newmail || transparent) {
omessage = message;
omsgCount = msgCount;
}
if (newmail)
dequeued = rec_dequeue();
if (had_exists >= 0) {
if (dequeued != OKAY)
msgCount = had_exists;
had_exists = -1;
}
if (had_expunge >= 0) {
if (dequeued != OKAY)
msgCount -= had_expunge;
had_expunge = -1;
}
if (newmail && expunged_messages)
printf("Expunged %ld message%s.\n",
expunged_messages,
expunged_messages != 1 ? "s" : "");
*prevcount = omsgCount - expunged_messages;
expunged_messages = 0;
if (msgCount < 0) {
fputs("IMAP error: Negative message count\n", stderr);
msgCount = 0;
}
if (dequeued != OKAY) {
message = scalloc(msgCount + 1, sizeof *message);
for (i = 0; i < msgCount; i++)
imap_init(mp, i);
if (!newmail && mp->mb_type == MB_IMAP)
initcache(mp);
if (msgCount > 0)
imap_flags(mp, 1, msgCount);
message[msgCount].m_size = 0;
message[msgCount].m_lines = 0;
rec_rmqueue();
}
if (newmail || transparent)
transflags(omessage, omsgCount, transparent);
else
setdot(message);
}
static char *
imap_have_password(const char *server)
{
char *var, *cp;
var = ac_alloc(strlen(server) + 10);
strcpy(var, "password-");
strcpy(&var[9], server);
if ((cp = value(var)) != NULL)
cp = savestr(cp);
ac_free(var);
return cp;
}
static void
imap_split(char **server, const char **sp, int *use_ssl, const char **cp,
char **uhp, char **mbx, const char **pass, char **user)
{
*sp = *server;
if (strncmp(*sp, "imap://", 7) == 0) {
*sp = &(*sp)[7];
*use_ssl = 0;
#ifdef USE_SSL
} else if (strncmp(*sp, "imaps://", 8) == 0) {
*sp = &(*sp)[8];
*use_ssl = 1;
#endif /* USE_SSL */
}
if ((*cp = strchr(*sp, '/')) != NULL && (*cp)[1] != '\0') {
*uhp = savestr((char *)(*sp));
(*uhp)[*cp - *sp] = '\0';
*mbx = (char *)&(*cp)[1];
} else {
if (*cp)
(*server)[*cp - *server] = '\0';
*uhp = (char *)(*sp);
*mbx = "INBOX";
}
*pass = imap_have_password(*uhp);
if ((*cp = last_at_before_slash(*uhp)) != NULL) {
*user = salloc(*cp - *uhp + 1);
memcpy(*user, *uhp, *cp - *uhp);
(*user)[*cp - *uhp] = '\0';
*sp = &(*cp)[1];
*user = strdec(*user);
} else {
*user = NULL;
*sp = *uhp;
}
}
int
imap_setfile(const char *xserver, int newmail, int isedit)
{
return imap_setfile1(xserver, newmail, isedit, 0);
}
static int
imap_setfile1(const char *xserver, int newmail, int isedit, int transparent)
{
struct sock so;
sighandler_type saveint;
sighandler_type savepipe;
char *server, *user, *account;
const char *cp, *sp, *pass;
char *uhp, *mbx;
int use_ssl = 0;
enum mbflags same_flags;
int prevcount = 0;
(void)&sp;
(void)&use_ssl;
(void)&saveint;
(void)&savepipe;
server = savestr((char *)xserver);
verbose = value("verbose") != NULL;
if (newmail) {
saveint = safe_signal(SIGINT, SIG_IGN);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (saveint != SIG_IGN)
safe_signal(SIGINT, imapcatch);
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
imaplock = 1;
goto newmail;
}
same_flags = mb.mb_flags;
same_imap_account = 0;
sp = protbase(server);
if (mb.mb_imap_account) {
if (mb.mb_sock.s_fd > 0 &&
strcmp(mb.mb_imap_account, sp) == 0 &&
disconnected(mb.mb_imap_account) == 0)
same_imap_account = 1;
}
account = sstrdup(sp);
imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
so.s_fd = -1;
if (!same_imap_account) {
if (!disconnected(account) &&
sopen(sp, &so, use_ssl, uhp,
use_ssl ? "imaps" : "imap", verbose) != OKAY)
return -1;
} else
so = mb.mb_sock;
if (!transparent)
quit();
edit = isedit;
free(mb.mb_imap_account);
mb.mb_imap_account = account;
if (!same_imap_account) {
if (mb.mb_sock.s_fd >= 0)
sclose(&mb.mb_sock);
}
same_imap_account = 0;
if (!transparent) {
if (mb.mb_itf) {
fclose(mb.mb_itf);
mb.mb_itf = NULL;
}
if (mb.mb_otf) {
fclose(mb.mb_otf);
mb.mb_otf = NULL;
}
free(mb.mb_imap_mailbox);
mb.mb_imap_mailbox = sstrdup(mbx);
initbox(server);
}
mb.mb_type = MB_VOID;
mb.mb_active = MB_NONE;;
imaplock = 1;
saveint = safe_signal(SIGINT, SIG_IGN);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1)) {
sclose(&so);
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
return -1;
}
if (saveint != SIG_IGN)
safe_signal(SIGINT, imapcatch);
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
if (mb.mb_sock.s_fd < 0) {
if (disconnected(mb.mb_imap_account)) {
if (cache_setptr(transparent) == STOP)
fprintf(stderr,
"Mailbox \"%s\" is not cached.\n",
server);
goto done;
}
if ((cp = value("imap-keepalive")) != NULL) {
if ((imapkeepalive = strtol(cp, NULL, 10)) > 0) {
savealrm = safe_signal(SIGALRM, imapalarm);
alarm(imapkeepalive);
}
}
mb.mb_sock = so;
mb.mb_sock.s_desc = "IMAP";
mb.mb_sock.s_onclose = imap_timer_off;
if (imap_preauth(&mb, sp, uhp) != OKAY ||
imap_auth(&mb, uhp, user, pass) != OKAY) {
sclose(&mb.mb_sock);
imap_timer_off();
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
return -1;
}
} else /* same account */
mb.mb_flags |= same_flags;
mb.mb_perm = Rflag ? 0 : MB_DELE;
mb.mb_type = MB_IMAP;
cache_dequeue(&mb);
if (imap_select(&mb, &mailsize, &msgCount, mbx) != OKAY) {
/*sclose(&mb.mb_sock);
imap_timer_off();*/
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
mb.mb_type = MB_VOID;
return -1;
}
newmail:
imap_setptr(&mb, newmail, transparent, &prevcount);
done: setmsize(msgCount);
if (!newmail && !transparent)
sawcom = 0;
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
if (!newmail && mb.mb_type == MB_IMAP)
purgecache(&mb, message, msgCount);
if ((newmail || transparent) && mb.mb_sorted) {
mb.mb_threaded = 0;
sort((void *)-1);
}
if (!newmail && !edit && msgCount == 0) {
if ((mb.mb_type == MB_IMAP || mb.mb_type == MB_CACHE) &&
value("emptystart") == NULL)
fprintf(stderr, catgets(catd, CATSET, 258,
"No mail at %s\n"), server);
return 1;
}
if (newmail)
newmailinfo(prevcount);
return 0;
}
static int
imap_fetchdata(struct mailbox *mp, struct message *m, size_t expected,
int need,
const char *head, size_t headsize, long headlines)
{
char *line = NULL, *lp;
size_t linesize = 0, linelen, size = 0;
int emptyline = 0, lines = 0, excess = 0;
off_t offset;
fseek(mp->mb_otf, 0L, SEEK_END);
offset = ftell(mp->mb_otf);
if (head)
fwrite(head, 1, headsize, mp->mb_otf);
while (sgetline(&line, &linesize, &linelen, &mp->mb_sock) > 0) {
lp = line;
if (linelen > expected) {
excess = linelen - expected;
linelen = expected;
}
/*
* Need to mask 'From ' lines. This cannot be done properly
* since some servers pass them as 'From ' and others as
* '>From '. Although one could identify the first kind of
* server in principle, it is not possible to identify the
* second as '>From ' may also come from a server of the
* first type as actual data. So do what is absolutely
* necessary only - mask 'From '.
*
* If the line is the first line of the message header, it
* is likely a real 'From ' line. In this case, it is just
* ignored since it violates all standards.
*/
if (lp[0] == 'F' && lp[1] == 'r' && lp[2] == 'o' &&
lp[3] == 'm' && lp[4] == ' ') {
if (lines + headlines != 0) {
fputc('>', mp->mb_otf);
size++;
} else
goto skip;
}
if (lp[linelen-1] == '\n' && (linelen == 1 ||
lp[linelen-2] == '\r')) {
emptyline = linelen <= 2;
if (linelen > 2)
fwrite(lp, 1, linelen - 2, mp->mb_otf);
fputc('\n', mp->mb_otf);
size += linelen - 1;
} else {
emptyline = 0;
fwrite(lp, 1, linelen, mp->mb_otf);
size += linelen;
}
lines++;
skip: if ((expected -= linelen) <= 0)
break;
}
if (!emptyline) {
/*
* This is very ugly; but some IMAP daemons don't end a
* message with \r\n\r\n, and we need \n\n for mbox format.
*/
fputc('\n', mp->mb_otf);
lines++;
size++;
}
fflush(mp->mb_otf);
if (m != NULL) {
m->m_size = size + headsize;
m->m_lines = lines + headlines;
m->m_block = mailx_blockof(offset);
m->m_offset = mailx_offsetof(offset);
switch (need) {
case NEED_HEADER:
m->m_have |= HAVE_HEADER;
break;
case NEED_BODY:
m->m_have |= HAVE_HEADER|HAVE_BODY;
m->m_xlines = m->m_lines;
m->m_xsize = m->m_size;
break;
}
}
free(line);
return excess;
}
static void
imap_putstr(struct mailbox *mp, struct message *m, const char *str,
const char *head, size_t headsize, long headlines)
{
off_t offset;
size_t len;
len = strlen(str);
fseek(mp->mb_otf, 0L, SEEK_END);
offset = ftell(mp->mb_otf);
if (head)
fwrite(head, 1, headsize, mp->mb_otf);
if (len > 0) {
fwrite(str, 1, len, mp->mb_otf);
fputc('\n', mp->mb_otf);
len++;
}
fflush(mp->mb_otf);
if (m != NULL) {
m->m_size = headsize + len;
m->m_lines = headlines + 1;
m->m_block = mailx_blockof(offset);
m->m_offset = mailx_offsetof(offset);
m->m_have |= HAVE_HEADER|HAVE_BODY;
m->m_xlines = m->m_lines;
m->m_xsize = m->m_size;
}
}
static enum okay
imap_get(struct mailbox *mp, struct message *m, enum needspec need)
{
sighandler_type saveint = SIG_IGN;
sighandler_type savepipe = SIG_IGN;
char o[LINESIZE], *cp = NULL, *item = NULL, *resp = NULL, *loc = NULL;
size_t expected, headsize = 0;
int number = m - message + 1;
enum okay ok = STOP;
FILE *queuefp = NULL;
char *head = NULL;
long headlines = 0;
struct message mt;
long n = -1;
unsigned long u = 0;
(void)&saveint;
(void)&savepipe;
(void)&number;
(void)&need;
(void)&cp;
(void)&loc;
(void)&ok;
(void)&headsize;
(void)&head;
(void)&headlines;
(void)&item;
(void)&resp;
(void)&u;
verbose = value("verbose") != NULL;
if (getcache(mp, m, need) == OKAY)
return OKAY;
if (mp->mb_type == MB_CACHE) {
fprintf(stderr, "Message %u not available.\n", number);
return STOP;
}
if (mp->mb_sock.s_fd < 0) {
fprintf(stderr, "IMAP connection closed.\n");
return STOP;
}
switch (need) {
case NEED_HEADER:
resp = item = "RFC822.HEADER";
break;
case NEED_BODY:
item = "BODY.PEEK[]";
resp = "BODY[]";
if (m->m_flag & HAVE_HEADER && m->m_size) {
char *hdr = smalloc(m->m_size);
fflush(mp->mb_otf);
if (fseek(mp->mb_itf, (long)mailx_positionof(m->m_block,
m->m_offset), SEEK_SET) < 0 ||
fread(hdr, 1, m->m_size, mp->mb_itf)
!= m->m_size) {
free(hdr);
break;
}
head = hdr;
headsize = m->m_size;
headlines = m->m_lines;
item = "BODY.PEEK[TEXT]";
resp = "BODY[TEXT]";
}
break;
case NEED_UNSPEC:
return STOP;
}
imaplock = 1;
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1)) {
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
return STOP;
}
if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
safe_signal(SIGINT, maincatch);
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
if (m->m_uid)
snprintf(o, sizeof o,
"%s UID FETCH %lu (%s)\r\n",
tag(1), m->m_uid, item);
else {
if (check_expunged() == STOP)
goto out;
snprintf(o, sizeof o,
"%s FETCH %u (%s)\r\n",
tag(1), number, item);
}
IMAP_OUT(o, MB_COMD, goto out)
for (;;) {
ok = imap_answer(mp, 1);
if (ok == STOP)
break;
if (response_status != RESPONSE_OTHER ||
response_other != MESSAGE_DATA_FETCH)
continue;
if ((loc = asccasestr(responded_other_text, resp)) == NULL)
continue;
if (m->m_uid) {
if ((cp = asccasestr(responded_other_text, "UID "))) {
u = atol(&cp[4]);
n = 0;
} else {
n = -1;
u = 0;
}
} else
n = responded_other_number;
if ((cp = strrchr(responded_other_text, '{')) == NULL) {
if (m->m_uid ? m->m_uid != u : n != number)
continue;
if ((cp = strchr(loc, '"')) != NULL) {
cp = imap_unquotestr(cp);
imap_putstr(mp, m, cp,
head, headsize, headlines);
} else {
m->m_have |= HAVE_HEADER|HAVE_BODY;
m->m_xlines = m->m_lines;
m->m_xsize = m->m_size;
}
goto out;
}
expected = atol(&cp[1]);
if (m->m_uid ? n == 0 && m->m_uid != u : n != number) {
imap_fetchdata(mp, NULL, expected, need, NULL, 0, 0);
continue;
}
mt = *m;
imap_fetchdata(mp, &mt, expected, need,
head, headsize, headlines);
if (n >= 0) {
commitmsg(mp, m, mt, mt.m_have);
break;
}
if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
&mp->mb_sock) > 0) {
if (verbose)
fputs(imapbuf, stderr);
if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
u = atol(&cp[4]);
if (u == m->m_uid) {
commitmsg(mp, m, mt, mt.m_have);
break;
}
}
}
}
out: while (mp->mb_active & MB_COMD)
ok = imap_answer(mp, 1);
if (saveint != SIG_IGN)
safe_signal(SIGINT, saveint);
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, savepipe);
imaplock--;
if (ok == OKAY)
putcache(mp, m);
free(head);
if (interrupts)
onintr(0);
return ok;
}
enum okay
imap_header(struct message *m)
{
return imap_get(&mb, m, NEED_HEADER);
}
enum okay
imap_body(struct message *m)
{
return imap_get(&mb, m, NEED_BODY);
}
static void
commitmsg(struct mailbox *mp, struct message *to,
struct message from, enum havespec have)
{
to->m_size = from.m_size;
to->m_lines = from.m_lines;
to->m_block = from.m_block;
to->m_offset = from.m_offset;
to->m_have = have;
if (have & HAVE_BODY) {
to->m_xlines = from.m_lines;
to->m_xsize = from.m_size;
}
putcache(mp, to);
}
static enum okay
imap_fetchheaders (
struct mailbox *mp,
struct message *m,
int bot,
int top /* bot > top */
)
{
char o[LINESIZE], *cp;
struct message mt;
size_t expected;
enum okay ok;
int n = 0, u;
FILE *queuefp = NULL;
if (m[bot].m_uid)
snprintf(o, sizeof o,
"%s UID FETCH %lu:%lu (RFC822.HEADER)\r\n",
tag(1), m[bot-1].m_uid, m[top-1].m_uid);
else {
if (check_expunged() == STOP)
return STOP;
snprintf(o, sizeof o,
"%s FETCH %u:%u (RFC822.HEADER)\r\n",
tag(1), bot, top);
}
IMAP_OUT(o, MB_COMD, return STOP)
for (;;) {
ok = imap_answer(mp, 1);
if (response_status != RESPONSE_OTHER)
break;
if (response_other != MESSAGE_DATA_FETCH)
continue;
if (ok == STOP || (cp=strrchr(responded_other_text, '{')) == 0)
return STOP;
if (asccasestr(responded_other_text, "RFC822.HEADER") == NULL)
continue;
expected = atol(&cp[1]);
if (m[bot-1].m_uid) {
if ((cp=asccasestr(responded_other_text, "UID "))) {
u = atol(&cp[4]);
for (n = bot; n <= top; n++)
if (m[n-1].m_uid == u)
break;
if (n > top) {
imap_fetchdata(mp, NULL, expected,
NEED_HEADER,
NULL, 0, 0);
continue;
}
} else
n = -1;
} else {
n = responded_other_number;
if (n <= 0 || n > msgCount) {
imap_fetchdata(mp, NULL, expected, NEED_HEADER,
NULL, 0, 0);
continue;
}
}
imap_fetchdata(mp, &mt, expected, NEED_HEADER, NULL, 0, 0);
if (n >= 0 && !(m[n-1].m_have & HAVE_HEADER))
commitmsg(mp, &m[n-1], mt, HAVE_HEADER);
if (n == -1 && sgetline(&imapbuf, &imapbufsize, NULL,
&mp->mb_sock) > 0) {
if (verbose)
fputs(imapbuf, stderr);
if ((cp = asccasestr(imapbuf, "UID ")) != NULL) {
u = atol(&cp[4]);
for (n = bot; n <= top; n++)
if (m[n-1].m_uid == u)
break;
if (n <= top && !(m[n-1].m_have & HAVE_HEADER))
commitmsg(mp, &m[n-1], mt, HAVE_HEADER);
n = 0;
}
}
}
while (mp->mb_active & MB_COMD)
ok = imap_answer(mp, 1);
return ok;
}
void
imap_getheaders(int bot, int top)
{
sighandler_type saveint, savepipe;
enum okay ok = STOP;
int i, chunk = 256;
(void)&saveint;
(void)&savepipe;
(void)&ok;
(void)⊥
(void)⊤
verbose = value("verbose") != NULL;
if (mb.mb_type == MB_CACHE)
return;
if (bot < 1)
bot = 1;
if (top > msgCount)
top = msgCount;
for (i = bot; i < top; i++) {
if (message[i-1].m_have & HAVE_HEADER ||
getcache(&mb, &message[i-1], NEED_HEADER)
== OKAY)
bot = i+1;
else
break;
}
for (i = top; i > bot; i--) {
if (message[i-1].m_have & HAVE_HEADER ||
getcache(&mb, &message[i-1], NEED_HEADER)
== OKAY)
top = i-1;
else
break;
}
if (bot >= top)
return;
imaplock = 1;
if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
safe_signal(SIGINT, maincatch);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1) == 0) {
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
for (i = bot; i <= top; i += chunk) {
ok = imap_fetchheaders(&mb, message, i,
i+chunk-1 < top ? i+chunk-1 : top);
if (interrupts)
onintr(0);
}
}
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
}
static enum okay
imap_exit(struct mailbox *mp)
{
char o[LINESIZE];
FILE *queuefp = NULL;
verbose = value("verbose") != NULL;
mp->mb_active |= MB_BYE;
snprintf(o, sizeof o, "%s LOGOUT\r\n", tag(1));
IMAP_OUT(o, MB_COMD, return STOP)
IMAP_ANSWER()
return OKAY;
}
static enum okay
imap_delete(struct mailbox *mp, int n, struct message *m, int needstat)
{
imap_store(mp, m, n, '+', "\\Deleted", needstat);
if (mp->mb_type == MB_IMAP)
delcache(mp, m);
return OKAY;
}
static enum okay
imap_close(struct mailbox *mp)
{
char o[LINESIZE];
FILE *queuefp = NULL;
snprintf(o, sizeof o, "%s CLOSE\r\n", tag(1));
IMAP_OUT(o, MB_COMD, return STOP)
IMAP_ANSWER()
return OKAY;
}
static enum okay
imap_update(struct mailbox *mp)
{
FILE *readstat = NULL;
struct message *m;
int dodel, c, gotcha = 0, held = 0, modflags = 0, needstat, stored = 0;
verbose = value("verbose") != NULL;
if (Tflag != NULL) {
if ((readstat = Zopen(Tflag, "w", NULL)) == NULL)
Tflag = NULL;
}
if (!edit && mp->mb_perm != 0) {
holdbits();
for (m = &message[0], c = 0; m < &message[msgCount]; m++) {
if (m->m_flag & MBOX)
c++;
}
if (c > 0)
if (makembox() == STOP)
goto bypass;
}
for (m = &message[0], gotcha=0, held=0; m < &message[msgCount]; m++) {
if (readstat != NULL && (m->m_flag & (MREAD|MDELETED)) != 0) {
char *id;
if ((id = hfield("message-id", m)) != NULL ||
(id = hfield("article-id", m)) != NULL)
fprintf(readstat, "%s\n", id);
}
if (mp->mb_perm == 0) {
dodel = 0;
} else if (edit) {
dodel = m->m_flag & MDELETED;
} else {
dodel = !((m->m_flag&MPRESERVE) ||
(m->m_flag&MTOUCH) == 0);
}
/*
* Fetch the result after around each 800 STORE commands
* sent (approx. 32k data sent). Otherwise, servers will
* try to flush the return queue at some point, leading
* to a deadlock if we are still writing commands but not
* reading their results.
*/
needstat = stored > 0 && stored % 800 == 0;
if (dodel) {
imap_delete(mp, m-message+1, m, needstat);
stored++;
gotcha++;
} else {
if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS)) {
imap_store(mp, m, m-message+1,
'+', "\\Seen", needstat);
stored++;
}
if (m->m_flag & MFLAG) {
imap_store(mp, m, m-message+1,
'+', "\\Flagged", needstat);
stored++;
}
if (m->m_flag & MUNFLAG) {
imap_store(mp, m, m-message+1,
'-', "\\Flagged", needstat);
stored++;
}
if (m->m_flag & MANSWER) {
imap_store(mp, m, m-message+1,
'+', "\\Answered", needstat);
stored++;
}
if (m->m_flag & MUNANSWER) {
imap_store(mp, m, m-message+1,
'-', "\\Answered", needstat);
stored++;
}
if (m->m_flag & MDRAFT) {
imap_store(mp, m, m-message+1,
'+', "\\Draft", needstat);
stored++;
}
if (m->m_flag & MUNDRAFT) {
imap_store(mp, m, m-message+1,
'-', "\\Draft", needstat);
stored++;
}
if (mp->mb_type != MB_CACHE ||
!edit && (!(m->m_flag&(MBOXED|MSAVED|MDELETED))
|| (m->m_flag &
(MBOXED|MPRESERVE|MTOUCH)) ==
(MPRESERVE|MTOUCH)) ||
edit && !(m->m_flag & MDELETED))
held++;
if (m->m_flag & MNEW) {
m->m_flag &= ~MNEW;
m->m_flag |= MSTATUS;
}
}
}
bypass: if (readstat != NULL)
Fclose(readstat);
if (gotcha)
imap_close(mp);
for (m = &message[0]; m < &message[msgCount]; m++)
if (!(m->m_flag&MUNLINKED) &&
m->m_flag&(MBOXED|MDELETED|MSAVED|MSTATUS|
MFLAG|MUNFLAG|MANSWER|MUNANSWER|
MDRAFT|MUNDRAFT)) {
putcache(mp, m);
modflags++;
}
if ((gotcha || modflags) && edit) {
printf(catgets(catd, CATSET, 168, "\"%s\" "), mailname);
printf(value("bsdcompat") || value("bsdmsgs") ?
catgets(catd, CATSET, 170, "complete\n") :
catgets(catd, CATSET, 212, "updated.\n"));
} else if (held && !edit && mp->mb_perm != 0) {
if (held == 1)
printf(catgets(catd, CATSET, 155,
"Held 1 message in %s\n"), mailname);
else if (held > 1)
printf(catgets(catd, CATSET, 156,
"Held %d messages in %s\n"), held, mailname);
}
fflush(stdout);
return OKAY;
}
void
imap_quit(void)
{
sighandler_type saveint;
sighandler_type savepipe;
verbose = value("verbose") != NULL;
if (mb.mb_type == MB_CACHE) {
imap_update(&mb);
return;
}
if (mb.mb_sock.s_fd < 0) {
fprintf(stderr, "IMAP connection closed.\n");
return;
}
imaplock = 1;
saveint = safe_signal(SIGINT, SIG_IGN);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1)) {
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, saveint);
imaplock = 0;
return;
}
if (saveint != SIG_IGN)
safe_signal(SIGINT, imapcatch);
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
imap_update(&mb);
if (!same_imap_account) {
imap_exit(&mb);
sclose(&mb.mb_sock);
}
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
}
static enum okay
imap_store(struct mailbox *mp, struct message *m, int n,
int c, const char *sp, int needstat)
{
char o[LINESIZE];
FILE *queuefp = NULL;
if (mp->mb_type == MB_CACHE && (queuefp = cache_queue(mp)) == NULL)
return STOP;
if (m->m_uid)
snprintf(o, sizeof o,
"%s UID STORE %lu %cFLAGS (%s)\r\n",
tag(1), m->m_uid, c, sp);
else {
if (check_expunged() == STOP)
return STOP;
snprintf(o, sizeof o,
"%s STORE %u %cFLAGS (%s)\r\n",
tag(1), n, c, sp);
}
IMAP_OUT(o, MB_COMD, return STOP)
if (needstat)
IMAP_ANSWER()
else
mb.mb_active &= ~MB_COMD;
if (queuefp != NULL)
Fclose(queuefp);
return OKAY;
}
enum okay
imap_undelete(struct message *m, int n)
{
return imap_unstore(m, n, "\\Deleted");
}
enum okay
imap_unread(struct message *m, int n)
{
return imap_unstore(m, n, "\\Seen");
}
static enum okay
imap_unstore(struct message *m, int n, const char *flag)
{
sighandler_type saveint, savepipe;
enum okay ok = STOP;
(void)&saveint;
(void)&savepipe;
(void)&ok;
verbose = value("verbose") != NULL;
imaplock = 1;
if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
safe_signal(SIGINT, maincatch);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1) == 0) {
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
ok = imap_store(&mb, m, n, '-', flag, 1);
}
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
if (interrupts)
onintr(0);
return ok;
}
static const char *
tag(int new)
{
static char ts[20];
static long n;
if (new)
n++;
snprintf(ts, sizeof ts, "T%lu", n);
return ts;
}
int
imap_imap(void *vp)
{
sighandler_type saveint, savepipe;
char o[LINESIZE];
enum okay ok = STOP;
struct mailbox *mp = &mb;
FILE *queuefp = NULL;
(void)&saveint;
(void)&savepipe;
(void)&ok;
verbose = value("verbose") != NULL;
if (mp->mb_type != MB_IMAP) {
printf("Not operating on an IMAP mailbox.\n");
return 1;
}
imaplock = 1;
if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
safe_signal(SIGINT, maincatch);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1) == 0) {
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
snprintf(o, sizeof o, "%s %s\r\n", tag(1), (char *)vp);
IMAP_OUT(o, MB_COMD, goto out)
while (mp->mb_active & MB_COMD) {
ok = imap_answer(mp, 0);
fputs(responded_text, stdout);
}
}
out: safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
if (interrupts)
onintr(0);
return ok != OKAY;
}
int
imap_newmail(int autoinc)
{
if (autoinc && had_exists < 0 && had_expunge < 0) {
verbose = value("verbose") != NULL;
imaplock = 1;
imap_noop();
imaplock = 0;
}
if (had_exists == msgCount && had_expunge < 0)
/*
* Some servers always respond with EXISTS to NOOP. If
* the mailbox has been changed but the number of messages
* has not, an EXPUNGE must also had been sent; otherwise,
* nothing has changed.
*/
had_exists = -1;
return had_expunge >= 0 ? 2 : had_exists >= 0 ? 1 : 0;
}
static char *
imap_putflags(int f)
{
const char *cp;
char *buf, *bp;
bp = buf = salloc(100);
if (f & (MREAD|MFLAGGED|MANSWERED|MDRAFTED)) {
*bp++ = '(';
if (f & MREAD) {
if (bp[-1] != '(')
*bp++ = ' ';
for (cp = "\\Seen"; *cp; cp++)
*bp++ = *cp;
}
if (f & MFLAGGED) {
if (bp[-1] != '(')
*bp++ = ' ';
for (cp = "\\Flagged"; *cp; cp++)
*bp++ = *cp;
}
if (f & MANSWERED) {
if (bp[-1] != '(')
*bp++ = ' ';
for (cp = "\\Answered"; *cp; cp++)
*bp++ = *cp;
}
if (f & MDRAFT) {
if (bp[-1] != '(')
*bp++ = ' ';
for (cp = "\\Draft"; *cp; cp++)
*bp++ = *cp;
}
*bp++ = ')';
*bp++ = ' ';
}
*bp = '\0';
return buf;
}
static void
imap_getflags(const char *cp, char **xp, enum mflag *f)
{
while (*cp != ')') {
if (*cp == '\\') {
if (ascncasecmp(cp, "\\Seen", 5) == 0)
*f |= MREAD;
else if (ascncasecmp(cp, "\\Recent", 7) == 0)
*f |= MNEW;
else if (ascncasecmp(cp, "\\Deleted", 8) == 0)
*f |= MDELETED;
else if (ascncasecmp(cp, "\\Flagged", 8) == 0)
*f |= MFLAGGED;
else if (ascncasecmp(cp, "\\Answered", 9) == 0)
*f |= MANSWERED;
else if (ascncasecmp(cp, "\\Draft", 6) == 0)
*f |= MDRAFTED;
}
cp++;
}
if (xp)
*xp = (char *)cp;
}
static enum okay
imap_append1(struct mailbox *mp, const char *name, FILE *fp,
off_t off1, long xsize, enum mflag flag, time_t t)
{
char o[LINESIZE];
char *buf;
size_t bufsize, buflen, count;
enum okay ok = STOP;
long size, lines, ysize;
int twice = 0;
FILE *queuefp = NULL;
if (mp->mb_type == MB_CACHE) {
queuefp = cache_queue(mp);
if (queuefp == NULL)
return STOP;
ok = OKAY;
}
buf = smalloc(bufsize = LINESIZE);
buflen = 0;
again: size = xsize;
count = fsize(fp);
fseek(fp, off1, SEEK_SET);
snprintf(o, sizeof o, "%s APPEND %s %s%s {%ld}\r\n",
tag(1), imap_quotestr(name),
imap_putflags(flag),
imap_make_date_time(t),
size);
IMAP_OUT(o, MB_COMD, goto out)
while (mp->mb_active & MB_COMD) {
ok = imap_answer(mp, twice);
if (response_type == RESPONSE_CONT)
break;
}
if (mp->mb_type != MB_CACHE && ok == STOP) {
if (twice == 0)
goto trycreate;
else
goto out;
}
lines = ysize = 0;
while (size > 0) {
fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
lines++;
ysize += buflen;
buf[buflen-1] = '\r';
buf[buflen] = '\n';
if (mp->mb_type != MB_CACHE)
swrite1(&mp->mb_sock, buf, buflen+1, 1);
else if (queuefp)
fwrite(buf, 1, buflen+1, queuefp);
size -= buflen+1;
}
if (mp->mb_type != MB_CACHE)
swrite(&mp->mb_sock, "\r\n");
else if (queuefp)
fputs("\r\n", queuefp);
while (mp->mb_active & MB_COMD) {
ok = imap_answer(mp, 0);
if (response_status == RESPONSE_NO /*&&
ascncasecmp(responded_text,
"[TRYCREATE] ", 12) == 0*/) {
trycreate: if (twice++) {
ok = STOP;
goto out;
}
snprintf(o, sizeof o, "%s CREATE %s\r\n",
tag(1),
imap_quotestr(name));
IMAP_OUT(o, MB_COMD, goto out);
while (mp->mb_active & MB_COMD)
ok = imap_answer(mp, 1);
if (ok == STOP)
goto out;
imap_created_mailbox++;
goto again;
} else if (ok != OKAY)
fprintf(stderr, "IMAP error: %s", responded_text);
else if (response_status == RESPONSE_OK &&
mp->mb_flags & MB_UIDPLUS)
imap_appenduid(mp, fp, t, off1, xsize, ysize, lines,
flag, name);
}
out: if (queuefp != NULL)
Fclose(queuefp);
free(buf);
return ok;
}
static enum okay
imap_append0(struct mailbox *mp, const char *name, FILE *fp)
{
char *buf, *bp, *lp;
size_t bufsize, buflen, count;
off_t off1 = -1, offs;
int inhead = 1;
int flag = MNEW|MNEWEST;
long size = 0;
time_t tim;
enum okay ok;
buf = smalloc(bufsize = LINESIZE);
buflen = 0;
count = fsize(fp);
offs = ftell(fp);
time(&tim);
do {
bp = fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
if (off1 != (off_t)-1) {
ok=imap_append1(mp, name, fp, off1,
size, flag, tim);
if (ok == STOP)
return STOP;
fseek(fp, offs+buflen, SEEK_SET);
}
off1 = offs + buflen;
size = 0;
inhead = 1;
flag = MNEW;
if (bp != NULL)
tim = unixtime(buf);
} else
size += buflen+1;
offs += buflen;
if (bp && buf[0] == '\n')
inhead = 0;
else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
lp = &buf[6];
while (whitechar(*lp&0377))
lp++;
if (*lp == ':')
while (*++lp != '\0')
switch (*lp) {
case 'R':
flag |= MREAD;
break;
case 'O':
flag &= ~MNEW;
break;
}
} else if (bp && inhead &&
ascncasecmp(buf, "x-status", 8) == 0) {
lp = &buf[8];
while (whitechar(*lp&0377))
lp++;
if (*lp == ':')
while (*++lp != '\0')
switch (*lp) {
case 'F':
flag |= MFLAGGED;
break;
case 'A':
flag |= MANSWERED;
break;
case 'T':
flag |= MDRAFTED;
break;
}
}
} while (bp != NULL);
free(buf);
return OKAY;
}
enum okay
imap_append(const char *xserver, FILE *fp)
{
sighandler_type saveint, savepipe;
char *server, *uhp, *mbx, *user;
const char *sp, *cp, *pass;
int use_ssl;
enum okay ok = STOP;
(void)&saveint;
(void)&savepipe;
(void)&ok;
verbose = value("verbose") != NULL;
server = savestr((char *)xserver);
imap_split(&server, &sp, &use_ssl, &cp, &uhp, &mbx, &pass, &user);
imaplock = 1;
if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
safe_signal(SIGINT, maincatch);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1))
goto out;
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
if ((mb.mb_type == MB_CACHE || mb.mb_sock.s_fd > 0) &&
mb.mb_imap_account &&
strcmp(protbase(server), mb.mb_imap_account) == 0) {
ok = imap_append0(&mb, mbx, fp);
}
else {
struct mailbox mx;
memset(&mx, 0, sizeof mx);
if (disconnected(server) == 0) {
if (sopen(sp, &mx.mb_sock, use_ssl, uhp,
use_ssl ? "imaps" : "imap",
verbose) != OKAY)
goto fail;
mx.mb_sock.s_desc = "IMAP";
mx.mb_type = MB_IMAP;
mx.mb_imap_account = (char *)protbase(server);
mx.mb_imap_mailbox = mbx;
if (imap_preauth(&mx, sp, uhp) != OKAY ||
imap_auth(&mx, uhp, user, pass)!=OKAY) {
sclose(&mx.mb_sock);
goto fail;
}
ok = imap_append0(&mx, mbx, fp);
imap_exit(&mx);
sclose(&mx.mb_sock);
} else {
mx.mb_imap_account = (char *)protbase(server);
mx.mb_imap_mailbox = mbx;
mx.mb_type = MB_CACHE;
ok = imap_append0(&mx, mbx, fp);
}
fail:;
}
out: safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
if (interrupts)
onintr(0);
return ok;
}
static enum okay
imap_list1(struct mailbox *mp, const char *base, struct list_item **list,
struct list_item **lend, int level)
{
char o[LINESIZE];
enum okay ok = STOP;
char *cp;
const char *bp;
FILE *queuefp = NULL;
struct list_item *lp;
*list = *lend = NULL;
snprintf(o, sizeof o, "%s LIST %s %%\r\n",
tag(1), imap_quotestr(base));
IMAP_OUT(o, MB_COMD, return STOP);
while (mp->mb_active & MB_COMD) {
ok = imap_answer(mp, 1);
if (response_status == RESPONSE_OTHER &&
response_other == MAILBOX_DATA_LIST &&
imap_parse_list() == OKAY) {
cp = imap_unquotestr(list_name);
lp = csalloc(1, sizeof *lp);
lp->l_name = cp;
for (bp = base; *bp && *bp == *cp; bp++)
cp++;
lp->l_base = *cp ? cp : savestr(base);
lp->l_attr = list_attributes;
lp->l_level = level+1;
lp->l_delim = list_hierarchy_delimiter;
if (*list && *lend) {
(*lend)->l_next = lp;
*lend = lp;
} else
*list = *lend = lp;
}
}
return ok;
}
static enum okay
imap_list(struct mailbox *mp, const char *base, int strip, FILE *fp)
{
struct list_item *list, *lend, *lp, *lx, *ly;
int n;
const char *bp;
char *cp;
int depth;
verbose = value("verbose") != NULL;
depth = (cp = value("imap-list-depth")) != NULL ? atoi(cp) : 2;
if (imap_list1(mp, base, &list, &lend, 0) == STOP)
return STOP;
if (list == NULL || lend == NULL)
return OKAY;
for (lp = list; lp; lp = lp->l_next)
if (lp->l_delim != '/' && lp->l_delim != EOF &&
lp->l_level < depth &&
(lp->l_attr&LIST_NOINFERIORS) == 0) {
cp = salloc((n = strlen(lp->l_name)) + 2);
strcpy(cp, lp->l_name);
cp[n] = lp->l_delim;
cp[n+1] = '\0';
if (imap_list1(mp, cp, &lx, &ly, lp->l_level) == OKAY &&
lx && ly) {
lp->l_has_children = 1;
if (strcmp(cp, lx->l_name) == 0)
lx = lx->l_next;
if (lx) {
lend->l_next = lx;
lend = ly;
}
}
}
for (lp = list; lp; lp = lp->l_next) {
if (strip) {
cp = lp->l_name;
for (bp = base; *bp && *bp == *cp; bp++)
cp++;
} else
cp = lp->l_name;
if ((lp->l_attr&LIST_NOSELECT) == 0)
fprintf(fp, "%s\n", *cp ? cp : base);
else if (lp->l_has_children == 0)
fprintf(fp, "%s%c\n", *cp ? cp : base,
lp->l_delim != EOF ? lp->l_delim : '\n');
}
return OKAY;
}
void
imap_folders(const char *name, int strip)
{
sighandler_type saveint, savepipe;
const char *fold, *cp, *sp;
char *tempfn;
FILE *fp;
int columnize = is_a_tty[1];
(void)&saveint;
(void)&savepipe;
(void)&fp;
(void)&fold;
cp = protbase(name);
sp = mb.mb_imap_account;
if (strcmp(cp, sp)) {
fprintf(stderr, "Cannot list folders on other than the "
"current IMAP account,\n\"%s\". "
"Try \"folders @\".\n", sp);
return;
}
fold = protfile(name);
if (columnize) {
if ((fp = Ftemp(&tempfn, "Ri", "w+", 0600, 1)) == NULL) {
perror("tmpfile");
return;
}
rm(tempfn);
Ftfree(&tempfn);
} else
fp = stdout;
imaplock = 1;
if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
safe_signal(SIGINT, maincatch);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1))
goto out;
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
if (mb.mb_type == MB_CACHE)
cache_list(&mb, fold, strip, fp);
else
imap_list(&mb, fold, strip, fp);
imaplock = 0;
if (interrupts) {
if (columnize)
Fclose(fp);
onintr(0);
}
fflush(fp);
if (columnize) {
rewind(fp);
if (fsize(fp) > 0)
dopr(fp);
else
fprintf(stderr, "Folder not found.\n");
}
out:
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
if (columnize)
Fclose(fp);
}
static void
dopr(FILE *fp)
{
char o[LINESIZE], *tempfn;
int c;
long n = 0, mx = 0, columns, width;
FILE *out;
if ((out = Ftemp(&tempfn, "Ro", "w+", 0600, 1)) == NULL) {
perror("tmpfile");
return;
}
rm(tempfn);
Ftfree(&tempfn);
while ((c = getc(fp)) != EOF) {
if (c == '\n') {
if (n > mx)
mx = n;
n = 0;
} else
n++;
}
rewind(fp);
width = scrnwidth;
if (mx < width / 2) {
columns = width / (mx+2);
snprintf(o, sizeof o,
"sort | pr -%lu -w%lu -t",
columns, width);
} else
strncpy(o, "sort", sizeof o)[sizeof o - 1] = '\0';
run_command(SHELL, 0, fileno(fp), fileno(out), "-c", o, NULL);
try_pager(out);
Fclose(out);
}
static enum okay
imap_copy1(struct mailbox *mp, struct message *m, int n, const char *name)
{
char o[LINESIZE];
const char *qname;
enum okay ok = STOP;
int twice = 0;
FILE *queuefp = NULL;
if (mp->mb_type == MB_CACHE) {
if ((queuefp = cache_queue(mp)) == NULL)
return STOP;
ok = OKAY;
}
qname = imap_quotestr(name = protfile(name));
/*
* Since it is not possible to set flags on the copy, recently
* set flags must be set on the original to include it in the copy.
*/
if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS))
imap_store(mp, m, n, '+', "\\Seen", 0);
if (m->m_flag&MFLAG)
imap_store(mp, m, n, '+', "\\Flagged", 0);
if (m->m_flag&MUNFLAG)
imap_store(mp, m, n, '-', "\\Flagged", 0);
if (m->m_flag&MANSWER)
imap_store(mp, m, n, '+', "\\Answered", 0);
if (m->m_flag&MUNANSWER)
imap_store(mp, m, n, '-', "\\Flagged", 0);
if (m->m_flag&MDRAFT)
imap_store(mp, m, n, '+', "\\Draft", 0);
if (m->m_flag&MUNDRAFT)
imap_store(mp, m, n, '-', "\\Draft", 0);
again: if (m->m_uid)
snprintf(o, sizeof o, "%s UID COPY %lu %s\r\n",
tag(1), m->m_uid, qname);
else {
if (check_expunged() == STOP)
goto out;
snprintf(o, sizeof o, "%s COPY %u %s\r\n",
tag(1), n, qname);
}
IMAP_OUT(o, MB_COMD, goto out)
while (mp->mb_active & MB_COMD)
ok = imap_answer(mp, twice);
if (mp->mb_type == MB_IMAP &&
mp->mb_flags & MB_UIDPLUS &&
response_status == RESPONSE_OK)
imap_copyuid(mp, m, name);
if (response_status == RESPONSE_NO && twice++ == 0) {
snprintf(o, sizeof o, "%s CREATE %s\r\n", tag(1), qname);
IMAP_OUT(o, MB_COMD, goto out)
while (mp->mb_active & MB_COMD)
ok = imap_answer(mp, 1);
if (ok == OKAY) {
imap_created_mailbox++;
goto again;
}
}
if (queuefp != NULL)
Fclose(queuefp);
/*
* ... and reset the flag to its initial value so that
* the 'exit' command still leaves the message unread.
*/
out: if ((m->m_flag&(MREAD|MSTATUS)) == (MREAD|MSTATUS))
imap_store(mp, m, n, '-', "\\Seen", 0);
if (m->m_flag&MFLAG)
imap_store(mp, m, n, '-', "\\Flagged", 0);
if (m->m_flag&MUNFLAG)
imap_store(mp, m, n, '+', "\\Flagged", 0);
if (m->m_flag&MANSWER)
imap_store(mp, m, n, '-', "\\Answered", 0);
if (m->m_flag&MUNANSWER)
imap_store(mp, m, n, '+', "\\Answered", 0);
if (m->m_flag&MDRAFT)
imap_store(mp, m, n, '-', "\\Draft", 0);
if (m->m_flag&MUNDRAFT)
imap_store(mp, m, n, '+', "\\Draft", 0);
return ok;
}
enum okay
imap_copy(struct message *m, int n, const char *name)
{
sighandler_type saveint, savepipe;
enum okay ok = STOP;
(void)&saveint;
(void)&savepipe;
(void)&ok;
verbose = value("verbose") != NULL;
imaplock = 1;
if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
safe_signal(SIGINT, maincatch);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1) == 0) {
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
ok = imap_copy1(&mb, m, n, name);
}
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
if (interrupts)
onintr(0);
return ok;
}
static enum okay
imap_copyuid_parse(const char *cp, unsigned long *uidvalidity,
unsigned long *olduid, unsigned long *newuid)
{
char *xp, *yp, *zp;
*uidvalidity = strtoul(cp, &xp, 10);
*olduid = strtoul(xp, &yp, 10);
*newuid = strtoul(yp, &zp, 10);
return *uidvalidity && *olduid && *newuid && xp > cp && *xp == ' ' &&
yp > xp && *yp == ' ' && zp > yp && *zp == ']';
}
static enum okay
imap_appenduid_parse(const char *cp, unsigned long *uidvalidity,
unsigned long *uid)
{
char *xp, *yp;
*uidvalidity = strtoul(cp, &xp, 10);
*uid = strtoul(xp, &yp, 10);
return *uidvalidity && *uid && xp > cp && *xp == ' ' &&
yp > xp && *yp == ']';
}
static enum okay
imap_copyuid(struct mailbox *mp, struct message *m, const char *name)
{
const char *cp;
unsigned long uidvalidity, olduid, newuid;
struct mailbox xmb;
struct message xm;
if ((cp = asccasestr(responded_text, "[COPYUID ")) == NULL ||
imap_copyuid_parse(&cp[9], &uidvalidity,
&olduid, &newuid) == STOP)
return STOP;
xmb = *mp;
xmb.mb_cache_directory = NULL;
xmb.mb_imap_mailbox = savestr((char *)name);
xmb.mb_uidvalidity = uidvalidity;
initcache(&xmb);
if (m == NULL) {
memset(&xm, 0, sizeof xm);
xm.m_uid = olduid;
if (getcache1(mp, &xm, NEED_UNSPEC, 3) != OKAY)
return STOP;
getcache(mp, &xm, NEED_HEADER);
getcache(mp, &xm, NEED_BODY);
} else {
if ((m->m_flag & HAVE_HEADER) == 0)
getcache(mp, m, NEED_HEADER);
if ((m->m_flag & HAVE_BODY) == 0)
getcache(mp, m, NEED_BODY);
xm = *m;
}
xm.m_uid = newuid;
xm.m_flag &= ~MFULLYCACHED;
putcache(&xmb, &xm);
return OKAY;
}
static enum okay
imap_appenduid(struct mailbox *mp, FILE *fp, time_t t, long off1,
long xsize, long size, long lines, int flag, const char *name)
{
const char *cp;
unsigned long uidvalidity, uid;
struct mailbox xmb;
struct message xm;
if ((cp = asccasestr(responded_text, "[APPENDUID ")) == NULL ||
imap_appenduid_parse(&cp[11], &uidvalidity,
&uid) == STOP)
return STOP;
xmb = *mp;
xmb.mb_cache_directory = NULL;
xmb.mb_imap_mailbox = savestr((char *)name);
xmb.mb_uidvalidity = uidvalidity;
xmb.mb_otf = xmb.mb_itf = fp;
initcache(&xmb);
memset(&xm, 0, sizeof xm);
xm.m_flag = flag&MREAD | MNEW;
xm.m_time = t;
xm.m_block = mailx_blockof(off1);
xm.m_offset = mailx_offsetof(off1);
xm.m_size = size;
xm.m_xsize = xsize;
xm.m_lines = xm.m_xlines = lines;
xm.m_uid = uid;
xm.m_have = HAVE_HEADER|HAVE_BODY;
putcache(&xmb, &xm);
return OKAY;
}
static enum okay
imap_appenduid_cached(struct mailbox *mp, FILE *fp)
{
FILE *tp = NULL;
time_t t;
long size, xsize, ysize, lines;
enum mflag flag = MNEW;
char *name, *buf, *bp, *cp, *tempCopy;
size_t bufsize, buflen, count;
enum okay ok = STOP;
buf = smalloc(bufsize = LINESIZE);
buflen = 0;
count = fsize(fp);
if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL)
goto stop;
for (bp = buf; *bp != ' '; bp++); /* strip old tag */
while (*bp == ' ')
bp++;
if ((cp = strrchr(bp, '{')) == NULL)
goto stop;
xsize = atol(&cp[1]) + 2;
if ((name = imap_strex(&bp[7], &cp)) == NULL)
goto stop;
while (*cp == ' ')
cp++;
if (*cp == '(') {
imap_getflags(cp, &cp, &flag);
while (*++cp == ' ');
}
t = imap_read_date_time(cp);
if ((tp = Ftemp(&tempCopy, "Rc", "w+", 0600, 1)) == NULL)
goto stop;
rm(tempCopy);
Ftfree(&tempCopy);
size = xsize;
ysize = lines = 0;
while (size > 0) {
if (fgetline(&buf, &bufsize, &count, &buflen, fp, 0) == NULL)
goto stop;
size -= buflen;
buf[--buflen] = '\0';
buf[buflen-1] = '\n';
fwrite(buf, 1, buflen, tp);
ysize += buflen;
lines++;
}
fflush(tp);
rewind(tp);
imap_appenduid(mp, tp, t, 0, xsize-2, ysize-1, lines-1, flag,
imap_unquotestr(name));
ok = OKAY;
stop: free(buf);
if (tp)
Fclose(tp);
return ok;
}
static enum okay
imap_search2(struct mailbox *mp, struct message *m, int count,
const char *spec, int f)
{
char *o;
size_t osize;
FILE *queuefp = NULL;
enum okay ok = STOP;
int i;
unsigned long n;
const char *cp;
char *xp, *cs, c;
c = 0;
for (cp = spec; *cp; cp++)
c |= *cp;
if (c & 0200) {
cp = gettcharset();
#ifdef HAVE_ICONV
if (asccasecmp(cp, "utf-8")) {
iconv_t it;
char *sp, *nsp, *nspec;
size_t sz, nsz;
if ((it = iconv_open_ft("utf-8", cp)) != (iconv_t)-1) {
sz = strlen(spec) + 1;
nsp = nspec = salloc(nsz = 6*strlen(spec) + 1);
sp = (char *)spec;
if (iconv(it, &sp, &sz, &nsp, &nsz)
!= (size_t)-1 && sz == 0) {
spec = nspec;
cp = "utf-8";
}
iconv_close(it);
}
}
#endif /* HAVE_ICONV */
cp = imap_quotestr(cp);
cs = salloc(n = strlen(cp) + 10);
snprintf(cs, n, "CHARSET %s ", cp);
} else
cs = "";
o = ac_alloc(osize = strlen(spec) + 60);
snprintf(o, osize, "%s UID SEARCH %s%s\r\n", tag(1), cs, spec);
IMAP_OUT(o, MB_COMD, goto out)
while (mp->mb_active & MB_COMD) {
ok = imap_answer(mp, 0);
if (response_status == RESPONSE_OTHER &&
response_other == MAILBOX_DATA_SEARCH) {
xp = responded_other_text;
while (*xp && *xp != '\r') {
n = strtoul(xp, &xp, 10);
for (i = 0; i < count; i++)
if (m[i].m_uid == n &&
(m[i].m_flag&MHIDDEN)
== 0 &&
(f == MDELETED ||
(m[i].m_flag&MDELETED)
== 0))
mark(i+1, f);
}
}
}
out: ac_free(o);
return ok;
}
enum okay
imap_search1(const char *spec, int f)
{
sighandler_type saveint, savepipe;
enum okay ok = STOP;
(void)&saveint;
(void)&savepipe;
(void)&ok;
if (mb.mb_type != MB_IMAP)
return STOP;
verbose = value("verbose") != NULL;
imaplock = 1;
if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
safe_signal(SIGINT, maincatch);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1) == 0) {
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
ok = imap_search2(&mb, message, msgCount, spec, f);
}
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
if (interrupts)
onintr(0);
return ok;
}
int
imap_thisaccount(const char *cp)
{
if (mb.mb_type != MB_CACHE && mb.mb_type != MB_IMAP)
return 0;
if ((mb.mb_type != MB_CACHE && mb.mb_sock.s_fd < 0) ||
mb.mb_imap_account == NULL)
return 0;
return strcmp(protbase(cp), mb.mb_imap_account) == 0;
}
enum okay
imap_remove(const char *name)
{
sighandler_type saveint, savepipe;
enum okay ok = STOP;
(void)&saveint;
(void)&savepipe;
(void)&ok;
verbose = value("verbose") != NULL;
if (mb.mb_type != MB_IMAP) {
fprintf(stderr, "Refusing to remove \"%s\" "
"in disconnected mode.\n", name);
return STOP;
}
if (!imap_thisaccount(name)) {
fprintf(stderr, "Can only remove mailboxes on current IMAP "
"server: \"%s\" not removed.\n", name);
return STOP;
}
imaplock = 1;
if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
safe_signal(SIGINT, maincatch);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1) == 0) {
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
ok = imap_remove1(&mb, protfile(name));
}
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
if (ok == OKAY)
ok = cache_remove(name);
if (interrupts)
onintr(0);
return ok;
}
static enum okay
imap_remove1(struct mailbox *mp, const char *name)
{
FILE *queuefp = NULL;
char *o;
int os;
enum okay ok = STOP;
o = ac_alloc(os = 2*strlen(name) + 100);
snprintf(o, os, "%s DELETE %s\r\n", tag(1), imap_quotestr(name));
IMAP_OUT(o, MB_COMD, goto out)
while (mp->mb_active & MB_COMD)
ok = imap_answer(mp, 1);
out: ac_free(o);
return ok;
}
enum okay
imap_rename(const char *old, const char *new)
{
sighandler_type saveint, savepipe;
enum okay ok = STOP;
(void)&saveint;
(void)&savepipe;
(void)&ok;
verbose = value("verbose") != NULL;
if (mb.mb_type != MB_IMAP) {
fprintf(stderr, "Refusing to rename mailboxes "
"in disconnected mode.\n");
return STOP;
}
if (!imap_thisaccount(old) || !imap_thisaccount(new)) {
fprintf(stderr, "Can only rename mailboxes on current IMAP "
"server: \"%s\" not renamed to \"%s\".\n",
old, new);
return STOP;
}
imaplock = 1;
if ((saveint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
safe_signal(SIGINT, maincatch);
savepipe = safe_signal(SIGPIPE, SIG_IGN);
if (sigsetjmp(imapjmp, 1) == 0) {
if (savepipe != SIG_IGN)
safe_signal(SIGPIPE, imapcatch);
ok = imap_rename1(&mb, protfile(old), protfile(new));
}
safe_signal(SIGINT, saveint);
safe_signal(SIGPIPE, savepipe);
imaplock = 0;
if (ok == OKAY)
ok = cache_rename(old, new);
if (interrupts)
onintr(0);
return ok;
}
static enum okay
imap_rename1(struct mailbox *mp, const char *old, const char *new)
{
FILE *queuefp = NULL;
char *o;
int os;
enum okay ok = STOP;
o = ac_alloc(os = 2*strlen(old) + 2*strlen(new) + 100);
snprintf(o, os, "%s RENAME %s %s\r\n", tag(1),
imap_quotestr(old), imap_quotestr(new));
IMAP_OUT(o, MB_COMD, goto out)
while (mp->mb_active & MB_COMD)
ok = imap_answer(mp, 1);
out: ac_free(o);
return ok;
}
enum okay
imap_dequeue(struct mailbox *mp, FILE *fp)
{
FILE *queuefp = NULL;
char o[LINESIZE], *newname;
char *buf, *bp, *cp, iob[4096];
size_t bufsize, buflen, count;
enum okay ok = OKAY, rok = OKAY;
long offs, offs1, offs2, octets;
int n, twice, gotcha = 0;
buf = smalloc(bufsize = LINESIZE);
buflen = 0;
count = fsize(fp);
while (offs1 = ftell(fp),
fgetline(&buf, &bufsize, &count, &buflen, fp, 0)
!= NULL) {
for (bp = buf; *bp != ' '; bp++); /* strip old tag */
while (*bp == ' ')
bp++;
twice = 0;
offs = ftell(fp);
again: snprintf(o, sizeof o, "%s %s", tag(1), bp);
if (ascncasecmp(bp, "UID COPY ", 9) == 0) {
cp = &bp[9];
while (digitchar(*cp&0377))
cp++;
if (*cp != ' ')
goto fail;
while (*cp == ' ')
cp++;
if ((newname = imap_strex(cp, NULL)) == NULL)
goto fail;
IMAP_OUT(o, MB_COMD, continue)
while (mp->mb_active & MB_COMD)
ok = imap_answer(mp, twice);
if (response_status == RESPONSE_NO && twice++ == 0)
goto trycreate;
if (response_status == RESPONSE_OK &&
mp->mb_flags & MB_UIDPLUS) {
imap_copyuid(mp, NULL,
imap_unquotestr(newname));
}
} else if (ascncasecmp(bp, "UID STORE ", 10) == 0) {
IMAP_OUT(o, MB_COMD, continue)
while (mp->mb_active & MB_COMD)
ok = imap_answer(mp, 1);
if (ok == OKAY)
gotcha++;
} else if (ascncasecmp(bp, "APPEND ", 7) == 0) {
if ((cp = strrchr(bp, '{')) == NULL)
goto fail;
octets = atol(&cp[1]) + 2;
if ((newname = imap_strex(&bp[7], NULL)) == NULL)
goto fail;
IMAP_OUT(o, MB_COMD, continue)
while (mp->mb_active & MB_COMD) {
ok = imap_answer(mp, twice);
if (response_type == RESPONSE_CONT)
break;
}
if (ok == STOP) {
if (twice++ == 0) {
fseek(fp, offs, SEEK_SET);
goto trycreate;
}
goto fail;
}
while (octets > 0) {
n = octets > sizeof iob ? sizeof iob : octets;
octets -= n;
if (fread(iob, 1, n, fp) != n)
goto fail;
swrite1(&mp->mb_sock, iob, n, 1);
}
swrite(&mp->mb_sock, "");
while (mp->mb_active & MB_COMD) {
ok = imap_answer(mp, 0);
if (response_status == RESPONSE_NO &&
twice++ == 0) {
fseek(fp, offs, SEEK_SET);
goto trycreate;
}
}
if (response_status == RESPONSE_OK &&
mp->mb_flags & MB_UIDPLUS) {
offs2 = ftell(fp);
fseek(fp, offs1, SEEK_SET);
if (imap_appenduid_cached(mp, fp) == STOP) {
fseek(fp, offs2, SEEK_SET);
goto fail;
}
}
} else {
fail: fprintf(stderr,
"Invalid command in IMAP cache queue: \"%s\"\n",
bp);
rok = STOP;
}
continue;
trycreate:
snprintf(o, sizeof o, "%s CREATE %s\r\n",
tag(1), newname);
IMAP_OUT(o, MB_COMD, continue)
while (mp->mb_active & MB_COMD)
ok = imap_answer(mp, 1);
if (ok == OKAY)
goto again;
}
fflush(fp);
rewind(fp);
ftruncate(fileno(fp), 0);
if (gotcha)
imap_close(mp);
return rok;
}
static char *
imap_strex(const char *cp, char **xp)
{
const char *cq;
char *n;
if (*cp != '"')
return NULL;
for (cq = &cp[1]; *cq; cq++) {
if (*cq == '\\')
cq++;
else if (*cq == '"')
break;
}
if (*cq != '"')
return NULL;
n = salloc(cq - cp + 2);
memcpy(n, cp, cq - cp + 1);
n[cq - cp + 1] = '\0';
if (xp)
*xp = (char *)&cq[1];
return n;
}
static enum okay
check_expunged(void)
{
if (expunged_messages > 0) {
fprintf(stderr,
"Command not executed - messages have been expunged\n");
return STOP;
}
return OKAY;
}
/*ARGSUSED*/
int
cconnect(void *vp)
{
char *cp, *cq;
int omsgCount = msgCount;
if (mb.mb_type == MB_IMAP && mb.mb_sock.s_fd > 0) {
fprintf(stderr, "Already connected.\n");
return 1;
}
unset_allow_undefined = 1;
unset_internal("disconnected");
cp = protbase(mailname);
if (strncmp(cp, "imap://", 7) == 0)
cp += 7;
else if (strncmp(cp, "imaps://", 8) == 0)
cp += 8;
if ((cq = strchr(cp, ':')) != NULL)
*cq = '\0';
unset_internal(savecat("disconnected-", cp));
unset_allow_undefined = 0;
if (mb.mb_type == MB_CACHE) {
imap_setfile1(mailname, 0, edit, 1);
if (msgCount > omsgCount)
newmailinfo(omsgCount);
}
return 0;
}
int
cdisconnect(void *vp)
{
int *msgvec = vp;
if (mb.mb_type == MB_CACHE) {
fprintf(stderr, "Not connected.\n");
return 1;
} else if (mb.mb_type == MB_IMAP) {
if (cached_uidvalidity(&mb) == 0) {
fprintf(stderr, "The current mailbox is not cached.\n");
return 1;
}
}
if (*msgvec)
ccache(vp);
assign("disconnected", "");
if (mb.mb_type == MB_IMAP) {
sclose(&mb.mb_sock);
imap_setfile1(mailname, 0, edit, 1);
}
return 0;
}
int
ccache(void *vp)
{
int *msgvec = vp, *ip;
struct message *mp;
if (mb.mb_type != MB_IMAP) {
fprintf(stderr, "Not connected to an IMAP server.\n");
return 1;
}
if (cached_uidvalidity(&mb) == 0) {
fprintf(stderr, "The current mailbox is not cached.\n");
return 1;
}
for (ip = msgvec; *ip; ip++) {
mp = &message[*ip-1];
if (!(mp->m_have & HAVE_BODY))
get_body(mp);
}
return 0;
}
#else /* !HAVE_SOCKETS */
#include "extern.h"
static void
noimap(void)
{
fprintf(stderr, catgets(catd, CATSET, 216,
"No IMAP support compiled in.\n"));
}
int
imap_setfile(const char *server, int newmail, int isedit)
{
noimap();
return -1;
}
enum okay
imap_header(struct message *mp)
{
noimap();
return STOP;
}
enum okay
imap_body(struct message *mp)
{
noimap();
return STOP;
}
void
imap_getheaders(int bot, int top)
{
}
void
imap_quit(void)
{
noimap();
}
/*ARGSUSED*/
int
imap_imap(void *vp)
{
noimap();
return 1;
}
/*ARGSUSED*/
int
imap_newmail(int dummy)
{
return 0;
}
/*ARGSUSED*/
enum okay
imap_undelete(struct message *m, int n)
{
return STOP;
}
/*ARGSUSED*/
enum okay
imap_unread(struct message *m, int n)
{
return STOP;
}
/*ARGSUSED*/
enum okay
imap_append(const char *server, FILE *fp)
{
noimap();
return STOP;
}
/*ARGSUSED*/
void
imap_folders(const char *name, int strip)
{
noimap();
}
/*ARGSUSED*/
enum okay
imap_remove(const char *name)
{
noimap();
return STOP;
}
/*ARGSUSED*/
enum okay
imap_rename(const char *old, const char *new)
{
noimap();
return STOP;
}
enum okay
imap_copy(struct message *m, int n, const char *name)
{
noimap();
return STOP;
}
/*ARGSUSED*/
enum okay
imap_search1(const char *spec, int f)
{
return STOP;
}
int
imap_thisaccount(const char *cp)
{
return 0;
}
enum okay
imap_noop(void)
{
noimap();
return STOP;
}
/*ARGSUSED*/
int
cconnect(void *vp)
{
noimap();
return 1;
}
/*ARGSUSED*/
int
cdisconnect(void *vp)
{
noimap();
return 1;
}
/*ARGSUSED*/
int
ccache(void *vp)
{
noimap();
return 1;
}
#endif /* HAVE_SOCKETS */
time_t
imap_read_date_time(const char *cp)
{
time_t t;
int i, year, month, day, hour, minute, second;
int sign = -1;
char buf[3];
/*
* "25-Jul-2004 15:33:44 +0200"
* | | | | | |
* 0 5 10 15 20 25
*/
if (cp[0] != '"' || strlen(cp) < 28 || cp[27] != '"')
goto invalid;
day = strtol(&cp[1], NULL, 10);
for (i = 0; month_names[i]; i++)
if (ascncasecmp(&cp[4], month_names[i], 3) == 0)
break;
if (month_names[i] == NULL)
goto invalid;
month = i + 1;
year = strtol(&cp[8], NULL, 10);
hour = strtol(&cp[13], NULL, 10);
minute = strtol(&cp[16], NULL, 10);
second = strtol(&cp[19], NULL, 10);
if ((t = combinetime(year, month, day, hour, minute, second)) ==
(time_t)-1)
goto invalid;
switch (cp[22]) {
case '-':
sign = 1;
break;
case '+':
break;
default:
goto invalid;
}
buf[2] = '\0';
buf[0] = cp[23];
buf[1] = cp[24];
t += strtol(buf, NULL, 10) * sign * 3600;
buf[0] = cp[25];
buf[1] = cp[26];
t += strtol(buf, NULL, 10) * sign * 60;
return t;
invalid:
time(&t);
return t;
}
time_t
imap_read_date(const char *cp)
{
time_t t;
int year, month, day, i, tzdiff;
struct tm *tmptr;
char *xp, *yp;
if (*cp == '"')
cp++;
day = strtol(cp, &xp, 10);
if (day <= 0 || day > 31 || *xp++ != '-')
return -1;
for (i = 0; month_names[i]; i++)
if (ascncasecmp(xp, month_names[i], 3) == 0)
break;
if (month_names[i] == NULL)
return -1;
month = i+1;
if (xp[3] != '-')
return -1;
year = strtol(&xp[4], &yp, 10);
if (year < 1970 || year > 2037 || yp != &xp[8])
return -1;
if (yp[0] != '\0' && (yp[1] != '"' || yp[2] != '\0'))
return -1;
if ((t = combinetime(year, month, day, 0, 0, 0)) == (time_t)-1)
return -1;
tzdiff = t - mktime(gmtime(&t));
tmptr = localtime(&t);
if (tmptr->tm_isdst > 0)
tzdiff += 3600;
t -= tzdiff;
return t;
}
const char *
imap_make_date_time(time_t t)
{
static char s[30];
struct tm *tmptr;
int tzdiff, tzdiff_hour, tzdiff_min;
tzdiff = t - mktime(gmtime(&t));
tzdiff_hour = (int)(tzdiff / 60);
tzdiff_min = tzdiff_hour % 60;
tzdiff_hour /= 60;
tmptr = localtime(&t);
if (tmptr->tm_isdst > 0)
tzdiff_hour++;
snprintf(s, sizeof s, "\"%02d-%s-%04d %02d:%02d:%02d %+03d%02d\"",
tmptr->tm_mday,
month_names[tmptr->tm_mon],
tmptr->tm_year + 1900,
tmptr->tm_hour,
tmptr->tm_min,
tmptr->tm_sec,
tzdiff_hour,
tzdiff_min);
return s;
}
char *
imap_quotestr(const char *s)
{
char *n, *np;
np = n = salloc(2 * strlen(s) + 3);
*np++ = '"';
while (*s) {
if (*s == '"' || *s == '\\')
*np++ = '\\';
*np++ = *s++;
}
*np++ = '"';
*np = '\0';
return n;
}
char *
imap_unquotestr(const char *s)
{
char *n, *np;
if (*s != '"')
return savestr(s);
np = n = salloc(strlen(s) + 1);
while (*++s) {
if (*s == '\\')
s++;
else if (*s == '"')
break;
*np++ = *s;
}
*np = '\0';
return n;
}
syntax highlighted by Code2HTML, v. 0.9.1