📄 mix.c
字号:
/* MIX test for message file name * Accepts: candidate directory name * Returns: T if message file name, NIL otherwise * * ".mix" with no suffix was used by experimental versions */int mix_select (struct direct *name){ char c,*s; /* make sure name has prefix */ if (mix_dirfmttest (name->d_name)) { for (c = *(s = name->d_name + sizeof (MIXNAME) - 1); c && isxdigit (c); c = *s++); if (!c) return T; /* all-hex or no suffix */ } return NIL; /* not suffix or non-hex */}/* MIX msg file name comparision * Accepts: first candidate directory entry * second candidate directory entry * Returns: -1 if d1 < d2, 0 if d1 == d2, 1 d1 > d2 */int mix_msgfsort (const void *d1,const void *d2){ char *n1 = (*(struct direct **) d1)->d_name + sizeof (MIXNAME) - 1; char *n2 = (*(struct direct **) d2)->d_name + sizeof (MIXNAME) - 1; return compare_ulong (*n1 ? strtoul (n1,NIL,16) : 0, *n2 ? strtoul (n2,NIL,16) : 0);}/* MIX add a range to a set * Accepts: pointer to set to add * start of set * size of set * Returns: T if success, set updated, NIL otherwise */long mix_addset (SEARCHSET **set,unsigned long start,unsigned long size){ SEARCHSET *s = *set; if (start < s->last) { /* sanity check */ char tmp[MAILTMPLEN]; sprintf (tmp,"Backwards-running mix index %lu < %lu",start,s->last); MM_LOG (tmp,ERROR); return NIL; } /* range initially empty? */ if (!s->last) s->first = start; else if (start > s->last) /* no, start new range if can't append */ (*set = s = s->next = mail_newsearchset ())->first = start; s->last = start + size; /* end of current range */ return LONGT;}/* MIX burp message file * Accepts: MAIL stream * current burp block for this message * Returns: T if successful, NIL if failed */static char *staterr = "Error in stat of mix message file %.80s: %.80s";static char *truncerr = "Error truncating mix message file %.80s: %.80s";long mix_burp (MAILSTREAM *stream,MIXBURP *burp,unsigned long *reclaimed){ MESSAGECACHE *elt; SEARCHSET *set; struct stat sbuf; off_t rpos,wpos; size_t size,wsize,wpending,written; int fd; FILE *f; void *s; unsigned long i; long ret = NIL; /* build file name */ mix_file_data (LOCAL->buf,stream->mailbox,burp->fileno); /* need to burp at start or multiple ranges? */ if (!burp->set.first && !burp->set.next) { /* easy case, single range at start of file */ if (stat (LOCAL->buf,&sbuf)) { sprintf (LOCAL->buf,staterr,burp->name,strerror (errno)); MM_LOG (LOCAL->buf,ERROR); } /* is this range sane? */ else if (mix_burp_check (&burp->set,sbuf.st_size,LOCAL->buf)) { /* if matches range then no burp needed! */ if (burp->set.last == sbuf.st_size) ret = LONGT; /* just need to remove cruft at end */ else if (ret = !truncate (LOCAL->buf,burp->set.last)) *reclaimed += sbuf.st_size - burp->set.last; else { sprintf (LOCAL->buf,truncerr,burp->name,strerror (errno)); MM_LOG (LOCAL->buf,ERROR); } } } /* have to do more work, get the file */ else if (((fd = open (LOCAL->buf,O_RDWR,NIL)) < 0) || !(f = fdopen (fd,"r+b"))) { sprintf (LOCAL->buf,"Error opening mix message file %.80s: %.80s", burp->name,strerror (errno)); MM_LOG (LOCAL->buf,ERROR); if (fd >= 0) close (fd); /* in case fdopen() failure */ } else if (fstat (fd,&sbuf)) { /* get file size */ sprintf (LOCAL->buf,staterr,burp->name,strerror (errno)); MM_LOG (LOCAL->buf,ERROR); fclose (f); } /* only if sane */ else if (mix_burp_check (&burp->set,sbuf.st_size,LOCAL->buf)) { /* make sure each range starts with token */ for (set = &burp->set; set; set = set->next) if (fseek (f,set->first,SEEK_SET) || (fread (LOCAL->buf,1,MSGTSZ,f) != MSGTSZ) || strncmp (LOCAL->buf,MSGTOK,MSGTSZ)) { sprintf (LOCAL->buf,"Bad message token in mix message file at %lu", set->first); MM_LOG (LOCAL->buf,ERROR); fclose (f); return NIL; /* burp fails for this file */ } /* burp out each old message */ for (set = &burp->set, wpos = 0; set; set = set->next) { /* move down this range */ for (rpos = set->first, size = set->last - set->first; size; size -= wsize) { if (rpos != wpos) { /* data to skip at start? */ /* no, slide this buffer down */ wsize = min (size,LOCAL->buflen); /* failure is not an option here */ while (fseek (f,rpos,SEEK_SET) || (fread (LOCAL->buf,1,wsize,f) != wsize)) { MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } /* nor here */ while (fseek (f,wpos,SEEK_SET)) { MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } /* and especially not here */ for (s = LOCAL->buf, wpending = wsize; wpending; wpending -= written) if (!(written = fwrite (LOCAL->buf,1,wpending,f))) { MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } } else wsize = size; /* nothing to skip, say we wrote it all */ rpos += wsize; wpos += wsize; } } while (fflush (f)) { /* failure also not an option here... */ MM_NOTIFY (stream,strerror (errno),WARN); MM_DISKERROR (stream,errno,T); } if (ftruncate (fd,wpos)) { /* flush cruft at end of file */ sprintf (LOCAL->buf,truncerr,burp->name,strerror (errno)); MM_LOG (LOCAL->buf,WARN); } else *reclaimed += rpos - wpos; ret = !fclose (f); /* close file */ /* slide down message positions in index */ for (i = 1,rpos = 0; i <= stream->nmsgs; ++i) if ((elt = mail_elt (stream,i))->private.spare.data == burp->fileno) { elt->private.special.offset = rpos; rpos += elt->private.msg.header.offset + elt->rfc822_size; } /* debugging */ if (rpos != wpos) fatal ("burp size consistency check!"); } return ret;}/* MIX burp sanity check to make sure not burping off end of file * Accepts: burp set * file size * file name * Returns: T if sane, NIL if insane */long mix_burp_check (SEARCHSET *set,size_t size,char *file){ do if (set->last > size) { /* sanity check */ char tmp[MAILTMPLEN]; sprintf (tmp,"Unexpected short mix message file %.80s %lu < %lu", file,size,set->last); MM_LOG (tmp,ERROR); return NIL; /* don't burp this file at all */ } while (set = set->next); return LONGT;}/* MIX mail copy message(s) * Accepts: MAIL stream * sequence * destination mailbox * copy options * Returns: T if copy successful, else NIL */long mix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options){ FDDATA d; STRING st; char tmp[2*MAILTMPLEN]; long ret = mix_isvalid (mailbox,LOCAL->buf); mailproxycopy_t pc = (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); MAILSTREAM *astream = NIL; FILE *idxf = NIL; FILE *msgf = NIL; FILE *statf = NIL; if (!ret) switch (errno) { /* make sure valid mailbox */ case NIL: /* no error in stat() */ if (pc) return (*pc) (stream,sequence,mailbox,options); sprintf (tmp,"Not a MIX-format mailbox: %.80s",mailbox); MM_LOG (tmp,ERROR); break; default: /* some stat() error */ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); break; } /* get sequence to copy */ else if (!(ret = ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)))); /* acquire stream to append */ else if (ret = ((astream = mail_open (NIL,mailbox,OP_SILENT)) && !astream->rdonly && (((MIXLOCAL *) astream->local)->expok = T) && (statf = mix_parse (astream,&idxf,LONGT,NIL))) ? LONGT : NIL) { int fd; unsigned long i; MESSAGECACHE *elt; unsigned long newsize,hdrsize,size; MIXLOCAL *local = (MIXLOCAL *) astream->local; unsigned long seq = mix_modseq (local->metaseq); /* make sure new modseq fits */ if (local->indexseq > seq) seq = local->indexseq + 1; if (local->statusseq > seq) seq = local->statusseq + 1; /* calculate size of per-message header */ sprintf (local->buf,MSRFMT,MSGTOK,0,0,0,0,0,0,0,'+',0,0,0); hdrsize = strlen (local->buf); MM_CRITICAL (stream); /* go critical */ astream->silent = T; /* no events here */ /* calculate size that will be added */ for (i = 1, newsize = 0; i <= stream->nmsgs; ++i) if ((elt = mail_elt (stream,i))->sequence) newsize += hdrsize + elt->rfc822_size; /* open data file */ if (msgf = mix_data_open (astream,&fd,&size,newsize)) { char *t; unsigned long j,uid,uidv; copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL); SEARCHSET *source = cu ? mail_newsearchset () : NIL; SEARCHSET *dest = cu ? mail_newsearchset () : NIL; for (i = 1,uid = uidv = 0; ret && (i <= stream->nmsgs); ++i) if (((elt = mail_elt (stream,i))->sequence) && elt->rfc822_size) { /* is message in current message file? */ if ((LOCAL->msgfd < 0) || (elt->private.spare.data != LOCAL->curmsg)) { if (LOCAL->msgfd >= 0) close (LOCAL->msgfd); if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf, stream->mailbox, elt->private.spare.data), O_RDONLY,NIL)) >= 0) LOCAL->curmsg = elt->private.spare.data; } if (LOCAL->msgfd < 0) ret = NIL; else { /* got file */ d.fd = LOCAL->msgfd;/* set up file descriptor */ /* start of message */ d.pos = elt->private.special.offset + elt->private.msg.header.offset; d.chunk = LOCAL->buf; d.chunksize = CHUNKSIZE; INIT (&st,fd_string,&d,elt->rfc822_size); /* init flag string */ tmp[0] = tmp[1] = '\0'; if (j = elt->user_flags) do if ((t = stream->user_flags[find_rightmost_bit (&j)]) && *t) strcat (strcat (tmp," "),t); while (j); if (elt->seen) strcat (tmp," \\Seen"); if (elt->deleted) strcat (tmp," \\Deleted"); if (elt->flagged) strcat (tmp," \\Flagged"); if (elt->answered) strcat (tmp," \\Answered"); if (elt->draft) strcat (tmp," \\Draft"); tmp[0] = '('; /* wrap list */ strcat (tmp,")"); /* if append OK, add to source set */ if ((ret = mix_append_msg (astream,msgf,tmp,elt,&st,dest, seq)) && source) mail_append_set (source,mail_uid (stream,i)); } } /* finish write if success */ if (ret && (ret = !fflush (msgf))) { fclose (msgf); /* all good, close the msg file now */ /* write new metadata, index, and status */ local->metaseq = local->indexseq = local->statusseq = seq; if (ret = (mix_meta_update (astream) && mix_index_update (astream,idxf,LONGT))) { /* success, delete if doing a move */ if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++) if ((elt = mail_elt (stream,i))->sequence) { elt->deleted = T; if (!stream->rdonly) elt->private.mod = LOCAL->statusseq = seq; MM_FLAGS (stream,elt->msgno); } /* done with status file now */ mix_status_update (astream,statf,LONGT); /* return sets if doing COPYUID */ if (cu) (*cu) (stream,mailbox,astream->uid_validity,source,dest); source = dest = NIL; /* don't free these sets now */ } } else { /* error */ if (errno) { /* output error message if system call error */ sprintf (tmp,"Message copy failed: %.80s",strerror (errno)); MM_LOG (tmp,ERROR); } ftruncate (fd,size); /* revert file */ close (fd); /* make sure that fclose doesn't corrupt us */ fclose (msgf); /* free the stdio resources */ } /* flush any sets remaining */ mail_free_searchset (&source); mail_free_searchset (&dest); } else { /* message file open failed */ sprintf (tmp,"Error opening copy message file: %.80s", strerror (errno)); MM_LOG (tmp,ERROR); ret = NIL; } MM_NOCRITICAL (stream); } else MM_LOG ("Can't open copy mailbox",ERROR); if (statf) fclose (statf); /* close status if still open */ if (idxf) fclose (idxf); /* close index if still open */ /* finished with append stream */ if (astream) mail_close (astream); return ret; /* return state */}/* MIX mail append message from stringstruct * Accepts: MAIL stream * destination mailbox * append callback * data for callback * Returns: T if append successful, else NIL */long mix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data){ STRING *message; char *flags,*date,tmp[MAILTMPLEN]; /* N.B.: can't use LOCAL->buf for tmp */ long ret = mix_isvalid (mailbox,tmp); /* default stream to prototype */ if (!stream) stream = user_flags (&mixproto); if (!ret) switch (errno) { /* if not valid mailbox */ case ENOENT: /* no such file? */ if (ret = compare_cstring (mailbox,"INBOX") ? NIL : mix_create (NIL,"INBOX"))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -