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.