home *** CD-ROM | disk | FTP | other *** search
- /* -*-C-*-
- *******************************************************************************
- *
- * File: appnmail.m
- * RCS: /usr/local/sources/CVS/mailapp-utilities/appnmail.m,v 1.4 1997/04/19 17:37:18 tom Exp
- * Description: Append stdin to Mail.app mailbox
- * Author: Carl Edman
- * Created: Fri Mar 12 18:21:23 1993
- * Modified: Sat Nov 30 23:28:19 1996 Tom Hageman <tom@basil.icce.rug.nl>
- * Language: Objective C
- * Package: mailapp-utilities
- * Status: First release.
- *
- * (C) Copyright 1993, but otherwise this file is perfect freeware.
- *
- *******************************************************************************
- */
-
- //#import <AppKit/AppKit.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <errno.h>
- #import <stdlib.h>
- #import <unistd.h>
- #import <stdio.h>
- #import <string.h>
- #import <strings.h>
- #import <sys/file.h>
- #import <sys/param.h>
- #import <sys/types.h>
- #import <sys/stat.h>
- #import <varargs.h>
- //#import <defaults/defaults.h>
- #import "mailutil.h"
- #import "mailtoc.h"
- #import "optutil.h"
- #import "regex.h"
-
- #define USAGE "\
- Usage: %s [-nv][-r|-f|-d|][-p pri][-H|-V] mbox...\n"
-
- #define HELP "\
- -n skip mailbox if locked\n\
- -v talkative mode\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\
- "
-
- char *header=0,*content=0;
- int headerlen=0,contentlen=0;
- int headermaxlen=0,contentmaxlen=0;
- char line[LINELEN];
-
- /* ISO header conversion cruft. */
- #import "iso2next.h"
- /* Tables in there are expected to be in the "recode --header" format. */
- /* XXX maybe we'd better use "recode --header --strict"? */
-
- int iso_convert(char *line)
- {
- static const struct
- {
- const char *name;
- const unsigned char *contents;
- }
- table[] = {
- /* The contents of these tables are defined in "iso2next.h".
- If you add one there, don't forget to add it here too. */
- { "iso-8859-1", latin1_to_next },
- { "iso-8859-2", latin2_to_next },
- };
- static struct regex *isore = 0;
- const unsigned char *tt = 0;
- const char *name;
- int i, namelen;
-
- if (!isore) isore = re_compile("=?\\([^?]*\\)?Q?",1);
- if (re_match(line, isore) == 0) return 0;
- name = isore->braslist[0];
- namelen = (isore->braelist[0] - isore->braslist[0]);
- for (i = 0; i < sizeof(table)/sizeof(table[0]); i++)
- {
- if (strncasecmp(table[i].name, name, namelen) == 0 &&
- table[i].name[namelen] == 0)
- {
- tt = table[i].contents;
- break;
- }
- }
- if (tt != 0)
- {
- int errors = 0;
- // hack to translate iso-stuff, in-place.
- char *s = (char *)isore->end, *t;
-
- for (; (t = strchr(s, '=')) != 0; s = t)
- {
- if (t > s && t[-1] == '?')
- {
- // ?= is end quote.
- if (errors > 0)
- {
- fprintf(stderr, "%s: warning: MIME 8-bit header encoding `%.*s' incomplete conversion\n", progname(), namelen, name);
- return -1;
- }
- strcpy(t-1, t+1);
- strcpy((char *)isore->start, isore->end);
- return 1;
- }
- else
- {
- // parse "=XX" where XX is a 2-digit hex number
- unsigned c = 0;
-
- sscanf(t+1, "%2x", &c);
- if (c < 256 && tt[c] != 0)
- {
- *t = tt[c];
- strcpy(t+1, t+3);
- }
- else
- ++errors;
- ++t;
- }
- }
- fprintf(stderr, "%s: warning: Missing terminating `?=' in MIME 8-bit header `%s'\n", progname(), line);
- return 0;
- }
- fprintf(stderr, "%s: warning: MIME 8-bit header encoding `%.*s' is unsupported\n", progname(), namelen, name);
- return 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);
- struct regex *dre=re_compile("^Date: .*\\([ 0123][0-9]\\) \\([A-Z][a-z][a-z][a-z]*\\) 1*9*\\([789][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-Z]*/[a-zA-Z]*\\)",0);
- struct regex *pre=0;
- const char *pridef=0;
- /*
- if (pridef=NXGetDefaultValue("Mail","PriorityHeader"))
- {
- static char buf[MAXPATHLEN];
- sprintf(buf,"^%s: [ \t]*\\([^ \t]*\\)[ \t]*\n",pridef);
- pre=re_compile(buf,0);
- pridef=NXGetDefaultValue("Mail","PriorityValues");
- }
- */
- mi=malloc(sizeof(*mi));
-
- mi->record_length=sizeof(*mi);
- mi->mes_offset=-1;
- mi->status='*';
- mi->msgtype=' ';
- mi->encrypted=' ';
- mi->sync=' ';
- mi->mes_date=message_current_date();
-
- headerlen=0;
-
- while(fgets(line,LINELEN,stdin))
- {
- if (*line=='\n') break;
- 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)
- mi->mes_date=message_date(atoi(dre->braslist[2])+1900,
- 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 ((mi->msgtype==' ') && (re_match(line,mre)==1))
- {
- if (!strpcaseequ("text/plain",mre->braslist[0],mre->braelist[0]))
- mi->msgtype='m';
- }
- if (re_match(line,rre)==1)
- {
- char *c;
- mi->msgtype='r';
- strpcpy(reference,rre->braslist[1],rre->braelist[1]);
- c=reference+strlen(reference);
- *c++='_';
- while(c-reference<22) *c++='_';
- *c='\0';
- sprintf(c,"%ld",time(0));
- strcat(reference,".attach");
- c=reference+strlen(reference);
- strcat(reference,", ");
- if (*(rre->braslist[2])=='E')
- {
- strcat(reference,"E, ");
- mi->encrypted='e';
- }
- 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!='r') 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);
-
- 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 = NULL; //NXGetDefaultValue("Mail", 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;
- }
-
- void main(int ac,char *av[])
- {
- struct message_index *mi;
- const char *decodeCmd=0;
- const char*uncompressCmd=0;
- const char *tarCmd=0;
- int c;
- int readflg=0,nowaitflg=0,verboseflg=0,multiflg=0,flagflg=0,deleteflg=0;
- int pri=-1;
- time_t mboxtime;
- int mfd=-1,tocfd=-1;
- FILE *tocf=NULL;
- struct table_of_contents_header *toch=0;
- int status=EXIT_SUCCESS;
-
- set_progname(av[0]);
-
- while((c=getopt(ac,av,"rfdvnp:VH"))!=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 '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);
-
- mi=readmail();
-
- 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=' ';
- else if (flagflg) mi->status='+';
- else if (deleteflg) mi->status='d';
-
- if (pri>=0) message_set_priority(mi,pri);
-
- for(;optind<ac;optind++)
- {
- if (verboseflg) fprintf(stderr,"Entering %s...\n",av[optind]);
- if (cd_mbox(av[optind],1)) goto next;
- if (lock_mbox(nowaitflg)) goto next;
-
- mboxtime=mtime("mbox");
-
- if ((mfd=open("mbox",O_WRONLY|O_APPEND|O_CREAT,0644))==-1)
- {
- fprintf(stderr,"%s: %s: %s\n",progname(),av[optind],strerror(errno));
- status=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(),av[optind],strerror(errno));
- status=EXIT_FAILURE;
- //goto unlock;
- }
- else
- {
- toch=get_table_of_contents_header(tocf,1);
- if (!toch)
- {
- fprintf(stderr,"%s: %s: warning: invalid table_of_contents\n",progname(),av[optind]);
- status=EXIT_FAILURE;
- }
- else if (toch->mbox_time!=mboxtime)
- {
- fprintf(stderr,"%s: %s: warning: table_of_contents out of sync\n",progname(),av[optind]);
- status=EXIT_FAILURE;
- }
- }
-
- /* FOR EACH */
- lseek(mfd,0,SEEK_END);
- mi->mes_offset=lseek(mfd,0,SEEK_CUR);
-
- if (mi->msgtype=='r')
- {
- char path[MAXPATHLEN];
- FILE *f;
-
- /* Locate executables, if not already done so. */
- if (!decodeCmd && !(decodeCmd=get_command("appnmailDecodeCommand","/NextApps/Mail.app/decode",NULL)))
- {
- /* ...not really a regular Mail default. */
- status=EXIT_FAILURE;
- goto unlock;
- }
- if (!uncompressCmd && !(uncompressCmd=get_command("UncompressCommand","/usr/bin/gunzip","/usr/ucb/uncompress",NULL)))
- {
- status=EXIT_FAILURE;
- goto unlock;
- }
- if (!tarCmd && !(tarCmd=get_command("TarCommand","/NextApps/Mail.app/safetar","/usr/bin/gnutar",NULL)))
- {
- status=EXIT_FAILURE;
- goto unlock;
- }
- strcpy(path,message_reference(mi));
- if ((mkdir(path,0755))==-1)
- {
- fprintf(stderr,"%s: %s: %s: %s\n",progname(),av[optind],path,strerror(errno));
- status=EXIT_FAILURE;
- goto unlock;
- }
- 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(),av[optind],path,strerror(errno));
- status=EXIT_FAILURE;
- goto unlock;
- }
- fwrite(content,contentlen,1,f);
- if ((c=pclose(f))!=0)
- {
- fprintf(stderr,"%s: %s: %s failed (status %d)\n",progname(),av[optind],line,c);
- status=EXIT_FAILURE;
- goto unlock;
- }
- message_set_attachsize(mi,dirsize(path));
- message_set_attachtime(mi,mtime(path));
- /* Add message body *after* all failure points, to avoid TOC corruption. */
- write(mfd,header,headerlen);
- write(mfd,"\n",1);
- // XXX should check status.
- }
- else
- {
- write(mfd,header,headerlen);
- write(mfd,content,contentlen);
- // XXX should check status.
- }
- if (toch)
- {
- toch->num_msgs++;
- fseek(tocf,0,SEEK_END);
- put_message_index(tocf,mi);
- }
- /* 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;
- unlock_mbox();
- next:;
- if (toch) { free(toch); toch=0; }
- uncd_mbox();
- }
- exit(status);
- }
-