/* ** Copyright 1998 - 2018 Double Precision, Inc. ** See COPYING for distribution information. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #if HAVE_UNISTD_H #include #endif #include #include #include "imapd.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 \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 **, int *); 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; int unicode_err=0; int report_unicode_err=0; 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) { int rc=fetchitem(&fp, &open_err, fi, ¤t_maildir_info, n-1, &rfc2045p, &unicode_err); if (rc > 0) seen=1; if ((fi=fi->next) != 0) writes(" "); } writes(")\r\n"); if (open_err) { writes("* NO [ALERT] 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; report_unicode_err=unicode_err; } } if (report_unicode_err) { writes("* OK [ALERT] Message "); writen(n); writes(" appears to be a Unicode message and your" " E-mail reader did not enable Unicode support." " Please use an E-mail reader that supports" " IMAP with UTF-8 (see" " https://tools.ietf.org/html/rfc6855.html)\r\n"); } 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, int *unicode_err) { void (*fetchfunc)(FILE *, struct fetchinfo *, struct imapscaninfo *, unsigned long, struct rfc2045 *); int parsemime=0; int rc=0; int do_open=1; int mimecorrectness=0; 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; mimecorrectness=1; rc=1; } } else if (strcmp(fi->name, "BODY.PEEK") == 0) { parsemime=1; mimecorrectness=1; fetchfunc= &body; if (fi->bodysection) { fetchfunc= &fetchmsgbody; mimecorrectness=1; } } 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; mimecorrectness=1; rc=1; } else if (strcmp(fi->name, "RFC822.HEADER") == 0) { fetchfunc= &rfc822header; mimecorrectness=1; } else if (strcmp(fi->name, "RFC822.SIZE") == 0) { parsemime=1; fetchfunc= &rfc822size; } else if (strcmp(fi->name, "RFC822.TEXT") == 0) { parsemime=1; mimecorrectness=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 (mimecorrectness && !enabled_utf8) parsemime=1; if (parsemime && !*mimep) { *mimep=fetch_alloc_rfc2045(msgnum, *fp); } if (mimecorrectness && !enabled_utf8 && ((*mimep)->rfcviolation & RFC2045_ERR8BITHEADER)) { *unicode_err=1; } (*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; iispartial) { 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; iispartial) { 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)) { (*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 (fd <0 && errno == ENOENT && (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); }