📄 mh.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: MH mail routines * * Author(s): Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 23 February 1992 * Last Edited: 11 October 2007 */#include <stdio.h>#include <ctype.h>#include <errno.h>extern int errno; /* just in case */#include "mail.h"#include "osdep.h"#include <pwd.h>#include <sys/stat.h>#include <sys/time.h>#include "misc.h"#include "dummy.h"#include "fdstring.h"/* Build parameters */#define MHINBOX "#mhinbox" /* corresponds to namespace in env_unix.c */#define MHINBOXDIR "inbox"#define MHPROFILE ".mh_profile"#define MHCOMMA ','#define MHSEQUENCE ".mh_sequence"#define MHSEQUENCES ".mh_sequences"#define MHPATH "Mail"/* mh_load_message() flags */#define MLM_HEADER 0x1 /* load message text */#define MLM_TEXT 0x2 /* load message text *//* MH I/O stream local data */ typedef struct mh_local { char *dir; /* spool directory name */ unsigned char buf[CHUNKSIZE]; /* temporary buffer */ unsigned long cachedtexts; /* total size of all cached texts */ time_t scantime; /* last time directory scanned */} MHLOCAL;/* Convenient access to local data */#define LOCAL ((MHLOCAL *) stream->local)/* Function prototypes */DRIVER *mh_valid (char *name);int mh_isvalid (char *name,char *tmp,long synonly);int mh_namevalid (char *name);char *mh_path (char *tmp);void *mh_parameters (long function,void *value);long mh_dirfmttest (char *name);void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);void mh_list (MAILSTREAM *stream,char *ref,char *pat);void mh_lsub (MAILSTREAM *stream,char *ref,char *pat);void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level);long mh_subscribe (MAILSTREAM *stream,char *mailbox);long mh_unsubscribe (MAILSTREAM *stream,char *mailbox);long mh_create (MAILSTREAM *stream,char *mailbox);long mh_delete (MAILSTREAM *stream,char *mailbox);long mh_rename (MAILSTREAM *stream,char *old,char *newname);MAILSTREAM *mh_open (MAILSTREAM *stream);void mh_close (MAILSTREAM *stream,long options);void mh_fast (MAILSTREAM *stream,char *sequence,long flags);void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags);char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, long flags);long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);long mh_ping (MAILSTREAM *stream);void mh_check (MAILSTREAM *stream);long mh_expunge (MAILSTREAM *stream,char *sequence,long options);long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox, long options);long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);int mh_select (struct direct *name);int mh_numsort (const void *d1,const void *d2);char *mh_file (char *dst,char *name);long mh_canonicalize (char *pattern,char *ref,char *pat);void mh_setdate (char *file,MESSAGECACHE *elt);/* MH mail routines *//* Driver dispatch used by MAIL */DRIVER mhdriver = { "mh", /* driver name */ /* driver flags */ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE|DR_NOSTICKY|DR_DIRFMT, (DRIVER *) NIL, /* next driver */ mh_valid, /* mailbox is valid for us */ mh_parameters, /* manipulate parameters */ mh_scan, /* scan mailboxes */ mh_list, /* find mailboxes */ mh_lsub, /* find subscribed mailboxes */ mh_subscribe, /* subscribe to mailbox */ mh_unsubscribe, /* unsubscribe from mailbox */ mh_create, /* create mailbox */ mh_delete, /* delete mailbox */ mh_rename, /* rename mailbox */ mail_status_default, /* status of mailbox */ mh_open, /* open mailbox */ mh_close, /* close mailbox */ mh_fast, /* fetch message "fast" attributes */ NIL, /* fetch message flags */ NIL, /* fetch overview */ NIL, /* fetch message envelopes */ mh_header, /* fetch message header */ mh_text, /* fetch message body */ NIL, /* fetch partial message text */ NIL, /* unique identifier */ NIL, /* message number */ NIL, /* modify flags */ NIL, /* per-message modify flags */ NIL, /* search for message based on criteria */ NIL, /* sort messages */ NIL, /* thread messages */ mh_ping, /* ping mailbox to see if still alive */ mh_check, /* check for new messages */ mh_expunge, /* expunge deleted messages */ mh_copy, /* copy messages to another mailbox */ mh_append, /* append string message to mailbox */ NIL /* garbage collect stream */}; /* prototype stream */MAILSTREAM mhproto = {&mhdriver};static char *mh_profile = NIL; /* holds MH profile */static char *mh_pathname = NIL; /* holds MH path name */static long mh_once = 0; /* already snarled once */static long mh_allow_inbox =NIL;/* allow INBOX as well as MHINBOX *//* MH mail validate mailbox * Accepts: mailbox name * Returns: our driver if name is valid, NIL otherwise */DRIVER *mh_valid (char *name){ char tmp[MAILTMPLEN]; return mh_isvalid (name,tmp,T) ? &mhdriver : NIL;}/* MH mail test for valid mailbox * Accepts: mailbox name * temporary buffer to use * syntax only test flag * Returns: T if valid, NIL otherwise */int mh_isvalid (char *name,char *tmp,long synonly){ struct stat sbuf; char *s,*t,altname[MAILTMPLEN]; unsigned long i; int ret = NIL; errno = NIL; /* zap any error condition */ /* mh name? */ if ((mh_allow_inbox && !compare_cstring (name,"INBOX")) || !compare_cstring (name,MHINBOX) || ((name[0] == '#') && ((name[1] == 'm') || (name[1] == 'M')) && ((name[2] == 'h') || (name[2] == 'H')) && (name[3] == '/') && name[4])){ if (mh_path (tmp)) /* validate name if INBOX or not synonly */ ret = (synonly && compare_cstring (name,"INBOX")) ? T : ((stat (mh_file (tmp,name),&sbuf) == 0) && (sbuf.st_mode & S_IFMT) == S_IFDIR); else if (!mh_once++) { /* only report error once */ sprintf (tmp,"%.900s not found, mh format names disabled",mh_profile); mm_log (tmp,WARN); } } /* see if non-NS name within mh hierarchy */ else if ((name[0] != '#') && (s = mh_path (tmp)) && (i = strlen (s)) && (t = mailboxfile (tmp,name)) && !strncmp (t,s,i) && (tmp[i] == '/') && tmp[i+1]) { sprintf (altname,"#mh%.900s",tmp+i); /* can't do synonly here! */ ret = mh_isvalid (altname,tmp,NIL); } else errno = EINVAL; /* bogus name */ return ret;}/* MH mail test for valid mailbox * Accepts: mailbox name * Returns: T if valid, NIL otherwise */int mh_namevalid (char *name){ char *s; if (name[0] == '#' && (name[1] == 'm' || name[1] == 'M') && (name[2] == 'h' || name[2] == 'H') && name[3] == '/') for (s = name; s && *s;) { /* make sure no all-digit nodes */ if (isdigit (*s)) s++; /* digit, check this node further... */ else if (*s == '/') break;/* all digit node, barf */ /* non-digit, skip to next node or return */ else if (!((s = strchr (s+1,'/')) && *++s)) return T; } return NIL; /* all numeric or empty node */}/* Return MH path * Accepts: temporary buffer * Returns: MH path or NIL if MH disabled */char *mh_path (char *tmp){ char *s,*t,*v,*r; int fd; struct stat sbuf; if (!mh_profile) { /* build mh_profile and mh_pathname now */ sprintf (tmp,"%s/%s",myhomedir (),MHPROFILE); if ((fd = open (mh_profile = cpystr (tmp),O_RDONLY,NIL)) >= 0) { fstat (fd,&sbuf); /* yes, get size and read file */ read (fd,(t = (char *) fs_get (sbuf.st_size + 1)),sbuf.st_size); close (fd); /* don't need the file any more */ t[sbuf.st_size] = '\0'; /* tie it off */ /* parse profile file */ for (s = strtok_r (t,"\r\n",&r); s && *s; s = strtok_r (NIL,"\r\n",&r)) { /* found space in line? */ if (v = strpbrk (s," \t")) { *v++ = '\0'; /* tie off, is keyword "Path:"? */ if (!compare_cstring (s,"Path:")) { /* skip whitespace */ while ((*v == ' ') || (*v == '\t')) ++v; /* absolute path? */ if (*v == '/') s = v; else sprintf (s = tmp,"%s/%s",myhomedir (),v); /* copy name */ mh_pathname = cpystr (s); break; /* don't need to look at rest of file */ } } } fs_give ((void **) &t); /* flush profile text */ if (!mh_pathname) { /* default path if not in the profile */ sprintf (tmp,"%s/%s",myhomedir (),MHPATH); mh_pathname = cpystr (tmp); } } } return mh_pathname;}/* MH manipulate driver parameters * Accepts: function code * function-dependent value * Returns: function-dependent return value */void *mh_parameters (long function,void *value){ void *ret = NIL; switch ((int) function) { case GET_INBOXPATH: if (value) ret = mh_file ((char *) value,"INBOX"); break; case GET_DIRFMTTEST: ret = (void *) mh_dirfmttest; break; case SET_MHPROFILE: if (mh_profile) fs_give ((void **) &mh_profile); mh_profile = cpystr ((char *) value); case GET_MHPROFILE: ret = (void *) mh_profile; break; case SET_MHPATH: if (mh_pathname) fs_give ((void **) &mh_pathname); mh_pathname = cpystr ((char *) value); case GET_MHPATH: ret = (void *) mh_pathname; break; case SET_MHALLOWINBOX: mh_allow_inbox = value ? T : NIL; case GET_MHALLOWINBOX: ret = (void *) (mh_allow_inbox ? VOIDT : NIL); } return ret;}/* MH test for directory format internal node * Accepts: candidate node name * Returns: T if internal name, NIL otherwise */long mh_dirfmttest (char *s){ int c; /* sequence(s) file is an internal name */ if (strcmp (s,MHSEQUENCE) && strcmp (s,MHSEQUENCES)) { if (*s == MHCOMMA) ++s; /* else comma + all numeric name */ /* success if all-numeric */ while (c = *s++) if (!isdigit (c)) return NIL; } return LONGT;}/* MH scan mailboxes * Accepts: mail stream * reference * pattern to search * string to scan */void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents){ char *s,test[MAILTMPLEN],file[MAILTMPLEN]; long i = 0; if (!pat || !*pat) { /* empty pattern? */ if (mh_canonicalize (test,ref,"*")) { /* tie off name at root */ if (s = strchr (test,'/')) *++s = '\0'; else test[0] = '\0'; mm_list (stream,'/',test,LATT_NOSELECT); } } /* get canonical form of name */ else if (mh_canonicalize (test,ref,pat)) { if (contents) { /* maybe I'll implement this someday */ mm_log ("Scan not valid for mh mailboxes",ERROR); return; } if (test[3] == '/') { /* looking down levels? */ /* yes, found any wildcards? */ if (s = strpbrk (test,"%*")) { /* yes, copy name up to that point */ strncpy (file,test+4,i = s - (test+4)); file[i] = '\0'; /* tie off */ } else strcpy (file,test+4);/* use just that name then */ /* find directory name */ if (s = strrchr (file,'/')) { *s = '\0'; /* found, tie off at that point */ s = file; } /* do the work */ mh_list_work (stream,s,test,0); } /* always an INBOX */ if (!compare_cstring (test,MHINBOX)) mm_list (stream,NIL,MHINBOX,LATT_NOINFERIORS); }}/* MH list mailboxes * Accepts: mail stream * reference * pattern to search */void mh_list (MAILSTREAM *stream,char *ref,char *pat){ mh_scan (stream,ref,pat,NIL);}/* MH list subscribed mailboxes * Accepts: mail stream * reference * pattern to search */void mh_lsub (MAILSTREAM *stream,char *ref,char *pat){ void *sdb = NIL; char *s,test[MAILTMPLEN]; /* get canonical form of name */ if (mh_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) { do if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL); while (s = sm_read (&sdb)); /* until no more subscriptions */ }}/* MH list mailboxes worker routine * Accepts: mail stream * directory name to search * search pattern * search level */void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level){ DIR *dp; struct direct *d; struct stat sbuf; char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN]; /* build MH name to search */ if (dir) sprintf (name,"#mh/%s/",dir); else strcpy (name,"#mh/"); /* make directory name, punt if bogus */ if (!mh_file (curdir,name)) return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -