diff options
| author | Sam Varshavchik | 2013-08-19 16:39:41 -0400 | 
|---|---|---|
| committer | Sam Varshavchik | 2013-08-25 14:43:51 -0400 | 
| commit | 9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch) | |
| tree | 7a81a04cb51efb078ee350859a64be2ebc6b8813 /imap/fetch.c | |
| parent | a9520698b770168d1f33d6301463bb70a19655ec (diff) | |
| download | courier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2 | |
Initial checkin
Imported from subversion report, converted to git. Updated all paths in
scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'imap/fetch.c')
| -rw-r--r-- | imap/fetch.c | 1626 | 
1 files changed, 1626 insertions, 0 deletions
| diff --git a/imap/fetch.c b/imap/fetch.c new file mode 100644 index 0000000..54e7139 --- /dev/null +++ b/imap/fetch.c @@ -0,0 +1,1626 @@ +/* +** Copyright 1998 - 2010 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if	HAVE_CONFIG_H +#include	"config.h" +#endif + +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<ctype.h> +#include	<errno.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif +#include	<sys/types.h> +#include	<sys/stat.h> + +#include	"imaptoken.h" +#include	"imapwrite.h" +#include	"imapscanclient.h" +#include	"fetchinfo.h" +#include	"rfc822/rfc822.h" +#include	"rfc2045/rfc2045.h" +#include	"maildir/config.h" +#include	"maildir/maildirgetquota.h" +#include	"maildir/maildirquota.h" +#include	"maildir/maildiraclt.h" + +#if SMAP +extern int smapflag; +#endif + +static const char unavailable[]= +	"\ +From: System Administrator <root@localhost>\n\ +Subject: message unavailable\n\n\ +This message is no longer available on the server.\n"; + +unsigned long header_count=0, body_count=0;	/* Total transferred */ + +extern int current_mailbox_ro; +extern char *current_mailbox_acl; +extern struct imapscaninfo current_maildir_info; +extern char *current_mailbox; +extern char *rfc2045id(struct rfc2045 *); + +extern void snapshot_needed(); + +extern void msgenvelope(void (*)(const char *, size_t), +                FILE *, struct rfc2045 *); +extern void msgbodystructure( void (*)(const char *, size_t), int, +        FILE *, struct rfc2045 *); + +extern int is_trash(const char *); +extern void get_message_flags(struct imapscanmessageinfo *, +	char *, struct imapflags *); +extern void append_flags(char *, struct imapflags *); + +static int fetchitem(FILE **, int *, struct fetchinfo *, +	struct imapscaninfo *,  unsigned long, +	struct rfc2045 **); + +static void bodystructure(FILE *, struct fetchinfo *, +	struct imapscaninfo *,  unsigned long, +	struct rfc2045 *); + +static void body(FILE *, struct fetchinfo *, +	struct imapscaninfo *,  unsigned long, +	struct rfc2045 *); + +static void fetchmsgbody(FILE *, struct fetchinfo *, +	struct imapscaninfo *,  unsigned long, +	struct rfc2045 *); + +static void dofetchmsgbody(FILE *, struct fetchinfo *, +	struct imapscaninfo *,  unsigned long, +	struct rfc2045 *); + +static void envelope(FILE *, struct fetchinfo *, +	struct imapscaninfo *,  unsigned long, +	struct rfc2045 *); + +void doflags(FILE *, struct fetchinfo *, +	     struct imapscaninfo *, unsigned long, struct rfc2045 *); + +static void internaldate(FILE *, struct fetchinfo *, +	struct imapscaninfo *, unsigned long, struct rfc2045 *); + +static void uid(FILE *, struct fetchinfo *, +	struct imapscaninfo *, unsigned long, struct rfc2045 *); + +static void all(FILE *, struct fetchinfo *, +	struct imapscaninfo *, unsigned long, struct rfc2045 *); + +static void fast(FILE *, struct fetchinfo *, +	struct imapscaninfo *, unsigned long, struct rfc2045 *); + +static void full(FILE *, struct fetchinfo *, +	struct imapscaninfo *, unsigned long, struct rfc2045 *); + +static void rfc822size(FILE *, struct fetchinfo *, +	struct imapscaninfo *, unsigned long, struct rfc2045 *); + +#if 0 +static void do_envelope(FILE *, struct fetchinfo *, +	struct imapscanmessageinfo *, struct rfc2045 *); +#endif + +static void dofetchheadersbuf(FILE *, struct fetchinfo *, +	struct imapscaninfo *, unsigned long, +	struct rfc2045 *, +	int (*)(struct fetchinfo *fi, const char *)); +static void dofetchheadersfile(FILE *, struct fetchinfo *, +	struct imapscaninfo *, unsigned long, +	struct rfc2045 *, +	int (*)(struct fetchinfo *fi, const char *)); + +static void print_bodysection_partial(struct fetchinfo *, +		void (*)(const char *)); +static void print_bodysection_output(const char *); + +static int dofetchheaderfields(struct fetchinfo *, const char *); +static int dofetchheadernotfields(struct fetchinfo *, const char *); +static int dofetchheadermime(struct fetchinfo *, const char *); + +static void rfc822(FILE *, struct fetchinfo *, +	struct imapscaninfo *, unsigned long, +	struct rfc2045 *); + +static void rfc822header(FILE *, struct fetchinfo *, +	struct imapscaninfo *, unsigned long, +	struct rfc2045 *); + +static void rfc822text(FILE *, struct fetchinfo *, +	struct imapscaninfo *, unsigned long, +	struct rfc2045 *); + +struct rfc2045 *fetch_alloc_rfc2045(unsigned long, FILE *); +FILE *open_cached_fp(unsigned long); + +void fetchflags(unsigned long); + +static void fetcherrorprt(const char *p) +{ +	fprintf(stderr, "%s", p); +} + +static void fetcherror(const char *errmsg, +		struct fetchinfo *fi, +		struct imapscaninfo *info, unsigned long j) +{ +struct imapscanmessageinfo *mi=info->msgs+j; + +	fprintf(stderr, "IMAP FETCH ERROR: %s, uid=%u, filename=%s: %s", +		errmsg, (unsigned)getuid(), mi->filename, fi->name); +	if (fi->bodysection) +		print_bodysection_partial(fi, &fetcherrorprt); +	fprintf(stderr, "\n"); +} + +char *get_reflagged_filename(const char *fn, struct imapflags *newflags) +{ +	char *p=malloc(strlen(fn)+20); +	char *q; + +	if (!p)	write_error_exit(0); +	strcpy(p, fn); +	if ((q=strrchr(p, MDIRSEP[0])) != 0)	*q=0; +	strcat(p, MDIRSEP "2,"); +	append_flags(p, newflags); +	return p; +} + +int reflag_filename(struct imapscanmessageinfo *mi, struct imapflags *flags, +	int fd) +{ +char    *p, *q, *r; +int	rc=0; +struct	imapflags old_flags; +struct	stat	stat_buf; + +	get_message_flags(mi, 0, &old_flags); + +	p=get_reflagged_filename(mi->filename, flags); + +	q=malloc(strlen(current_mailbox)+strlen(mi->filename)+sizeof("/cur/")); +	r=malloc(strlen(current_mailbox)+strlen(p)+sizeof("/cur/")); +	if (!q || !r)	write_error_exit(0); +	strcat(strcat(strcpy(q, current_mailbox), "/cur/"), mi->filename); +	strcat(strcat(strcpy(r, current_mailbox), "/cur/"), p); +	if (strcmp(q, r)) +	{ +		if (maildirquota_countfolder(current_mailbox) +			&& old_flags.deleted != flags->deleted +			&& fstat(fd, &stat_buf) == 0) +		{ +			struct maildirsize quotainfo; +			int64_t	nbytes; +			unsigned long unbytes; +			int	nmsgs=1; + +			if (maildir_parsequota(mi->filename, &unbytes) == 0) +				nbytes=unbytes; +			else +				nbytes=stat_buf.st_size; +			if ( flags->deleted ) +			{ +				nbytes= -nbytes; +				nmsgs= -nmsgs; +			} + +			if ( maildir_quota_delundel_start(current_mailbox, +							  "ainfo, +							  nbytes, nmsgs)) +				rc= -1; +			else +				maildir_quota_delundel_end("ainfo, +							   nbytes, nmsgs); +		} + +		if (rc == 0) +			rename(q, r); + +#if SMAP +		snapshot_needed(); +#endif +	} +	free(q); +	free(r); +	free(mi->filename); +	mi->filename=p; + +#if 0 +	if (is_sharedsubdir(current_mailbox)) +		maildir_shared_updateflags(current_mailbox, p); +#endif + +	return (rc); +} + +int do_fetch(unsigned long n, int byuid, void *p) +{ +	struct fetchinfo *fi=(struct fetchinfo *)p; +	FILE	*fp; +	struct	rfc2045 *rfc2045p; +	int	seen; +	int	open_err; + +	fp=NULL; +	open_err=0; + +	writes("* "); +	writen(n); +	writes(" FETCH ("); + +	if (byuid) +	{ +	struct fetchinfo *fip; + +		for (fip=fi; fip; fip=fip->next) +			if (strcmp(fip->name, "UID") == 0) +				break; + +		if (fip == 0) +		{ +			writes("UID "); +			writen(current_maildir_info.msgs[n-1].uid); +			writes(" "); +		} +	} +	seen=0; +	rfc2045p=0; +	while (fi) +	{ +		if (fetchitem(&fp, &open_err, fi, ¤t_maildir_info, n-1, +			&rfc2045p))	seen=1; +		if ((fi=fi->next) != 0)	writes(" "); +	} +	writes(")\r\n"); + +	if (open_err) +	{ +		writes("* NO Cannot open message "); +		writen(n); +		writes("\r\n"); +		return (0); +	} + + +#if SMAP +	if (!smapflag) +#endif +		if (current_mailbox_acl && +		    strchr(current_mailbox_acl, ACL_SEEN[0]) == NULL) +			seen=0; /* No permissions */ + +	if (seen && !current_mailbox_ro) +	{ +	struct	imapflags	flags; + +		get_message_flags(current_maildir_info.msgs+(n-1), +				0, &flags); +		if (!flags.seen) +		{ +			flags.seen=1; +			reflag_filename(¤t_maildir_info.msgs[n-1],&flags, +				fileno(fp)); +			current_maildir_info.msgs[n-1].changedflags=1; +		} +	} + +	if (current_maildir_info.msgs[n-1].changedflags) +		fetchflags(n-1); +	return (0); +} + +static int fetchitem(FILE **fp, int *open_err, struct fetchinfo *fi, +	struct imapscaninfo *i, unsigned long msgnum, +	struct rfc2045 **mimep) +{ +	void (*fetchfunc)(FILE *, struct fetchinfo *, +			  struct imapscaninfo *, unsigned long, +			  struct rfc2045 *); +	int	parsemime=0; +	int	rc=0; +	int	do_open=1; + +	if (strcmp(fi->name, "ALL") == 0) +	{ +		parsemime=1; +		fetchfunc= &all; +	} +	else if (strcmp(fi->name, "BODYSTRUCTURE") == 0) +	{ +		parsemime=1; +		fetchfunc= &bodystructure; +	} +	else if (strcmp(fi->name, "BODY") == 0) +	{ +		parsemime=1; +		fetchfunc= &body; +		if (fi->bodysection) +		{ +			fetchfunc= &fetchmsgbody; +			rc=1; +		} +	} +	else if (strcmp(fi->name, "BODY.PEEK") == 0) +	{ +		parsemime=1; +		fetchfunc= &body; +		if (fi->bodysection) +			fetchfunc= &fetchmsgbody; +	} +	else if (strcmp(fi->name, "ENVELOPE") == 0) +	{ +		parsemime=1; +		fetchfunc= &envelope; +	} +	else if (strcmp(fi->name, "FAST") == 0) +	{ +		parsemime=1; +		fetchfunc= &fast; +	} +	else if (strcmp(fi->name, "FULL") == 0) +	{ +		parsemime=1; +		fetchfunc= &full; +	} +	else if (strcmp(fi->name, "FLAGS") == 0) +	{ +		fetchfunc= &doflags; +		do_open=0; +	} +	else if (strcmp(fi->name, "INTERNALDATE") == 0) +	{ +		fetchfunc= &internaldate; +	} +	else if (strcmp(fi->name, "RFC822") == 0) +	{ +		fetchfunc= &rfc822; +		rc=1; +	} +	else if (strcmp(fi->name, "RFC822.HEADER") == 0) +	{ +		fetchfunc= &rfc822header; +	} +	else if (strcmp(fi->name, "RFC822.SIZE") == 0) +	{ +		parsemime=1; +		fetchfunc= &rfc822size; +	} +	else if (strcmp(fi->name, "RFC822.TEXT") == 0) +	{ +		parsemime=1; +		fetchfunc= &rfc822text; +	} +	else if (strcmp(fi->name, "UID") == 0) +	{ +		fetchfunc= &uid; +		do_open=0; +	} +	else	return (0); + +	if (do_open && *fp == NULL) +	{ +		*fp=open_cached_fp(msgnum); +		if (!*fp) +		{ +			*open_err=1; +			return rc; +		} +	} + +	if (parsemime && !*mimep) +	{ +		*mimep=fetch_alloc_rfc2045(msgnum, *fp); +	} + +	(*fetchfunc)(*fp, fi, i, msgnum, *mimep); +	return (rc); +} + +static void bodystructure(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *i, unsigned long msgnum, +	struct rfc2045 *mimep) +{ +	writes("BODYSTRUCTURE "); +	msgbodystructure(writemem, 1, fp, mimep); +} + +static void body(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *i, unsigned long msgnum, +	struct rfc2045 *mimep) +{ +	writes("BODY "); +	msgbodystructure(writemem, 0, fp, mimep); +} + +static void envelope(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *i, unsigned long msgnum, +	struct rfc2045 *mimep) +{ +	writes("ENVELOPE "); +	msgenvelope( &writemem, fp, mimep); +} + +void fetchflags(unsigned long n) +{ +#if SMAP +	if (smapflag) +	{ +		writes("* FETCH "); +		writen(n+1); +	} +	else +#endif +	{ +		writes("* "); +		writen(n+1); +		writes(" FETCH ("); +	} + +	doflags(0, 0, ¤t_maildir_info, n, 0); + +#if SMAP +	if (smapflag) +	{ +		writes("\n"); +	} +	else +#endif +		writes(")\r\n"); +} + +void fetchflags_byuid(unsigned long n) +{ +	writes("* "); +	writen(n+1); +	writes(" FETCH ("); +	uid(0, 0, ¤t_maildir_info, n, 0); +	writes(" "); +	doflags(0, 0, ¤t_maildir_info, n, 0); +	writes(")\r\n"); +} + +void doflags(FILE *fp, struct fetchinfo *fi, +	     struct imapscaninfo *i, unsigned long msgnum, +	     struct rfc2045 *mimep) +{ +	struct libmail_kwMessageEntry *kme; + +	char	buf[256]; + +#if SMAP +	if (smapflag) +	{ +		writes(" FLAGS="); +		get_message_flags(i->msgs+msgnum, buf, 0); +		writes(buf); +	} +	else +#endif +	{ +		struct libmail_kwMessage *km; + +		writes("FLAGS "); + +		get_message_flags(i->msgs+msgnum, buf, 0); + +		writes("("); +		writes(buf); + +		if (buf[0]) +			strcpy(buf, " "); + +		if ((km=i->msgs[msgnum].keywordMsg) != NULL) +			for (kme=km->firstEntry; kme; kme=kme->next) +			{ +				writes(buf); +				strcpy(buf, " "); +				writes(keywordName(kme->libmail_keywordEntryPtr)); +			} +		writes(")"); +	} + +	i->msgs[msgnum].changedflags=0; +} + +static void internaldate(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *i, unsigned long msgnum, +	struct rfc2045 *mimep) +{ +struct	stat	stat_buf; +char	buf[256]; +char	*p, *q; + +	writes("INTERNALDATE "); +	if (fstat(fileno(fp), &stat_buf) == 0) +	{ +		rfc822_mkdate_buf(stat_buf.st_mtime, buf); + +		/* Convert RFC822 date to imap date */ + +		p=strchr(buf, ','); +		if (p)	++p; +		else	p=buf; +		while (*p == ' ')	++p; +		if ((q=strchr(p, ' ')) != 0)	*q++='-'; +		if ((q=strchr(p, ' ')) != 0)	*q++='-'; +		writes("\""); +		writes(p); +		writes("\""); +	} +	else +		writes("NIL"); +} + +static void uid(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *i, unsigned long msgnum, +	struct rfc2045 *mimep) +{ +	writes("UID "); +	writen(i->msgs[msgnum].uid); +} + +static void rfc822size(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *i, unsigned long msgnum, +	struct rfc2045 *mimep) +{ +off_t start_pos, end_pos, start_body; +off_t nlines, nbodylines; + +	writes("RFC822.SIZE "); + +	rfc2045_mimepos(mimep, &start_pos, &end_pos, &start_body, +		&nlines, &nbodylines); + +	writen(end_pos - start_pos + nlines); +} + +static void all(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *i, unsigned long msgnum, +	struct rfc2045 *mimep) +{ +	doflags(fp, fi, i, msgnum, mimep); +	writes(" "); +	internaldate(fp, fi, i, msgnum, mimep); +	writes(" "); +	rfc822size(fp, fi, i, msgnum, mimep); +	writes(" "); +	envelope(fp, fi, i, msgnum, mimep); +} + +static void fast(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *i, unsigned long msgnum, +	struct rfc2045 *mimep) +{ +	doflags(fp, fi, i, msgnum, mimep); +	writes(" "); +	internaldate(fp, fi, i, msgnum, mimep); +	writes(" "); +	rfc822size(fp, fi, i, msgnum, mimep); +} + +static void full(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *i, unsigned long msgnum, +	struct rfc2045 *mimep) +{ +	doflags(fp, fi, i, msgnum, mimep); +	writes(" "); +	internaldate(fp, fi, i, msgnum, mimep); +	writes(" "); +	rfc822size(fp, fi, i, msgnum, mimep); +	writes(" "); +	envelope(fp, fi, i, msgnum, mimep); +	writes(" "); +	body(fp, fi, i, msgnum, mimep); +} + +static void fetchmsgbody(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *i, unsigned long msgnum, +	struct rfc2045 *mimep) +{ +	writes("BODY"); +	print_bodysection_partial(fi, &print_bodysection_output); +	writes(" "); +	dofetchmsgbody(fp, fi, i, msgnum, mimep); +} + +static void print_bodysection_output(const char *p) +{ +	writes(p); +} + +static void print_bodysection_partial(struct fetchinfo *fi, +	void (*func)(const char *)) +{ +	(*func)("["); +	if (fi->bodysection) +	{ +	struct fetchinfo *subl; + +		(*func)(fi->bodysection); +		if (fi->bodysublist) +		{ +		char	*p=" ("; + +			for (subl=fi->bodysublist; subl; subl=subl->next) +			{ +				(*func)(p); +				p=" "; +				(*func)("\""); +				(*func)(subl->name); +				(*func)("\""); +			} +			(*func)(")"); +		} +	} +	(*func)("]"); +	if (fi->ispartial) +	{ +	char	buf[80]; + +		sprintf(buf, "<%lu>", (unsigned long)fi->partialstart); +		(*func)(buf); +	} +} + +static void dofetchmsgbody(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *i, unsigned long msgnum, +	struct rfc2045 *mimep) +{ +const char *p=fi->bodysection; +off_t start_pos, end_pos, start_body; +off_t nlines, nbodylines; +unsigned long cnt; +char	buf[BUFSIZ]; +char	rbuf[BUFSIZ]; +char	*rbufptr; +int	rbufleft; +unsigned long bufptr; +unsigned long skipping; +int	ismsgrfc822=1; + +off_t start_seek_pos; + struct rfc2045 *headermimep; + +/* +** To optimize consecutive FETCHes, we cache our virtual and physical +** position.  What we do is that on the first fetch we count off the +** characters we read, and keep track of both the physical and the CRLF-based +** offset into the message.  Then, on subsequent FETCHes, we attempt to +** use that information. +*/ + +off_t cnt_virtual_chars; +off_t cnt_phys_chars; + +off_t cache_virtual_chars; +off_t cache_phys_chars; + +	headermimep=mimep; +  +	while (p && isdigit((int)(unsigned char)*p)) +	{ +	unsigned long n=0; + +		headermimep=mimep; + +		do +		{ +			n=n*10 + (*p++ - '0'); +		} while (isdigit((int)(unsigned char)*p)); + +		if (mimep) +		{ +			if (ismsgrfc822) +			{ +				const char *ct, *dummy; + +				if (mimep->firstpart == 0) +				{ +					/* Not a multipart, n must be 1 */ +					if (n != 1) +						mimep=0; +					if (*p == '.') +						++p; +					continue; +				} +				ismsgrfc822=0; + +				rfc2045_mimeinfo(mimep, &ct, +						 &dummy, +						 &dummy); + +				if (ct && strcasecmp(ct, "message/rfc822" +						     ) == 0) +					ismsgrfc822=1; +				/* The content is another message/rfc822 */ +			} + +			mimep=mimep->firstpart; +			while (mimep) +			{ +				if (!mimep->isdummy && --n == 0) +					break; +				mimep=mimep->next; +			} +			headermimep=mimep; + +			if (mimep && mimep->firstpart && +				!mimep->firstpart->isdummy) +				/* This is a message/rfc822 part */ +			{ +				if (!*p) +					break; + +				mimep=mimep->firstpart; +				ismsgrfc822=1; +			} +		} +		if (*p == '.') +			++p; +	} + +	if (p && strcmp(p, "MIME") == 0) +		mimep=headermimep; + +	if (mimep == 0) +	{ +		writes("{0}\r\n"); +		return; +	} + +	rfc2045_mimepos(mimep, &start_pos, &end_pos, &start_body, +		&nlines, &nbodylines); + + +	if (p && strcmp(p, "TEXT") == 0) +	{ +		start_seek_pos=start_body; +		cnt=end_pos - start_body + nbodylines; +	} +	else if (p && strcmp(p, "HEADER") == 0) +	{ +		start_seek_pos=start_pos; +		cnt= start_body - start_pos + (nlines - nbodylines); +	} +	else if (p && strcmp(p, "HEADER.FIELDS") == 0) +	{ +		if (start_body - start_pos <= BUFSIZ) +			dofetchheadersbuf(fp, fi, i, msgnum, mimep, +				&dofetchheaderfields); +		else +			dofetchheadersfile(fp, fi, i, msgnum, mimep, +				&dofetchheaderfields); +		return; +	} +	else if (p && strcmp(p, "HEADER.FIELDS.NOT") == 0) +	{ +		if (start_body - start_pos <= BUFSIZ) +			dofetchheadersbuf(fp, fi, i, msgnum, mimep, +				&dofetchheadernotfields); +		else +			dofetchheadersfile(fp, fi, i, msgnum, mimep, +				&dofetchheadernotfields); +		return; +	} +	else if (p && strcmp(p, "MIME") == 0) +	{ +		if (start_body - start_pos <= BUFSIZ) +			dofetchheadersbuf(fp, fi, i, msgnum, mimep, +				&dofetchheadermime); +		else +			dofetchheadersfile(fp, fi, i, msgnum, mimep, +				&dofetchheadermime); +		return; +	} +	else if (*fi->bodysection == 0) +	{ +		start_seek_pos=start_pos; + +		cnt= end_pos - start_pos + nlines; +	} +	else	/* Last possibility: entire body */ +	{ +		start_seek_pos=start_body; + +		cnt= end_pos - start_body + nbodylines; +	} + +	skipping=0; +	if (fi->ispartial) +	{ +		skipping=fi->partialstart; +		if (skipping > cnt)	skipping=cnt; +		cnt -= skipping; +		if (fi->ispartial > 1 && cnt > fi->partialend) +			cnt=fi->partialend; +	} + +	if (get_cached_offsets(start_seek_pos, &cnt_virtual_chars, +			       &cnt_phys_chars) == 0 && +	    cnt_virtual_chars <= skipping)	/* Yeah - cache it, baby! */ +	{ +		if (fseek(fp, start_seek_pos+cnt_phys_chars, SEEK_SET) == -1) +		{ +			writes("{0}\r\n"); +			fetcherror("fseek", fi, i, msgnum); +			return; +		} +		skipping -= cnt_virtual_chars; +	} +	else +	{ +		if (fseek(fp, start_seek_pos, SEEK_SET) == -1) +		{ +			writes("{0}\r\n"); +			fetcherror("fseek", fi, i, msgnum); +			return; +		} + +		cnt_virtual_chars=0; +		cnt_phys_chars=0; +	} + +	cache_virtual_chars=cnt_virtual_chars; +	cache_phys_chars=cnt_phys_chars; + +	writes("{"); +	writen(cnt); +	writes("}\r\n"); +	bufptr=0; +	writeflush(); + +	rbufptr=0; +	rbufleft=0; + +	while (cnt) +	{ +	int	c; + +		if (!rbufleft) +		{ +			rbufleft=fread(rbuf, 1, sizeof(rbuf), fp); +			if (rbufleft < 0)	rbufleft=0; +			rbufptr=rbuf; +		} + +		if (!rbufleft) +		{ +			fetcherror("unexpected EOF", fi, i, msgnum); +			_exit(1); +		} + +		--rbufleft; +		c=(int)(unsigned char)*rbufptr++; +		++cnt_phys_chars; + +		if (c == '\n') +		{ +			++cnt_virtual_chars; + +			if (skipping) +				--skipping; +			else +			{ +				if (bufptr >= sizeof(buf)) +				{ +					writemem(buf, sizeof(buf)); +					bufptr=0; +					/*writeflush();*/ +				} +				buf[bufptr++]='\r'; +				--cnt; + +				if (cnt == 0) +					break; +			} +		} + +		++cnt_virtual_chars; +		if (skipping) +			--skipping; +		else +		{ +			++body_count; + +			if (bufptr >= sizeof(buf)) +			{ +				writemem(buf, sizeof(buf)); +				bufptr=0; +				/*writeflush();*/ +			} +			buf[bufptr++]=c; +			--cnt; +		} +		cache_virtual_chars=cnt_virtual_chars; +		cache_phys_chars=cnt_phys_chars; +	} +	writemem(buf, bufptr); +	writeflush(); +	save_cached_offsets(start_seek_pos, cache_virtual_chars, +			    cache_phys_chars); +} + +static int dofetchheaderfields(struct fetchinfo *fi, const char *name) +{ +	for (fi=fi->bodysublist; fi; fi=fi->next) +	{ +	int	i, a, b; + +		if (fi->name == 0)	continue; +		for (i=0; fi->name[i] && name[i]; i++) +		{ +			a=(unsigned char)name[i]; +			a=toupper(a); +			b=fi->name[i]; +			b=toupper(b); +			if (a != b)	break; +		} +		if (fi->name[i] == 0 && name[i] == 0)	return (1); +	} + +	return (0); +} + +static int dofetchheadernotfields(struct fetchinfo *fi, const char *name) +{ +	return (!dofetchheaderfields(fi, name)); +} + +static int dofetchheadermime(struct fetchinfo *fi, const char *name) +{ +int	i, a; +static const char mv[]="MIME-VERSION"; + +	for (i=0; i<sizeof(mv)-1; i++) +	{ +		a= (unsigned char)name[i]; +		a=toupper(a); +		if (a != mv[i])	break; +	} +	if (mv[i] == 0 && name[i] == 0)	return (1); + +	for (i=0; i<8; i++) +	{ +		a= (unsigned char)name[i]; +		a=toupper(a); +		if (a != "CONTENT-"[i])	return (0); +	} +	return (1); +} + +static void dofetchheadersbuf(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *info, unsigned long msgnum, +	struct rfc2045 *mimep, +	int (*headerfunc)(struct fetchinfo *fi, const char *)) +{ +off_t start_pos, end_pos, start_body; +off_t nlines, nbodylines; +size_t i,j,k,l; +char	buf[BUFSIZ+2]; +int	goodheader; +unsigned long skipping; +unsigned long cnt; +char	*p; +int	ii; + +	rfc2045_mimepos(mimep, &start_pos, &end_pos, &start_body, +		&nlines, &nbodylines); +	if (fseek(fp, start_pos, SEEK_SET) == -1) +	{ +		writes("{0}\r\n"); +		fetcherror("fseek", fi, info, msgnum); +		return; +	} + +	ii=fread(buf, 1, start_body - start_pos, fp); +	if (ii < 0 || (i=ii) != start_body - start_pos) +	{ +		fetcherror("unexpected EOF", fi, info, msgnum); +		exit(1); +	} +	goodheader= (*headerfunc)(fi, ""); + +	l=0; +	for (j=0; j<i; ) +	{ +		if (buf[j] != '\n' && buf[j] != '\r' && +			!isspace((int)(unsigned char)buf[j])) +		{ +			goodheader= (*headerfunc)(fi, ""); + +			for (k=j; k<i; k++) +			{ +				if (buf[k] == '\n' || buf[k] == ':') +					break; +			} + +			if (k < i && buf[k] == ':') +			{ +				buf[k]=0; +				goodheader=(*headerfunc)(fi, buf+j); +				buf[k]=':'; +			} +		} +		else if (buf[j] == '\n') +			goodheader=0; + +		for (k=j; k<i; k++) +			if (buf[k] == '\n') +			{ +				++k; +				break; +			} + +		if (goodheader) +		{ +			while (j<k) +				buf[l++]=buf[j++]; +		} +		j=k; +	} + +	buf[l++]='\n';	/* Always append a blank line */ + +	cnt=l; +	for (i=0; i<l; i++) +		if (buf[i] == '\n')	++cnt; + +	skipping=0; +	if (fi->ispartial) +	{ +		skipping=fi->partialstart; +		if (skipping > cnt)	skipping=cnt; +		cnt -= skipping; +		if (fi->ispartial > 1 && cnt > fi->partialend) +			cnt=fi->partialend; +	} + +	writes("{"); +	writen(cnt); +	writes("}\r\n"); +	p=buf; +	while (skipping) +	{ +		if (*p == '\n') +		{ +			--skipping; +			if (skipping == 0) +			{ +				if (cnt) +				{ +					writes("\n"); +					--cnt; +				} +				break; +			} +		} +		--skipping; +		++p; +	} + +	while (cnt) +	{ +		if (*p == '\n') +		{ +			writes("\r"); +			if (--cnt == 0)	break; +			writes("\n"); +			--cnt; +			++p; +			continue; +		} +		for (i=0; i<cnt; i++) +			if (p[i] == '\n') +				break; +		writemem(p, i); +		p += i; +		cnt -= i; +		header_count += i; +	} +} + +struct fetchheaderinfo { +	unsigned long skipping; +	unsigned long cnt; +	} ; + +static void countheader(struct fetchheaderinfo *, const char *, size_t); + +static void printheader(struct fetchheaderinfo *, const char *, size_t); + +static void dofetchheadersfile(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *info, unsigned long msgnum, +	struct rfc2045 *mimep, +	int (*headerfunc)(struct fetchinfo *fi, const char *)) +{ +off_t start_pos, end_pos, start_body, left; +off_t nlines, nbodylines; +size_t i; +int	c, pass; +char	buf1[256]; +int	goodheader; +struct	fetchheaderinfo finfo; + +	finfo.cnt=0; +	for (pass=0; pass<2; pass++) +	{ +	void (*func)(struct fetchheaderinfo *, const char *, size_t)= +			pass ? printheader:countheader; + +		rfc2045_mimepos(mimep, &start_pos, &end_pos, &start_body, +			&nlines, &nbodylines); +		if (fseek(fp, start_pos, SEEK_SET) == -1) +		{ +			writes("{0}\r\n"); +			fetcherror("fseek", fi, info, msgnum); +			return; +		} +		if (pass) +		{ +			finfo.skipping=0; +			if (fi->ispartial) +			{ +				finfo.skipping=fi->partialstart; +				if (finfo.skipping > finfo.cnt) +					finfo.skipping=finfo.cnt; +				finfo.cnt -= finfo.skipping; +				if (fi->ispartial > 1 && +					finfo.cnt > fi->partialend) +					finfo.cnt=fi->partialend; +			} + +			writes("{"); +			writen(finfo.cnt+2);	/* BUG */ +			writes("}\r\n"); +		} +		left=start_body - start_pos; + +		goodheader= (*headerfunc)(fi, ""); +		while (left) +		{ +			for (i=0; i<sizeof(buf1)-1 && i<left; i++) +			{ +				c=getc(fp); +				if (c == EOF) +				{ +					fetcherror("unexpected EOF", fi, info, msgnum); +					_exit(1); +				} + +				if (c == '\n' || c == ':') +				{ +					ungetc(c, fp); +					break; +				} +				buf1[i]=c; +			} +			buf1[i]=0; +			left -= i; + +			if (buf1[0] != '\n' && buf1[0] != '\r' && +				!isspace((int)(unsigned char)buf1[0])) +				goodheader= (*headerfunc)(fi, buf1); +			else if (buf1[0] == '\n') +				goodheader=0; + +			if (!goodheader) +			{ +				while (left) +				{ +					c=getc(fp); +					--left; +					if (c == EOF) +					{ +						fetcherror("unexpected EOF", fi, info, msgnum); +						_exit(1); +					} +					if (c == '\n')	break; +				} +				continue; +			} + +			(*func)(&finfo, buf1, i); + +			i=0; +			while (left) +			{ +				c=getc(fp); +				if (c == EOF) +				{ +					fetcherror("unexpected EOF", fi, info, msgnum); +					_exit(1); +				} +				--left; +				if (i >= sizeof(buf1)) +				{ +					(*func)(&finfo, buf1, i); +					i=0; +				} +				if (c == '\n') +				{ +					(*func)(&finfo, buf1, i); +					buf1[0]='\r'; +					i=1; +				} +				buf1[i++]=c; +				if (c == '\n')	break; +			} +			(*func)(&finfo, buf1, i); +			if (pass && finfo.cnt == 0)	break; +		} +	} +	writes("\r\n");	/* BUG */ +} + +static void countheader(struct fetchheaderinfo *fi, const char *p, size_t s) +{ +	fi->cnt += s; +} + +static void printheader(struct fetchheaderinfo *fi, const char *p, size_t s) +{ +	size_t i; + +	if (fi->skipping) +	{ +		if (fi->skipping > s) +		{ +			fi->skipping -= s; +			return; +		} +		p += fi->skipping; +		s -= fi->skipping; +		fi->skipping=0; +	} +	if (s > fi->cnt)	s=fi->cnt; +	for (i=0; i <= s; i++) +		if (p[i] != '\r') +			++header_count; +	writemem(p, s); +	fi->cnt -= s; +} + +static void rfc822(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *info, unsigned long msgnum, +	struct rfc2045 *rfcp) +{ +unsigned long n=0; +int	c; +char	buf[BUFSIZ]; +unsigned long i; + +	writes("RFC822 "); + +	if (fseek(fp, 0L, SEEK_SET) == -1) +	{ +		fetcherror("fseek", fi, info, msgnum); +		writes("{0}\r\n"); +		return; +	} +	while ((c=getc(fp)) != EOF) +	{ +		++n; +		if (c == '\n')	++n; +	} + +	if (fseek(fp, 0L, SEEK_SET) == -1) +	{ +		fetcherror("fseek", fi, info, msgnum); +		writes("{0}\r\n"); +		return; +	} +	writes("{"); +	writen(n); +	writes("}\r\n"); + +	i=0; +	while (n) +	{ +		c=getc(fp); +		if (c == '\n') +		{ +			if (i >= sizeof(buf)) +			{ +				writemem(buf, i); +				i=0; +			} +			buf[i++]='\r'; +			if (--n == 0)	break; +		} + +		if (i >= sizeof(buf)) +		{ +			writemem(buf, i); +			i=0; +		} +		buf[i++]=c; +		--n; +		++body_count; +	} +	writemem(buf, i); +} + +static void rfc822header(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *info, unsigned long msgnum, +	struct rfc2045 *rfcp) +{ +unsigned long n=0; +int	c; +char	buf[BUFSIZ]; +unsigned long i; +int	eol; + +	writes("RFC822.HEADER "); + +	if (fseek(fp, 0L, SEEK_SET) == -1) +	{ +		fetcherror("fseek", fi, info, msgnum); +		writes("{0}\r\n"); +		return; +	} + +	eol=0; +	while ((c=getc(fp)) != EOF) +	{ +		++n; +		if (c != '\n') +		{ +			eol=0; +			continue; +		} +		++n; +		if (eol)	break; +		eol=1; +	} + +	if (fseek(fp, 0L, SEEK_SET) == -1) +	{ +		fetcherror("fseek", fi, info, msgnum); +		writes("{0}\r\n"); +		return; +	} +	writes("{"); +	writen(n); +	writes("}\r\n"); + +	i=0; +	while (n) +	{ +		c=getc(fp); +		if (c == '\n') +		{ +			if (i >= sizeof(buf)) +			{ +				writemem(buf, i); +				i=0; +			} +			buf[i++]='\r'; +			if (--n == 0)	break; +		} + +		if (i >= sizeof(buf)) +		{ +			writemem(buf, i); +			i=0; +		} +		buf[i++]=c; +		--n; +		++header_count; +	} +	writemem(buf, i); +} + +static void rfc822text(FILE *fp, struct fetchinfo *fi, +	struct imapscaninfo *info, unsigned long msgnum, +	struct rfc2045 *rfcp) +{ +off_t start_pos, end_pos, start_body; +off_t nlines, nbodylines; +unsigned long i; +int	c; +char	buf[BUFSIZ]; +unsigned long l; + +	writes("RFC822.TEXT {"); + +	rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body, +		&nlines, &nbodylines); + +	if (fseek(fp, start_body, SEEK_SET) == -1) +	{ +		fetcherror("fseek", fi, info, msgnum); +		writes("0}\r\n"); +		return; +	} + +	i=end_pos - start_body + nbodylines; + +	writen(i); +	writes("}\r\n"); + +	l=0; +	while (i) +	{ +		c=getc(fp); +		if (c == EOF) +		{ +			fetcherror("unexpected EOF", fi, info, msgnum); +			_exit(1); +		} +		--i; +		if (l >= sizeof(BUFSIZ)) +		{ +			writemem(buf, l); +			l=0; +		} +		if (c == '\n' && i) +		{ +			--i; +			buf[l++]='\r'; +			if (l >= sizeof(BUFSIZ)) +			{ +				writemem(buf, l); +				l=0; +			} +		} +		buf[l++]=c; +		++body_count; +	} +	writemem(buf, l); +} + +/* +** Poorly written IMAP clients (read: Netscape Messenger) like to issue +** consecutive partial fetches for downloading large messages. +** +** To save the time of reparsing the MIME structure, we cache it. +*/ + +static struct rfc2045 *cached_rfc2045p; +static char *cached_filename; + +void fetch_free_cached() +{ +	if (cached_rfc2045p) +	{ +		rfc2045_free(cached_rfc2045p); +		cached_rfc2045p=0; +		free(cached_filename); +		cached_filename=0; +	} +} + +struct rfc2045 *fetch_alloc_rfc2045(unsigned long msgnum, FILE *fp) +{ +	if (cached_rfc2045p && +	    strcmp(cached_filename, +		   current_maildir_info.msgs[msgnum].filename) == 0) +		return (cached_rfc2045p); + +	fetch_free_cached(); + +	if ((cached_filename=strdup(current_maildir_info. +				    msgs[msgnum].filename)) +	    == 0) write_error_exit(0); + +	if (fseek(fp, 0L, SEEK_SET) == -1) +	{ +		write_error_exit(0); +		return (0); +	} +	cached_rfc2045p=rfc2045_fromfp(fp); +	if (!cached_rfc2045p) +	{ +		free(cached_filename); +		cached_filename=0; +		write_error_exit(0); +	} +	return (cached_rfc2045p); +} + +static FILE *cached_fp=0; +static char *cached_fp_filename=0; +static off_t cached_base_offset; +static off_t cached_virtual_offset; +static off_t cached_phys_offset; + +FILE *open_cached_fp(unsigned long msgnum) +{ +	int	fd; + +	if (cached_fp && strcmp(cached_fp_filename, +				current_maildir_info.msgs[msgnum].filename) +	    == 0) +		return (cached_fp); + +	if (cached_fp) +	{ +		fclose(cached_fp); +		free(cached_fp_filename); +		cached_fp_filename=0; +		cached_fp=0; +	} + +	fd=imapscan_openfile(current_mailbox, ¤t_maildir_info, msgnum); +	if (fd < 0 || (cached_fp=fdopen(fd, "r")) == 0) +	{ +		if (fd >= 0)	close(fd); + +		if ((cached_fp=tmpfile()) != 0) +		{ +			fprintf(cached_fp, unavailable); +			if (fseek(cached_fp, 0L, SEEK_SET) < 0 || +			    ferror(cached_fp)) +			{ +				fclose(cached_fp); +				cached_fp=0; +			} +		} + +		if (cached_fp == 0) +		{ +			fprintf(stderr, "ERR: %s: %s\n", +				getenv("AUTHENTICATED"), +#if	HAVE_STRERROR +				strerror(errno) +#else +				"error" +#endif + +				); +			fflush(stderr); +			_exit(1); +		} +	} + +	if ((cached_fp_filename=strdup(current_maildir_info. +				       msgs[msgnum].filename)) +	    == 0) +	{ +		fclose(cached_fp); +		cached_fp=0; +		write_error_exit(0); +	} +	cached_base_offset=0; +	cached_virtual_offset=0; +	cached_phys_offset=0; +	return (cached_fp); +} + +void fetch_free_cache() +{ +	if (cached_fp) +	{ +		fclose(cached_fp); +		cached_fp=0; +		free(cached_fp_filename); +		cached_fp_filename=0; +	} +} + +void save_cached_offsets(off_t base, off_t virt, off_t phys) +{ +	cached_base_offset=base; +	cached_virtual_offset=virt; +	cached_phys_offset=phys; +} + +int get_cached_offsets(off_t base, off_t *virt, off_t *phys) +{ +	if (!cached_fp) +		return (-1); +	if (base != cached_base_offset) +		return (-1); + +	*virt=cached_virtual_offset; +	*phys=cached_phys_offset; +	return (0); +} | 
