This is appnmail.m in view mode; [Download] [Up]
/* -*-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); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.