home *** CD-ROM | disk | FTP | other *** search
- /* -*-ObjC-*-
- *******************************************************************************
- *
- * File: appnmail.m
- * RCS: /usr/local/sources/CVS/mailapp-utilities/appnmail.m,v 1.18 1998/02/14 17:34:41 tom Exp
- * Description: Append stdin to Mail.app mailbox
- * Author: Carl Edman
- * Created: Fri Mar 12 18:21:23 1993
- * Modified: Fri Jan 2 14:57:58 1998 Tom Hageman <tom@basil.icce.rug.nl>
- * Language: Objective C
- * Package: mailapp-utilities
- * Status: Exp.
- *
- * (C) Copyright 1993, but otherwise this file is perfect freeware.
- *
- *******************************************************************************
- */
-
- #import <libc.h>
- #import <errno.h>
- //#import <stdlib.h>
- //#import <stdio.h>
- //#import <string.h>
- //#import <stdarg.h>
- #import <ctype.h>
- #import <regex.h>
- //#import <sys/file.h>
- //#import <sys/param.h>
- //#import <sys/types.h>
- //#import <sys/stat.h>
- #import "compat.h"
- #import "MailProxy.h"
- #import "mailutil.h"
- #define _MAILTOC_DEFINES 1
- #import "mailtoc.h"
- #import "optutil.h"
- #import "iso_convert.h"
-
- #define USAGE "\
- Usage: %s [-nvi][-r|-f|-d|][-p pri][-H|-V] mbox...\n"
-
- #define HELP "\
- -n skip mailbox if locked\n\
- -v talkative mode\n\
- -i force incorporation of pending messages into mailbox\n\
- -r mark message as read\n\
- -f mark message as flagged\n\
- -d mark message as deleted\n\
- -p pri set message's priority level to `pri'\n\
- -V show version\n\
- -H this help\n\
- "
-
- #define APPNMAIL_LOCK ".appnmail.lock"
- #define APPNMAIL_MBOX "appnmail_mbox"
- #define APPNMAIL_TOC "appnmail_table_of_contents"
- #define APPNMAIL_DEQUEUE_LOCK ".appnmail_dequeue.lock"
-
- #define PGP_SIGNED "-----BEGIN PGP SIGNED MESSAGE-----"
- #define PGP_ENCRYPTED "-----BEGIN PGP MESSAGE-----"
-
- char *header=0,*content=0;
- int headerlen=0,contentlen=0;
- int headermaxlen=0,contentmaxlen=0;
- char line[LINELEN];
-
- int verboseflg=0;
-
- struct regex *mailutilre=0;
- const char *decodeCmd=0;
- const char*uncompressCmd=0;
- const char *tarCmd=0;
-
-
- struct message_index *readmail(void)
- {
- struct message_index *mi;
- int i,pri=0;
- char from[LINELEN]="",subject[LINELEN]="",reference[LINELEN]="";
- struct regex *fre=re_compile("^From: *\\(.*\\)\n",0);
- struct regex *sre=re_compile("^Subject: *\\(.*\\)\n",0);
- // Y2000 fix applied here.
- struct regex *dre=re_compile("^Date:.*\\([ 0123][0-9]\\) \\([A-Z][a-z][a-z]\\)[a-z]* \\([12]*[0-9]*[0-9][0-9]\\) ",0);
- struct regex *rre=re_compile("^Next-Attachment: \\.tar\\.\\([0-9]*\\)\\.\\(.*\\)\\.attach, \\(E*,* *\\)\\([0-9]*\\), \\([0-9]*/[0-9]*\\), \\([0-9]*\\), \\([0-9]*\\)",0);
- struct regex *mre=re_compile("^Content-Type: \\([-_a-zA-Z0-9]*/[-_a-zA-Z0-9]*\\)",0);
- struct regex *pre=0;
- const char *pridef=0;
-
- if ((pridef=NXGetDefaultValue(MAIL_APP,"PriorityHeader")) != NULL)
- {
- static char buf[MAXPATHLEN];
- sprintf(buf,"^%s: [ \t]*\\([^ \t]*\\)[ \t]*\n",pridef);
- pre=re_compile(buf,0);
- pridef=NXGetDefaultValue(MAIL_APP,"PriorityValues");
- }
-
- mi = malloc(sizeof(*mi));
-
- mi->record_length = sizeof(*mi);
- mi->mes_offset = -1;
- mi->status = MT_STATUS_NEW;
- mi->msgtype = MT_UNSET;
- mi->encrypted = MT_UNSET;
- mi->sync = MT_UNSET;
- mi->mes_date = message_current_date();
-
- headerlen=0;
-
- while(fgets(line,LINELEN,stdin))
- {
- if (*line=='\n') break;
-
- /* XXX Should handle line-continuations. */
- if (re_match(line,fre)==1)
- {
- strpcpy(from,fre->braslist[0],fre->braelist[0]);
- iso_convert(from);
- }
- if (re_match(line,sre)==1)
- {
- strpcpy(subject,sre->braslist[0],sre->braelist[0]);
- iso_convert(subject);
- }
- if (re_match(line,dre)==1)
- {
- // Y2000 fix applied here.
- unsigned year = atoi(dre->braslist[2]);
- if (year < 70) year += 2000;
- else if (year < 200) year += 1900;
- // else assume year includes century...
- mi->mes_date=message_date(year,dre->braslist[1],atoi(dre->braslist[0]));
- }
- if (pre && pridef && (re_match(line,pre)==1))
- {
- const char *beg,*end;
-
- for(beg=pridef,pri=1;*beg;(beg=*end ? end+1 : end),(pri++))
- {
- if (!(end=index(beg,' '))) end=beg+strlen(beg);
- if (((pre->braelist[0]-pre->braslist[0])==(end-beg))
- &&(strncmp(pre->braslist[0],beg,end-beg)==0))
- break;
- }
- if (!*beg) pri=0;
- }
- if (re_match(line, mre) == 1)
- {
- if ((mi->msgtype == MT_UNSET) &&
- !strpcaseequ("text/plain",mre->braslist[0],mre->braelist[0]))
- mi->msgtype = MT_TYPE_MIME;
- // Experimental RFC2015 encrypt/signature detection.
- if (strpcaseequ("multipart/encrypted",mre->braslist[0],mre->braelist[0]))
- mi->encrypted = MT_ENCRYPTED;
- else if (strpcaseequ("multipart/signed",mre->braslist[0],mre->braelist[0]))
- mi->encrypted = 's'; // Naughty, naughty...
- }
- if (re_match(line,rre)==1)
- {
- char *c;
- mi->msgtype = MT_TYPE_NEXT;
- strpcpy(reference,rre->braslist[1],rre->braelist[1]);
- c=reference+strlen(reference);
- *c++='_';
- while(c-reference<22) *c++='_';
- *c='\0';
- sprintf(c,"%lu",(unsigned long)time(0));
- strcat(reference,".attach");
- c=reference+strlen(reference);
- strcat(reference,", ");
- if (*(rre->braslist[2])=='E')
- {
- strcat(reference,"E, ");
- mi->encrypted = MT_ENCRYPTED;
- }
- strpcat(reference,rre->braslist[4],rre->braelist[4]);
- strcat(reference,"\n");
- appstring(&header,&headerlen,&headermaxlen,"Next-Reference: ",
- strlen("Next-Reference: "));
- appstring(&header,&headerlen,&headermaxlen,reference,strlen(reference));
- *c='\0';
- }
- else
- {
- appstring(&header,&headerlen,&headermaxlen,line,strlen(line));
- }
- }
- appstring(&header,&headerlen,&headermaxlen,"\n",1);
-
- contentlen=0;
-
- while((i=fread(growstring(&content,&contentlen,&contentmaxlen,LINELEN*16),
- 1,16*LINELEN,stdin))>0)
- contentlen+=i;
- if (content[contentlen-1]!='\n')
- appstring(&content,&contentlen,&contentmaxlen,"\n",1);
-
- mi->mes_length=headerlen;
- if (mi->msgtype != MT_TYPE_NEXT) mi->mes_length+=contentlen;
- mi->record_length+=strlen(from)+1+strlen(subject)+1+strlen(reference)+1;
- mi->record_length+=sizeof(int)+sizeof(time_t)+sizeof(int);
- mi=realloc(mi,mi->record_length);
- strcpy(mi->data,from);
- strcpy(mi->data+strlen(from)+1,subject);
- strcpy(mi->data+strlen(from)+1+strlen(subject)+1,reference);
-
- message_set_attachsize(mi,0);
- message_set_attachtime(mi,0);
- message_set_priority(mi,pri);
-
- // Experimental PGP encrypt/signature detection.
- if (mi->encrypted == MT_UNSET)
- {
- if (contentlen >= sizeof(PGP_ENCRYPTED)-1 &&
- strncmp(content, PGP_ENCRYPTED, sizeof(PGP_ENCRYPTED)-1)==0)
- mi->encrypted = MT_ENCRYPTED;
- else if (contentlen >= sizeof(PGP_SIGNED)-1 &&
- strncmp(content, PGP_SIGNED, sizeof(PGP_SIGNED)-1)==0)
- mi->encrypted = 's'; // XXX Naughty, naughty...
- }
-
- free(fre); free(sre); free(rre); free(dre);
- if (pre) free(pre);
- return mi;
- }
-
- const char *get_command(const char *defaultKey, ...)
- {
- /* Find command named `defaultKey' in Mail's defaults database, and check
- if it is an executable. If not, try each of the defaults given on
- the (NULL-terminated) argument list in turn. */
- const char *command = NXGetDefaultValue(MAIL_APP, defaultKey);
- va_list ap;
-
- va_start(ap, defaultKey);
- do
- {
- struct stat st;
- if (command == NULL) continue;
- if (command[0] != '/') break; /* XXX should check PATH? */
- if (access(command, X_OK) < 0) continue;
- if (stat(command, &st) < 0) continue;
- if ((st.st_mode & S_IFMT) == S_IFREG) break;
- }
- while ((command = va_arg(ap, const char *)) != NULL);
- va_end(ap);
- if (command == NULL)
- {
- fprintf(stderr,"%s: cannot find executable for `%s'.\n", progname(), defaultKey);
- }
- return command;
- }
-
- #define INCOMING_MBOX "Incoming_Mail"
- #define INCOMING_TOC "Incoming_Table_of_Contents"
-
- int incorporate_mail(MailProxy *mailProxy, const char *mboxname, const char *mbox, const char *table_of_contents)
- {
- int interval=1,lastinterval=0;
-
- /* Signal Mail.app to incorporate mail. Assumes Mail the mailbox open,
- and approporiate locks are in place. */
- if (mailProxy == nil) return -1;
- if (link(mbox, INCOMING_MBOX)<0)
- {
- fprintf(stderr,"%s: %s: cannot link %s to %s (%s)\n",progname(),mboxname,mbox,INCOMING_MBOX,strerror(errno));
- return -1;
- }
- if (link(table_of_contents, INCOMING_TOC)<0)
- {
- fprintf(stderr,"%s: %s: cannot link %s to %s (%s)\n",progname(),mboxname,mbox,INCOMING_MBOX,strerror(errno));
- unlink(INCOMING_MBOX);
- return -1;
- }
- if (![mailProxy incorporateNewMail])
- {
- fprintf(stderr,"%s: %s: -incorporateNewMail failed\n",progname(),mboxname);
- unlink(INCOMING_MBOX);
- unlink(INCOMING_TOC);
- return 1;
- }
-
- /* Unfortunately this seems to be asynchronous, ie. Mail.app does not
- immediately process (and remove) the Incoming mbox when it receives
- a remote kick in the butt. So we have to wait a little while... */
- while (access(INCOMING_MBOX, F_OK)==0 || access(INCOMING_TOC, F_OK)==0)
- {
- /* Nice Fibonacci series. */
- int newinterval=interval+lastinterval;
-
- lastinterval=interval;
- interval=newinterval;
- if (interval > 34)
- {
- /* so we waited for 1+1+2+3+5+8+13+21+34 = 88 seconds. Is this enough? */
- fprintf(stderr,"%s: %s: Mail.app failed to incorporate Incoming mail???\n",progname(),mboxname);
- unlink(INCOMING_MBOX);
- unlink(INCOMING_TOC);
- return 1;
- }
- sleep(interval);
- }
- unlink(mbox);
- unlink(table_of_contents);
- return 0;
- }
-
- /* Transfer messages from one {mbox, table_of_contents} combo to another. */
- int transfer_mbox_contents(const char *mboxname, const char *srcmbox, const char *srctoc, const char *dstmbox, const char *dsttoc)
- {
- int result=-1;
- int srcboxfd=-1, dstboxfd=-1;
- FILE *srctocf=NULL, *dsttocf=NULL;
- struct table_of_contents_header *srctoch=NULL, *dsttoch=NULL;
- struct message_index *mi;
- time_t mboxtime;
- int n;
-
- mboxtime = mtime(srcmbox);
- if ((srcboxfd = open(srcmbox, O_RDONLY)) < 0)
- {
- fprintf(stderr,"%s: %s: opening %s (%s)\n", progname(), mboxname, srcmbox, strerror(errno));
- goto _close;
- }
- if ((srctocf = fopen(srctoc, "r+b")) == NULL)
- {
- fprintf(stderr,"%s: %s: opening %s (%s)\n", progname(), mboxname, srctoc, strerror(errno));
- goto _close;
- }
- if ((srctoch = get_table_of_contents_header(srctocf, 0)) == NULL)
- {
- fprintf(stderr,"%s: %s: [%s] invalid header\n", progname(), mboxname, srctoc);
- goto _close;
- }
- if (srctoch->mbox_time != mboxtime)
- {
- fprintf(stderr,"%s: %s: [%s] out of sync\n", progname(), mboxname, srctoc);
- goto _close;
- }
-
- mboxtime = mtime(dstmbox);
- if ((dstboxfd = open(dstmbox,O_WRONLY)) < 0)
- {
- fprintf(stderr,"%s: %s: opening %s (%s)\n", progname(), mboxname, dstmbox, strerror(errno));
- goto _close;
- }
- if ((dsttocf = fopen(dsttoc,"r+b")) == NULL)
- {
- fprintf(stderr,"%s: %s: opening %s (%s)\n", progname(), mboxname, dsttoc, strerror(errno));
- goto _close;
- }
- if ((dsttoch = get_table_of_contents_header(dsttocf,1)) == NULL)
- {
- fprintf(stderr,"%s: %s: [%s] invalid header\n", progname(), mboxname, dsttoc);
- goto _close;
- }
- if (dsttoch->mbox_time != mboxtime)
- {
- fprintf(stderr,"%s: %s: [%s] out of sync\n", progname(), mboxname, dsttoc);
- goto _close;
- }
-
- for (n = 0; n < srctoch->num_msgs; free(mi), n++)
- {
- long src_offset, src_length;
- void *buf=NULL;
-
- errno = 0;
- if ((mi = get_message_index(srctocf)) == NULL)
- {
- fprintf(stderr,"%s: %s: [%s] inconsistency (%s)\n", progname(), mboxname, srctoc, (errno ? strerror(errno) : "invalid"));
- goto _close;
- }
- if (mi->status == 'D') continue; // Deleted in a previous pass.
-
- src_offset = mi->mes_offset;
- src_length = mi->mes_length;
- if (lseek(srcboxfd, src_offset, SEEK_SET) != src_offset ||
- (buf = malloc(src_length)) == NULL ||
- (src_length = read(srcboxfd, buf, src_length)) != mi->mes_length)
- {
- fprintf(stderr,"%s: %s: [%s] inconsistency (%s)\n", progname(), mboxname, srcmbox, (errno ? strerror(errno) : "invalid"));
- free(buf);
- free(mi);
- goto _close;
- }
- mi->mes_offset = lseek(dstboxfd, 0, SEEK_END);
- if ((mi->mes_length = write(dstboxfd, buf, src_length)) != src_length)
- {
- fprintf(stderr,"%s: %s: [%s] inconsistency (%s)\n", progname(), mboxname, dstmbox, (errno ? strerror(errno) : "invalid"));
- }
- free(buf);
-
- /* Append message index to destination table of contents. */
- fseek(dsttocf, 0, SEEK_END);
- put_message_index(dsttocf, mi);
-
- /* Update header. */
- dsttoch->num_msgs++;
- dsttoch->mbox_time = mtime(dstmbox);
- fseek(dsttocf, 0, SEEK_SET);
- put_table_of_contents_header(dsttocf, dsttoch);
-
- /* Mark message as deleted (and restore original offset/length) in source mbox. */
- mi->status = 'D';
- mi->mes_offset = src_offset;
- mi->mes_length = src_length;
- fseek(srctocf, -mi->record_length, SEEK_CUR);
- put_message_index(srctocf, mi);
- fseek(srctocf, 0, SEEK_CUR);
- }
-
- /* Update header once again, just to be sure... */
- close(dstboxfd), dstboxfd = -1;
- dsttoch->mbox_time = mtime(dstmbox);
- fseek(dsttocf, 0, SEEK_SET);
- put_table_of_contents_header(dsttocf, dsttoch);
-
- result = 0;
- _close:
- free(dsttoch);
- if (dstboxfd >= 0) close(dstboxfd);
- if (dsttocf != NULL) fclose(dsttocf);
- free(srctoch);
- if (srcboxfd >= 0) close(srcboxfd);
- if (srctocf != NULL) fclose(srctocf);
-
- return result;
- }
-
- /* Dequeue mail by copying it from appnmail's private mbox to the real mbox.
- assumes proper locking has been done. */
- void dequeue_mail(const char *mboxname)
- {
- if (access(APPNMAIL_MBOX, F_OK) != 0) return;
- if (transfer_mbox_contents(mboxname, APPNMAIL_MBOX, APPNMAIL_TOC, "mbox", "table_of_contents") != 0) return;
-
- /* If successful, remove our private inbox. */
- unlink(APPNMAIL_MBOX);
- unlink(APPNMAIL_TOC);
- }
-
- /* Extract NeXTmail attachment from message into `path'. */
- int extract_attachment(const char *mboxname, const char *path)
- {
- FILE *f;
- int st;
-
- if (verboseflg>1) fprintf(stderr,"%s[%d] Extracting NeXTmail attachment...\n",progname(),getpid());
-
- /* Locate executables, if not already done so. */
- if (!decodeCmd && !(decodeCmd=get_command("appnmailDecodeCommand","/NextApps/Mail.app/decode",NULL)))
- {
- /* ...not really a regular Mail default. */
- return EXIT_FAILURE;
- }
- if (!uncompressCmd && !(uncompressCmd=get_command("UncompressCommand","/usr/bin/gunzip","/usr/ucb/uncompress",NULL)))
- {
- return EXIT_FAILURE;
- }
- if (!tarCmd && !(tarCmd=get_command("TarCommand","/NextApps/Mail.app/safetar","/usr/bin/gnutar",NULL)))
- {
- return EXIT_FAILURE;
- }
- if ((mkdir(path,0755))==-1)
- {
- fprintf(stderr,"%s: %s: %s: %s\n",progname(),mboxname,path,strerror(errno));
- return EXIT_FAILURE;
- }
- sprintf(line,"cd %s && %s | %s | %s xf -",path,decodeCmd,uncompressCmd,tarCmd);
- if (!(f=popen(line,"w")))
- {
- fprintf(stderr,"%s: %s: %s: %s\n",progname(),mboxname,path,strerror(errno));
- return EXIT_FAILURE;
- }
- fwrite(content,contentlen,1,f);
- if ((st=pclose(f))!=0)
- {
- fprintf(stderr,"%s: %s: %s failed (status %d)\n",progname(),mboxname,line,st);
- return EXIT_FAILURE;
- }
- return EXIT_SUCCESS;
- }
-
- /* Save message in the mailbox, or, if it is locked, in a (temporary) private
- mailbox inside the .mbox folder. */
- int save_message(const char *mboxname, struct message_index *mi)
- {
- time_t mboxtime;
- int mfd=-1,tocfd=-1;
- FILE *tocf=NULL;
- struct table_of_contents_header *toch=0;
- char host[MAXHOSTNAMELEN];
- char *mbox="mbox", *table_of_contents="table_of_contents";
- const char *attachment;
- int ownslock=1,ownsappnmaillock=0;
- int st;
- int result=EXIT_SUCCESS;
-
- if (verboseflg) fprintf(stderr,"%s[%d] Entering %s to deliver...\n",progname(),getpid(),mboxname);
-
- if (cd_mbox(mboxname,1) != 0) return EXIT_FAILURE;
-
- if ((st = try_lock_mbox(host, line, NULL)) != 0)
- {
- if (st < 0) return EXIT_FAILURE;
-
- /* If the mbox is locked (by Mail.app), we'll write the message to
- a temporary mbox, and try to incorporate it into the mbox later.
- (so the message is at least stored to disk in case anything
- goes wrong).
- XXX The way to determine whether the lock is owned by Mail.app
- or one of our own mailapp-utilities is completely bogus
- and depends on what is written in the user field of the lock
- by try_lock_mbox(). */
- if (!mailutilre) mailutilre=re_compile(LOCK_USER_RE,0);
- if (*line && !re_match(line,mailutilre))
- {
- if (verboseflg>1) fprintf(stderr,"%s[%d] Delivering into temporary mbox...\n",progname(),getpid());
- ownslock=0;
- mbox=APPNMAIL_MBOX;
- table_of_contents=APPNMAIL_TOC;
- /* Operations on our temp. mbox are protected with our own lock. */
- /* XXX What about stale locks? */
- if (lock_mbox_file(APPNMAIL_LOCK,0)!=0)
- {
- result=EXIT_FAILURE;
- goto unlock;
- }
- ownsappnmaillock=1;
- }
- else if (lock_mbox(0)!=0)
- {
- result=EXIT_FAILURE;
- goto cleanup;
- }
- }
- if (ownslock)
- {
- /* try to dequeue mail first, but don't insist on it if already
- locked by another appnmail. */
- if (lock_mbox_file(APPNMAIL_LOCK,1)==0)
- {
- dequeue_mail(mboxname);
- unlock_mbox_file(APPNMAIL_LOCK);
- }
- }
-
- mboxtime=mtime(mbox);
-
- if ((mfd=open(mbox,O_WRONLY|O_APPEND|O_CREAT,0644))==-1)
- {
- fprintf(stderr,"%s: %s: %s\n",progname(),mboxname,strerror(errno));
- result=EXIT_FAILURE;
- goto unlock;
- }
-
- /* Even if we cannot update the table of contents,
- we'll try our damnedest to deliver the message anyway. */
-
- if ((tocfd=open(table_of_contents,O_CREAT|O_RDWR,0644))==-1 ||
- (tocf=fdopen(tocfd,"r+b"))==NULL)
- {
- fprintf(stderr,"%s: %s: %s\n",progname(),mboxname,strerror(errno));
- result=EXIT_FAILURE;
- }
- else
- {
- toch=get_table_of_contents_header(tocf,1);
- if (!toch)
- {
- fprintf(stderr,"%s: %s: warning: invalid table_of_contents\n",progname(),mboxname);
- result=EXIT_FAILURE;
- }
- else if (toch->mbox_time!=mboxtime)
- {
- fprintf(stderr,"%s: %s: warning: table_of_contents out of sync\n",progname(),mboxname);
- result=EXIT_FAILURE;
- }
- }
-
- /* FOR EACH */
- lseek(mfd,0,SEEK_END);
- mi->mes_offset=lseek(mfd,0,SEEK_CUR);
-
- if ((attachment = message_reference(mi)) && *attachment)
- {
- char path[MAXPATHLEN+1];
-
- if (extract_attachment(mboxname, attachment) != EXIT_SUCCESS)
- {
- result=EXIT_FAILURE;
- goto unlock;
- }
- message_set_attachsize(mi, dirsize(strcpy(path, attachment)));
- message_set_attachtime(mi, mtime(attachment));
- /* Add message body *after* all failure points, to avoid TOC corruption. */
- if (write(mfd,header,headerlen) != headerlen ||
- write(mfd,"\n",1) != 1)
- {
- fprintf(stderr, "%s: %s: truncated message\n",progname(),mboxname);
- result=EXIT_FAILURE;
- goto unlock;
- }
- }
- else
- {
- if (write(mfd,header,headerlen) != headerlen ||
- write(mfd,content,contentlen) != contentlen)
- {
- fprintf(stderr, "%s: %s: truncated message\n",progname(),mboxname);
- result=EXIT_FAILURE;
- goto unlock;
- }
- }
- if (toch)
- {
- toch->num_msgs++;
- fseek(tocf,0,SEEK_END);
- if (put_message_index(tocf,mi) != 0)
- {
- fprintf(stderr, "%s: %s: warning: truncated message index\n",progname(),mboxname);
- result=EXIT_FAILURE;
- goto unlock;
- }
- }
- /* END FOR EACH */
-
- close(mfd), mfd=-1;
- if (toch)
- {
- if (toch->mbox_time==mboxtime) toch->mbox_time=mtime(mbox);
- fseek(tocf,0,SEEK_SET);
- put_table_of_contents_header(tocf,toch);
- }
- unlock:
- if (tocf!=NULL) fclose(tocf), tocf=NULL;
- else if (tocfd>=0) close(tocfd), tocfd=-1;
- if (mfd>=0) close(mfd), mfd=-1;
- if (ownslock)
- {
- unlock_mbox();
- }
- else if (ownsappnmaillock)
- {
- unlock_mbox_file(APPNMAIL_LOCK);
- }
- cleanup:
- if (result==EXIT_SUCCESS && verboseflg) fprintf(stderr,"%s[%d] ...deliver %s done\n",progname(),getpid(),mboxname);
- if (toch) free(toch);
- uncd_mbox();
- return result;
- }
-
- /* Incorporate mail from appnmail's private mbox into the real mbox.
- Handles synchronization with other instances of appnmail, proper locking,
- etc. */
- void incorporate(const char *mboxname, int force)
- {
- int st;
- char host[MAXHOSTNAMELEN];
-
- if (cd_mbox(mboxname, 0) != 0) return;
- if (access(APPNMAIL_MBOX, F_OK) == 0)
- {
- /* Make sure that only a single appnmail is waiting to dequeue. */
- if (lock_mbox_file(APPNMAIL_DEQUEUE_LOCK, 1) == 0)
- {
- int unlocked = 0;
-
- if (verboseflg) fprintf(stderr,"%s[%d] Entering %s to dequeue...\n",progname(),getpid(),mboxname);
- if ((st = try_lock_mbox(host, line, NULL)) > 0)
- {
- if (!mailutilre) mailutilre = re_compile(LOCK_USER_RE,0);
- if (*line && !re_match(line, mailutilre))
- {
- /* XXX This does not work in Rhapsody, or if compiled in OPENSTEP */
- /* Not locked by appnmail; try to contact Mail.app */
- int incorporateDelay = 0;
- const char *delaydef = NXGetDefaultValue(MAIL_APP, "appnmailIncorporateDelay");
-
- if (delaydef && isdigit(*delaydef)) incorporateDelay = atoi(delaydef);
-
- /* Wait some time (&& recheck lock) */
- if (incorporateDelay != 0)
- {
- if (verboseflg>2) fprintf(stderr,"%s[%d] Sleeping for appnmailIncorporateDelay = %d seconds...\n",progname(),getpid(),incorporateDelay);
- sleep(incorporateDelay);
- st = try_lock_mbox(host, line, NULL);
- }
-
- if (st > 0 && *line && !re_match(line, mailutilre) &&
- [MailProxy canIncorporateNewMail])
- {
- if (verboseflg>2) fprintf(stderr,"%s[%d] Locking %s...\n",progname(),getpid(),APPNMAIL_LOCK);
- if ((st = lock_mbox_file(APPNMAIL_LOCK, 0)) == 0)
- {
- /* XXX Should re-check .lock host? */
- st = -1;
- if (access(APPNMAIL_MBOX, F_OK) == 0)
- {
- MailProxy *mailProxy;
-
- if (verboseflg>2) fprintf(stderr,"%s[%d] Locating Mail port on host %s...\n",progname(),getpid(), host);
- mailProxy = [[MailProxy alloc] initWithMailer:NULL host:host forceLaunch:NO];
- if ([mailProxy isConnected])
- {
- /* Unlock dequeue_lock now, to avoid race conditions.
- XXX should analyze this more thoroughly... */
- unlock_mbox_file(APPNMAIL_DEQUEUE_LOCK);
- unlocked++;
- if (verboseflg>1) fprintf(stderr,"%s[%d] Incorporating mail in open mailbox...\n",progname(),getpid());
- if (incorporate_mail(mailProxy, mboxname, APPNMAIL_MBOX, APPNMAIL_TOC) != 0)
- {
- /* Retry using conventional mbox lock, but only
- if no other appnmails are dequeueing. */
- if (lock_mbox_file(APPNMAIL_DEQUEUE_LOCK, 1) == 0)
- {
- unlocked = 0;
- st = 1;
- }
- else
- {
- st = -1;
- }
- }
- [mailProxy release];
- }
- }
- unlock_mbox_file(APPNMAIL_LOCK);
- }
- }
- }
- if (st > 0) st = lock_mbox(0);
- }
-
- if (st == 0)
- {
- if (verboseflg>2) fprintf(stderr,"%s[%d] Locking %s...\n",progname(),getpid(),APPNMAIL_LOCK);
- if ((st = lock_mbox_file(APPNMAIL_LOCK, 0)) == 0)
- {
- /* Unlock dequeue_lock now, to avoid race conditions. */
- unlock_mbox_file(APPNMAIL_DEQUEUE_LOCK);
- unlocked++;
-
- if (access(APPNMAIL_MBOX, F_OK) == 0)
- {
- if (verboseflg>1) fprintf(stderr,"%s[%d] Appending mail to mailbox...\n",progname(),getpid());
- dequeue_mail(mboxname);
- }
- unlock_mbox_file(APPNMAIL_LOCK);
- }
- unlock_mbox();
- }
- if (!unlocked)
- {
- unlock_mbox_file(APPNMAIL_DEQUEUE_LOCK);
- }
- if (verboseflg) fprintf(stderr,"%s[%d] ...dequeue %s done\n",progname(),getpid(),mboxname);
- }
- }
- uncd_mbox();
- }
-
-
- int main(int ac,char *av[])
- {
- struct message_index *mi;
- int c,ai;
- int readflg=0,nowaitflg=0,multiflg=0,flagflg=0,deleteflg=0,incorporateflg=0;
- int pri=-1;
- int status=EXIT_SUCCESS;
- POOL_INIT
-
- set_progname(av[0]);
-
- while((c=getopt(ac,av,"rfdvnp:iVH"))!=EOF) switch(c)
- {
- case 'r':
- readflg++;
- if (flagflg||deleteflg) status=EXIT_USAGE;
- break;
- case 'f':
- flagflg++;
- if (readflg||deleteflg) status=EXIT_USAGE;
- break;
- case 'd':
- deleteflg++;
- if (readflg||flagflg) status=EXIT_USAGE;
- break;
- case 'n':
- nowaitflg++;
- break;
- case 'v':
- verboseflg++;
- break;
- case 'm': // XXX currently unused.
- multiflg++;
- break;
- case 'p':
- if (pri!=-1) status=EXIT_USAGE;
- pri=atoi(optarg);
- if (pri<0) status=EXIT_USAGE;
- break;
- case 'i':
- incorporateflg++;
- break;
- case 'V':
- status=EXIT_VERSION;
- break;
- case 'H':
- status=EXIT_HELP;
- break;
- case '?':
- default:
- status=EXIT_USAGE;
- }
- if (status==EXIT_SUCCESS && optind>=ac) status=EXIT_USAGE;
- handle_usage_help_version(status, USAGE, HELP);
-
- /* This is to avoid problems with effective vs. real user ids. */
- setuid(geteuid());
-
- /* Mail message is optional if -i flag is given, so avoid reading stdin
- in interactive use, and allow empty message if redirected. */
- if (!incorporateflg || !isatty(fileno(stdin)))
- {
- mi=readmail();
-
- if (!(incorporateflg && headerlen == 0 && contentlen == 0))
- {
- if (!header || (headerlen<5) || strncmp(header,"From ",5))
- {
- fprintf(stderr,"%s: message is not in UNIX mailbox format\n", progname());
- exit(EXIT_FAILURE);
- }
-
- if (readflg) mi->status = MT_STATUS_READ;
- else if (flagflg) mi->status = MT_STATUS_FLAGGED;
- else if (deleteflg) mi->status = MT_STATUS_DELETED;
-
- if (pri>=0) message_set_priority(mi,pri);
-
- for (ai=optind; ai<ac; ai++)
- {
- c = save_message(av[ai], mi);
- if (c != EXIT_SUCCESS) status = c;
- }
- }
- }
-
- if (incorporateflg || (status == EXIT_SUCCESS && !nowaitflg))
- {
- /* Dequeue pass. */
- for (ai = optind; ai < ac; ai++)
- {
- incorporate(av[ai], incorporateflg);
- }
- }
- POOL_RELEASE
- return status;
- }
-