home *** CD-ROM | disk | FTP | other *** search
- #if !defined(lint) && !defined(DOS)
- static char rcsid[] = "$Id: attach.c,v 4.8 1993/11/08 23:08:56 mikes Exp $";
- #endif
- /*
- * Program: Routines to support attachments in the Pine composer
- *
- *
- * Michael Seibel
- * Networks and Distributed Computing
- * Computing and Communications
- * University of Washington
- * Administration Builiding, AG-44
- * Seattle, Washington, 98195, USA
- * Internet: mikes@cac.washington.edu
- *
- * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
- *
- * Copyright 1991-1993 University of Washington
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee to the University of
- * Washington is hereby granted, provided that the above copyright notice
- * appears in all copies and that both the above copyright notice and this
- * permission notice appear in supporting documentation, and that the name
- * of the University of Washington not be used in advertising or publicity
- * pertaining to distribution of the software without specific, written
- * prior permission. This software is made available "as is", and
- * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
- * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
- * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
- * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
- * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Pine and Pico are trademarks of the University of Washington.
- * No commercial use of these trademarks may be made without prior
- * written permission of the University of Washington.
- *
- *
- * NOTES:
- *
- *
- */
- #include <stdio.h>
- #include <ctype.h>
- #include <math.h>
- #include "osdep.h"
- #include "pico.h"
- #include "estruct.h"
- #include "edef.h"
- #include "efunc.h"
-
- #ifdef ATTACHMENTS
-
-
- #ifdef ANSI
- int ParseAttach(struct hdr_line **,int *,char *,char *,char *,int *);
- PATMT *NewAttach(char *, long, char *);
- void ZotAttach(struct pico_atmt *);
- int sinserts(char *, int, char *, int);
- #else
- int ParseAttach();
- PATMT *NewAttach();
- void ZotAttach();
- int sinserts();
- #endif
-
-
- /*
- * max number of attachments
- */
- #define MAXATCH 64
-
-
- /*
- * AskAttach - ask for attachment fields and build resulting structure
- * return pointer to that struct if OK, NULL otherwise
- */
- AskAttach(fn, sz, cmnt)
- char *fn, *sz, *cmnt;
- {
- int i, status;
- long l = 0;
- char bfn[NFILEN];
-
- i = 2;
- fn[0] = '\0';
- sz[0] = '\0';
- cmnt[0] = '\0';
-
- while(i){
-
- if(i == 2){
- wkeyhelp("GC00000T0000","Get Help,Cancel,To Files");
- status = mlreply("File to attach: ", fn, NLINE, QFFILE);
- }
- else{
- wkeyhelp("GC0000000000","Get Help,Cancel");
- status = mlreply("Attachment comment: ", cmnt, NLINE, QNORML);
- }
-
- switch(status){
- case HELPCH:
- if(i == 2)
- emlwrite("No Attachment file help yet!", NULL);
- else
- emlwrite("No Attachment comment help yet!", NULL);
-
- /* remove break and sleep when help text done to force redraw */
- sleep(3);
- break;
-
- case (CTRL|'T'):
- if(i != 2){
- (*term.t_beep)();
- break;
- }
-
- *bfn = '\0';
- if(*fn == '\0' || !isdir(fn, NULL))
- strcpy(fn, gethomedir(NULL));
-
- if(FileBrowse(fn, bfn, sz) == 1){
- strcat(fn, S_FILESEP);
- strcat(fn, bfn);
- i--;
- }
- else
- *fn = '\0';
-
- refresh(FALSE, 1);
- update();
- break;
-
- case (CTRL|'L'):
- refresh(FALSE, 1);
- update();
- continue;
-
- case ABORT:
- emlwrite("\007Cancelled", NULL);
- return(0);
- case TRUE: /* some comment */
- case FALSE: /* No comment */
- if(i-- == 2){
- fixpath(fn, NLINE); /* names relative to ~ */
- if((gmode&MDSCUR) && homeless(fn)){
- emlwrite("\007Restricted mode allows attachments from home directory only", NULL);
- return(0);
- }
-
- if((status=fexist(fn, "r", &l)) != FIOSUC){ /* does file exist? */
- fioperr(status, fn);
- return(0);
- }
- strcpy(sz, prettysz(l));
- }
- else{
- mlerase();
- return(1); /* mission accomplished! */
- }
-
- break;
- default:
- break;
- }
- }
- }
-
-
-
- /*
- * SyncAttach - given a pointer to a linked list of attachment structures,
- * return with that structure sync'd with what's displayed.
- * delete any attachments in list of structs that's not on
- * the display, and add any that aren't in list but on display.
- */
- SyncAttach()
- {
- int offset = 0, /* the offset to begin */
- rv = 0,
- ki = 0, /* number of known attmnts */
- bi = 0, /* build array index */
- na, /* old number of attachmnt */
- status, i, j, n;
- char file[NLINE], /* buffers to hold it all */
- size[32],
- comment[1024];
- struct hdr_line *lp; /* current line in header */
- PATMT *tp, *knwn[MAXATCH], *bld[MAXATCH];
- extern struct headerentry headents[];
-
- if(Pmaster == NULL)
- return(-1);
-
- for(i=0;i<MAXATCH;i++) /* bug - ever pop this? */
- knwn[i] = bld[i] = NULL; /* zero out table */
-
- tp = Pmaster->attachments;
- while(tp != NULL){ /* fill table of */
- knwn[ki++] = tp; /* known attachments */
- tp = tp->next;
- }
-
- n = 0;
- lp = headents[ATTCHDR].hd_text;
- while(lp != NULL){
- na = ++n;
-
- if(status = ParseAttach(&lp, &offset, file, size, comment, &na))
- rv = (rv < 0) ? rv : status ; /* remember worst case */
-
- if(*file == '\0'){
- if(n != na && na > 0 && na <= ki && (knwn[na-1]->flags&A_FLIT)){
- bld[bi++] = knwn[na-1];
- knwn[na-1] = NULL;
- }
- continue;
- }
-
- if((gmode&MDSCUR) && homeless(file))
- /* no attachments outsize ~ in secure mode! */
- continue;
-
- tp = NULL;
- for(i=0;i < ki; i++){ /* already know about it? */
- /*
- * this is kind of gruesome. what we want to do is keep track
- * of literal attachment entries because they may not be
- * actual files we can access or that the user readily
- * access
- */
- if(knwn[i] &&
- ((!(knwn[i]->flags&A_FLIT) && !strcmp(file, knwn[i]->filename))
- || ((knwn[i]->flags&A_FLIT) && i+1 == na))){
- tp = knwn[i];
- knwn[i] = NULL; /* forget we know about it */
-
- if(status == -1) /* ignore garbage! */
- break;
-
- if((tp->flags&A_FLIT) && strcmp(file, tp->filename)){
- rv = 1;
- if((j=strlen(file)) > strlen(tp->filename)){
- if((tp->filename = (char *)realloc(tp->filename,
- sizeof(char)*(j+1))) == NULL){
- emlwrite("\007Can't realloc filename space",NULL);
- return(-1);
- }
- }
-
- strcpy(tp->filename, file);
- }
- else if(tp->size && strcmp(tp->size, size)){
- rv = 1;
- if((j=strlen(size)) > strlen(tp->size)){
- if((tp->size=(char *)realloc(tp->size,
- sizeof(char)*(j+1))) == NULL){
- emlwrite("\007Can't realloc space for size", NULL);
- return(-1);
- }
- }
-
- strcpy(tp->size, size);
- }
-
- if(strcmp(tp->description, comment)){ /* new comment */
- rv = 1;
- if((j=strlen(comment)) > strlen(tp->description)){
- if((tp->description=(char *)realloc(tp->description,
- sizeof(char)*(j+1))) == NULL){
- emlwrite("\007Can't realloc description", NULL);
- return(-1);
- }
- }
-
- strcpy(tp->description, comment);
- }
- break;
- }
- }
-
- if(tp){
- bld[bi++] = tp;
- }
- else{
- if(file[0] != '['){
- if((tp = NewAttach(file, atol(size), comment)) == NULL)
- return(-1);
- bld[bi++] = tp;
- }
- }
-
- if(status < 0)
- tp->flags |= A_ERR; /* turn ON error bit */
- else
- tp->flags &= ~(A_ERR); /* turn OFF error bit */
- }
-
- for(i=0; i < bi; i++) /* link together newly built list */
- bld[i]->next = bld[i+1];
-
- Pmaster->attachments = bld[0];
-
- for(i=0; i < ki; i++){ /* kill old/unused references */
-
- if(knwn[i]){
- ZotAttach(knwn[i]);
- free((char *) knwn[i]);
- }
- }
-
- return(rv);
- }
-
-
-
-
- /*
- * ParseAttach - given a header line and an offset into it, return with
- * the three given fields filled in. Assumes the size of
- * the buffers passed is large enough to hold what's there.
- * Always updates header fields that have changed or are
- * fixed. An error advances offset to next attachment.
- *
- * returns: 1 if a field changed
- * 0 nothing changed
- * -1 on error
- */
- ParseAttach(lp, off, fn, sz, cmnt, no)
- struct hdr_line **lp; /* current header line */
- int *off; /* offset into that line */
- char *fn, *sz, *cmnt; /* places to return fields */
- int *no; /* attachment number */
- {
- int j, status, /* various return codes */
- rv = 0, /* return value */
- lbln = 0; /* label'd attachment */
- long l; /* attachment length */
- char c,
- *p = s,
- *lblsz = NULL, /* label'd attchmnt's size */
- number[8];
- register struct hdr_line *lprev;
- enum { /* parse levels */
- LWS, /* leading white space */
- NUMB, /* attachment number */
- WSN, /* white space after number */
- TAG, /* attachments tag (fname) */
- WST, /* white space after tag */
- SIZE, /* attachments size */
- SWS, /* white space after size */
- COMMENT, /* attachment comment */
- TG} level; /* trailing garbage */
-
- *fn = *sz = *cmnt = '\0'; /* initialize return strings */
-
- level = LWS; /* start at beginning */
- while(*lp != NULL){
-
- if((c=(*lp)->text[*off]) == '\0'){ /* end of display line */
- lprev = *lp;
- if((*lp = (*lp)->next) != NULL)
- c = (*lp)->text[*off = 0]; /* reset offset */
- }
-
- switch(level){
- case LWS: /* skip leading white space */
- if(isspace(c) || c == '\0'){
- break;
- }
- else if(c < '0' || c > '9'){ /* add a number */
- sprintf(number, "%d. ", *no);
- *no = 0; /* no previous number! */
- sinserts((*lp == NULL) ? &lprev->text[*off]
- : &(*lp)->text[*off],
- 0, number, j=strlen(number));
- *off += j - 1;
- rv = 1;
- if(c == ',') /* special case */
- (*off)++;
- else
- level = TAG; /* interpret the name */
- break;
- }
- level = NUMB;
- case NUMB: /* attachment number */
- if(c == '.'){ /* finished grabbing size */
- /*
- * replace number if it's not right
- */
- *p = '\0';
- sprintf(number, "%d", *no); /* record the current... */
- *no = atoi(s); /* and the old place in list */
- if(strcmp(number, s)){
- if(p-s > *off){ /* where to begin replacemnt */
- j = (p-s) - *off;
- sinserts((*lp)->text, *off, "", 0);
- sinserts(&lprev->text[strlen(lprev->text)-j], j,
- number, strlen(number));
- *off = 0;
- }
- else{
- j = (*off) - (p-s);
- sinserts((*lp == NULL) ? &lprev->text[j]
- : &(*lp)->text[j],
- p-s , number, strlen(number));
- *off += strlen(number) - (p-s);
- }
- rv = 1;
- }
- p = s;
- level = WSN; /* what's next... */
- }
- else if(c < '0' || c > '9'){
- *p = '\0';
- emlwrite("\007Attchmnt: Number field missing '.': \"%s\"", s);
- rv = -1;
- }
- else
- *p++ = c;
-
- break;
-
- case WSN: /* blast whitespace */
- if(isspace(c) || c == '\0'){
- break;
- }
- else if(c == '['){ /* labeled attachment */
- lbln++;
- }
- else if(!fallowc(c)){
- emlwrite("\007Attchmnt: '%c' not allowed in file name",
- (void *)(int)c);
- rv = -1;
- level = TG; /* eat rest of garbage */
- break;
- }
- level = TAG;
- case TAG: /* get and check filename */
- /* or labeled attachment */
- /* enclosed in [] */
- if(c == '\0' || (!lbln && (isspace(c) || strchr(",(\"", c)))
- || (lbln && c == ']')){
- if(p != s){
- *p = '\0'; /* got something */
-
- strcpy(fn, s); /* store file name */
- if(!lbln){ /* normal file attachment */
- fixpath(fn, NLINE);
- if((status=fexist(fn, "r", &l)) != FIOSUC){
- fioperr(status, fn);
- rv = -1;
- level = TG; /* munch rest of garbage */
- break;
- }
-
-
- if((gmode&MDSCUR) && homeless(fn)){
- emlwrite("\007Restricted mode allows attachments from home directory only", NULL);
- rv = -1;
- level = TG;
- break;
- }
-
- if(strcmp(fn, s)){ /* fn changed: display it */
- if(*off >= p - s){ /* room for it? */
- sinserts((*lp == NULL)?
- &lprev->text[(*off)-(p-s)] :
- &(*lp)->text[(*off)-(p-s)],
- p-s, fn, j=strlen(fn));
- *off += j - (p - s); /* advance offset */
- rv = 1;
- }
- else{
- emlwrite("\007Attchmnt: Problem displaying real file path", NULL);
- }
- }
- }
- else{ /* labelled attachment! */
- /*
- * should explain about labelled attachments:
- * these are attachments that came into the composer
- * with meaningless file names (up to caller of
- * composer to decide), for example, attachments
- * being forwarded from another message. here, we
- * just make sure the size stays what was passed
- * to us. The user is SOL if they change the label
- * since, as it is now, after changed, it will
- * just get dropped from the list of what gets
- * passed back to the caller.
- */
- PATMT *tp;
-
- if(c != ']'){ /* legit label? */
- emlwrite("\007Attchmnt: Expected ']' after \"%s\"",
- fn);
- rv = -1;
- level = TG;
- break;
- }
- strcat(fn, "]");
-
- /*
- * This is kind of cheating since otherwise
- * ParseAttach doesn't know about the attachment
- * struct. OK if filename's not found as it will
- * get taken care of later...
- */
- tp = Pmaster->attachments; /* caller check Pmaster! */
- j = 0;
- while(tp != NULL){
- if(++j == *no){
- lblsz = tp->size;
- break;
- }
- tp = tp->next;
- }
-
- if(tp == NULL){
- emlwrite("\007Attchmnt: Unknown reference: %s",fn);
- lblsz = "XXX";
- }
- }
- p = s; /* reset p in s */
- level = WST;
- }
-
- if(!lbln && c == '(') /* no space 'tween file, size*/
- level = SIZE;
- else if(c == '\0' || (!lbln && (c == ',' || c == '\"'))){
- strcpy(sz, (lblsz) ? lblsz : prettysz(l));
- sprintf(s, " (%s) %s", sz, (c == '\"') ? "" : "\"\"");
- sinserts((*lp == NULL) ? &lprev->text[*off]
- : &(*lp)->text[*off],
- 0, s, j = strlen(s));
- *off += j;
- rv = 1;
- level = (c == '\"') ? COMMENT : TG;/* cmnt or eat trash */
- }
- }
- else if(!lbln && (!fallowc(c) || c == '[' || c == ']')){
- emlwrite("\007Attchmnt: '%c' not allowed in file name",
- (void *)(int)c);
- rv = -1; /* bad char in file name */
- level = TG; /* gobble garbage */
- }
- else
- *p++ = c; /* add char to name */
-
- break;
-
- case WST: /* skip white space */
- if(!isspace(c)){
- /*
- * whole attachment, comment or done!
- */
- if(c == ',' || c == '\0' || c == '\"'){
- strcpy(sz, (lblsz) ? lblsz : prettysz(l));
- sprintf(s, " (%s) %s", sz,
- (c == '\"') ? "" : "\"\"");
- sinserts((*lp == NULL) ? &lprev->text[*off]
- : &(*lp)->text[*off],
- 0, s, j = strlen(s));
- *off += j;
- rv = 1;
- level = (c == '\"') ? COMMENT : TG;
- lbln = 0; /* reset flag */
- }
- else if(c == '('){ /* get the size */
- level = SIZE;
- }
- else{
- emlwrite("\007Attchmnt: Expected '(' or '\"' after %s",fn);
- rv = -1; /* bag it all */
- level = TG;
- }
- }
- break;
-
- case SIZE: /* check size */
- if(c == ')'){ /* finished grabbing size */
- *p = '\0';
- /*
- * replace sizes if they don't match!
- */
- strcpy(sz, s);
- if(strcmp(sz, (lblsz) ? lblsz : prettysz(l))){
- strcpy(sz, (lblsz) ? lblsz : prettysz(l));
- if(p-s > *off){ /* where to begin replacemnt */
- j = (p-s) - *off;
- sinserts((*lp)->text, *off, "", 0);
- sinserts(&lprev->text[strlen(lprev->text)-j], j,
- sz, strlen(sz));
- *off = 0;
- }
- else{
- j = (*off) - (p-s);
- sinserts((*lp == NULL) ? &lprev->text[j]
- : &(*lp)->text[j],
- p-s , sz, strlen(sz));
- *off += strlen(sz) - (p-s);
- }
- rv = 1;
- }
- p = s;
- level = SWS; /* what's next... */
- }
- else if(c == '\0' || c == ','){
- *p = '\0';
- emlwrite("\007Attchmnt: Size field missing ')': \"%s\"", s);
- rv = -1;
- level = TG;
- }
- else
- *p++ = c;
-
- break;
-
- case SWS: /* skip white space */
- if(!isspace(c)){
- if(c == ','){ /* no description */
- level = TG; /* munch rest of garbage */
- lbln = 0; /* reset flag */
- }
- else if(c != '\"' && c != '\0'){
- emlwrite("\007Attchmnt: Malformed comment, quotes required", NULL);
- rv = -1;
- level = TG;
- }
- else
- level = COMMENT;
- }
- break;
-
- case COMMENT: /* slurp up comment */
- if(c == '\"' || c == '\0'){ /* got comment */
- *p = '\0'; /* cap it off */
- p = s; /* reset p */
- strcpy(cmnt,s); /* copy the comment */
- if(c == '\0'){
- emlwrite("\007Attchmnt: Closing quote required at end of comment", NULL);
- rv = -1;
- }
- level = TG; /* prepare for next one */
- lbln = 0; /* reset flag */
- }
- else
- *p++ = c;
-
- break;
-
- case TG: /* get comma or final EOL */
- if(!isspace(c)){
- if(c != ',' && c != '\0'){
- if(rv != -1)
- emlwrite("\007Attchmnt: Comma must separate attachments", NULL);
- rv = -1;
- }
- }
- break;
-
- default: /* something's very wrong */
- emlwrite("\007Attchmnt: Weirdness in ParseAttach", NULL);
- return(-1); /* just give up */
- }
-
- if(c == '\0') /* we're done */
- break;
-
- (*off)++;
-
- /*
- * not in comment or label name? done.
- */
- if(c == ',' && (level != COMMENT && !lbln))
- break; /* put offset past ',' */
- }
-
- return(rv);
- }
-
-
-
- /*
- * NewAttach - given a filename (assumed to accessible) and comment, creat
- */
- PATMT *NewAttach(f, l, c)
- char *f;
- long l;
- char *c;
- {
- PATMT *tp;
-
- if((tp=(PATMT *)malloc(sizeof(PATMT))) == NULL){
- emlwrite("No memory to add attachment", NULL);
- return(NULL);
- }
- else{
- tp->filename = tp->description = NULL;
- tp->size = tp->id = NULL;
- tp->next = NULL;
- }
-
- /* file and size malloc */
- if((tp->filename = (char *)malloc(strlen(f)+1)) == NULL){
- emlwrite("Can't malloc name for attachment", NULL);
- free((char *) tp);
- return(NULL);
- }
- strcpy(tp->filename, f);
-
- if(l > -1){
- strcpy(s, prettysz(l));
- if((tp->size = (char *)malloc(sizeof(char)*(strlen(s)+1))) == NULL){
- emlwrite("Can't malloc size for attachment", NULL);
- free((char *) tp->filename);
- free((char *) tp);
- return(NULL);
- }
- else
- strcpy(tp->size, s);
- }
-
- /* description malloc */
- if((tp->description = (char *)malloc(strlen(c)+1)) == NULL){
- emlwrite("Can't malloc description for attachment", NULL);
- free((char *) tp->size);
- free((char *) tp->filename);
- free((char *) tp);
- return(NULL);
- }
- strcpy(tp->description, c);
-
- return(tp);
- }
-
-
-
- /*
- * AttachError - Sniff list of attachments, returning TRUE if there's
- * any sign of trouble...
- */
- int
- AttachError()
- {
- PATMT *ap;
-
- if(!Pmaster)
- return(0);
-
- ap = Pmaster->attachments;
- while(ap){
- if((ap->flags) & A_ERR)
- return(1);
-
- ap = ap->next;
- }
-
- return(FALSE);
- }
-
-
-
- void ZotAttach(p)
- PATMT *p;
- {
- if(!p)
- return;
- if(p->description)
- free((char *)p->description);
- if(p->filename)
- free((char *)p->filename);
- if(p->size)
- free((char *)p->size);
- if(p->id)
- free((char *)p->id);
- p->next = NULL;
- }
- #endif /* ATTACHMENTS */
-
-
- /*
- * intag - return TRUE if i is in a column that makes up an
- * attachment line number
- */
- intag(s, i)
- char *s;
- int i;
- {
- char *p = s;
- int n = 0;
-
- while(*p != '\0' && (p-s) < 5){ /* is there a tag? it */
- if(n && *p == '.') /* can't be more than 4 */
- return(i <= p-s); /* chars long! */
-
- if(*p < '0' || *p > '9')
- break;
- else
- n = (n * 10) + (*p - '0');
-
- p++;
- }
-
- return(FALSE);
- }
-
-
- /*
- * prettysz - return pointer to string containing nice
- */
- char *prettysz(l)
- long l;
- {
- static char b[32];
-
- if(l < 1000)
- sprintf(b, "%d B", l); /* xxx B */
- else if(l < 10000)
- sprintf(b, "%1.1f KB", (float)l/1000); /* x.x KB */
- else if(l < 1000000)
- sprintf(b, "%d KB", l/1000); /* xxx KB */
- else if(l < 10000000)
- sprintf(b, "%1.1f MB", (float)l/1000000); /* x.x MB */
- else
- sprintf(b, "%d MB", l/1000000); /* xxx MB */
- return(b);
- }
-
-
- /*
- * sinserts - s insert into another string
- */
- sinserts(ds, dl, ss, sl)
- char *ds, *ss; /* dest. and source strings */
- int dl, sl; /* their lengths */
- {
- char *dp, *edp; /* pointers into dest. */
- int j; /* jump difference */
-
- if(sl >= dl){ /* source bigger than dest. */
- dp = ds + dl; /* shift dest. to make room */
- if((edp = strchr(dp, '\0')) != NULL){
- j = sl - dl;
-
- for( ;edp >= dp; edp--)
- edp[j] = *edp;
-
- while(sl--)
- *ds++ = *ss++;
- }
- else
- emlwrite("\007No end of line???", NULL); /* can this happen? */
- }
- else{ /* dest is longer, shrink it */
- j = dl - sl; /* difference in lengths */
-
- while(sl--) /* copy ss onto ds */
- *ds++ = *ss++;
-
- if(strlen(ds) > j){ /* shuffle the rest left */
- do
- *ds = ds[j];
- while(*ds++ != '\0');
- }
- else
- *ds = '\0';
- }
- }
-