📄 tenexnt.c
字号:
/* ======================================================================== * Copyright 1988-2007 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * * ======================================================================== *//* * Program: Tenex mail routines * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 22 May 1990 * Last Edited: 18 June 2007 *//* FILE TIME SEMANTICS * * The atime is the last read time of the file. * The mtime is the last flags update time of the file. * The ctime is the last write time of the file. * * TEXT SIZE SEMANTICS * * Most of the text sizes are in internal (LF-only) form, except for the * msg.text size. Beware. */#include <stdio.h>#include <ctype.h>#include <errno.h>extern int errno; /* just in case */#include "mail.h"#include "osdep.h"#include <fcntl.h>#include <time.h>#include <sys/stat.h>#include <sys/utime.h>#include "misc.h"#include "dummy.h"/* TENEX I/O stream local data */ typedef struct tenex_local { unsigned int shouldcheck: 1; /* if ping should do a check instead */ unsigned int mustcheck: 1; /* if ping must do a check instead */ int fd; /* file descriptor for I/O */ off_t filesize; /* file size parsed */ time_t filetime; /* last file time */ time_t lastsnarf; /* local snarf time */ unsigned char *buf; /* temporary buffer */ unsigned long buflen; /* current size of temporary buffer */ unsigned long uid; /* current text uid */ SIZEDTEXT text; /* current text */} TENEXLOCAL;/* Convenient access to local data */#define LOCAL ((TENEXLOCAL *) stream->local)/* Function prototypes */DRIVER *tenex_valid (char *name);int tenex_isvalid (char *name,char *file);void *tenex_parameters (long function,void *value);void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);void tenex_list (MAILSTREAM *stream,char *ref,char *pat);void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat);long tenex_create (MAILSTREAM *stream,char *mailbox);long tenex_delete (MAILSTREAM *stream,char *mailbox);long tenex_rename (MAILSTREAM *stream,char *old,char *newname);long tenex_status (MAILSTREAM *stream,char *mbx,long flags);MAILSTREAM *tenex_open (MAILSTREAM *stream);void tenex_close (MAILSTREAM *stream,long options);void tenex_fast (MAILSTREAM *stream,char *sequence,long flags);void tenex_flags (MAILSTREAM *stream,char *sequence,long flags);char *tenex_header (MAILSTREAM *stream,unsigned long msgno, unsigned long *length,long flags);long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);long tenex_ping (MAILSTREAM *stream);void tenex_check (MAILSTREAM *stream);void tenex_snarf (MAILSTREAM *stream);long tenex_expunge (MAILSTREAM *stream,char *sequence,long options);long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);unsigned long tenex_size (MAILSTREAM *stream,unsigned long m);long tenex_parse (MAILSTREAM *stream);MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno);void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);void tenex_update_status (MAILSTREAM *stream,unsigned long msgno, long syncflag);unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno, unsigned long *size);/* Tenex mail routines *//* Driver dispatch used by MAIL */DRIVER tenexdriver = { "tenex", /* driver name */ DR_LOCAL|DR_MAIL, /* driver flags */ (DRIVER *) NIL, /* next driver */ tenex_valid, /* mailbox is valid for us */ tenex_parameters, /* manipulate parameters */ tenex_scan, /* scan mailboxes */ tenex_list, /* list mailboxes */ tenex_lsub, /* list subscribed mailboxes */ NIL, /* subscribe to mailbox */ NIL, /* unsubscribe from mailbox */ tenex_create, /* create mailbox */ tenex_delete, /* delete mailbox */ tenex_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ tenex_open, /* open mailbox */ tenex_close, /* close mailbox */ tenex_flags, /* fetch message "fast" attributes */ tenex_flags, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ tenex_header, /* fetch message header */ tenex_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ tenex_flag, /* modify flags */ tenex_flagmsg, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ tenex_ping, /* ping mailbox to see if still alive */ tenex_check, /* check for new messages */ tenex_expunge, /* expunge deleted messages */ tenex_copy, /* copy messages to another mailbox */ tenex_append, /* append string message to mailbox */ NIL /* garbage collect stream */}; /* prototype stream */MAILSTREAM tenexproto = {&tenexdriver};/* Tenex mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */DRIVER *tenex_valid (char *name){ char tmp[MAILTMPLEN]; return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;}/* Tenex mail test for valid mailbox * Accepts: mailbox name * buffer to return file name * Returns: T if valid, NIL otherwise */int tenex_isvalid (char *name,char *file){ int fd; int ret = NIL; char *s,tmp[MAILTMPLEN]; struct stat sbuf; struct utimbuf times; errno = EINVAL; /* assume invalid argument */ /* if file, get its status */ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) { if (!sbuf.st_size)errno = 0;/* empty file */ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) { memset (tmp,'\0',MAILTMPLEN); if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) && (s[-1] != '\015')) { /* valid format? */ *s = '\0'; /* tie off header */ /* must begin with dd-mmm-yy" */ ret = (((tmp[2] == '-' && tmp[6] == '-') || (tmp[1] == '-' && tmp[5] == '-')) && (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL; } else errno = -1; /* bogus format */ close (fd); /* close the file */ /* \Marked status? */ if (sbuf.st_ctime > sbuf.st_atime) { /* preserve atime and mtime */ times.actime = sbuf.st_atime; times.modtime = sbuf.st_mtime; utime (file,×); /* set the times */ } } } /* in case INBOX but not tenex format */ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; return ret; /* return what we should */}/* Tenex manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */void *tenex_parameters (long function,void *value){ return NIL;}/* Tenex mail scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents){ if (stream) dummy_scan (NIL,ref,pat,contents);}/* Tenex mail list mailboxes * Accepts: mail stream * reference * pattern to search */void tenex_list (MAILSTREAM *stream,char *ref,char *pat){ if (stream) dummy_list (NIL,ref,pat);}/* Tenex mail list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat){ if (stream) dummy_lsub (NIL,ref,pat);}/* Tenex mail create mailbox * Accepts: MAIL stream * mailbox name to create * Returns: T on success, NIL on failure */long tenex_create (MAILSTREAM *stream,char *mailbox){ char *s,mbx[MAILTMPLEN]; if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s); sprintf (mbx,"Can't create %.80s: invalid name",mailbox); mm_log (mbx,ERROR); return NIL;}/* Tenex mail delete mailbox * Accepts: MAIL stream * mailbox name to delete * Returns: T on success, NIL on failure */long tenex_delete (MAILSTREAM *stream,char *mailbox){ return tenex_rename (stream,mailbox,NIL);}/* Tenex mail rename mailbox * Accepts: MAIL stream * old mailbox name * new mailbox name (or NIL for delete) * Returns: T on success, NIL on failure */long tenex_rename (MAILSTREAM *stream,char *old,char *newname){ long ret = LONGT; char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; int fd,ld; struct stat sbuf; if (!dummy_file (file,old) || (newname && (!((s = mailboxfile (tmp,newname)) && *s) || ((s = strrchr (tmp,'\\')) && !s[1])))) { sprintf (tmp,newname ? "Can't rename mailbox %.80s to %.80s: invalid name" : "Can't delete mailbox %.80s: invalid name", old,newname); mm_log (tmp,ERROR); return NIL; } else if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) { sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); mm_log (tmp,ERROR); return NIL; } /* get exclusive parse/append permission */ if ((ld = lockname (lock,file,LOCK_EX)) < 0) { mm_log ("Unable to lock rename mailbox",ERROR); return NIL; } /* lock out other users */ if (flock (fd,LOCK_EX|LOCK_NB)) { close (fd); /* couldn't lock, give up on it then */ sprintf (tmp,"Mailbox %.80s is in use by another process",old); mm_log (tmp,ERROR); unlockfd (ld,lock); /* release exclusive parse/append permission */ return NIL; } if (newname) { /* want rename? */ /* found superior to destination name? */ if ((s = strrchr (tmp,'\\')) && (s != tmp) && ((tmp[1] != ':') || (s != tmp + 2))) { c = s[1]; /* remember character after delimiter */ *s = s[1] = '\0'; /* tie off name at delimiter */ /* name doesn't exist, create it */ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { *s = '\\'; /* restore delimiter */ if (!dummy_create (stream,tmp)) ret = NIL; } else *s = '\\'; /* restore delimiter */ s[1] = c; /* restore character after delimiter */ } flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ /* rename the file */ if (ret && rename (file,tmp)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } else { flock (fd,LOCK_UN); /* release lock on the file */ close (fd); /* pacify NTFS */ if (unlink (file)) { sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno)); mm_log (tmp,ERROR); ret = NIL; /* set failure */ } } unlockfd (ld,lock); /* release exclusive parse/append permission */ return ret; /* return success */}/* Tenex mail open * Accepts: stream to open * Returns: stream on success, NIL on failure */MAILSTREAM *tenex_open (MAILSTREAM *stream){ int fd,ld; char tmp[MAILTMPLEN]; /* return prototype for OP_PROTOTYPE call */ if (!stream) return &tenexproto; if (stream->local) fatal ("tenex recycle stream"); /* canonicalize the mailbox name */ if (!dummy_file (tmp,stream->mailbox)) { sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); mm_log (tmp,ERROR); } if (stream->rdonly || (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) { if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) { sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno)); mm_log (tmp,ERROR); return NIL; } else if (!stream->rdonly) { /* got it, but readonly */ mm_log ("Can't get write access to mailbox, access is readonly",WARN); stream->rdonly = T; } } stream->local = fs_get (sizeof (TENEXLOCAL)); LOCAL->fd = fd; /* bind the file */ LOCAL->buf = (char *) fs_get (CHUNKSIZE); LOCAL->buflen = CHUNKSIZE - 1; LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE); LOCAL->text.size = CHUNKSIZE - 1; /* note if an INBOX or not */ stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); fs_give ((void **) &stream->mailbox); stream->mailbox = cpystr (tmp); /* get shared parse permission */ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) { mm_log ("Unable to lock open mailbox",ERROR); return NIL; } flock (LOCAL->fd,LOCK_SH); /* lock the file */ unlockfd (ld,tmp); /* release shared parse permission */ LOCAL->filesize = 0; /* initialize parsed file size */ LOCAL->filetime = 0; /* time not set up yet */ LOCAL->mustcheck = LOCAL->shouldcheck = NIL; stream->sequence++; /* bump sequence number */ stream->uid_validity = (unsigned long) time (0); /* parse mailbox */ stream->nmsgs = stream->recent = 0; if (tenex_ping (stream) && !stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL); if (!LOCAL) return NIL; /* failure if stream died */ stream->perm_seen = stream->perm_deleted = stream->perm_flagged = stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T; stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; return stream; /* return stream to caller */}/* Tenex mail close * Accepts: MAIL stream * close options */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -