home *** CD-ROM | disk | FTP | other *** search
- /*
- * Program: Routines to support attachments in the Pine composer
- *
- * Author: Michael Seibel
- * Networks and Distributed Computing
- * Computing & Communications
- * University of Washington
- * Administration Building, AG-44
- * Seattle, WA 98195
- * Internet: mikes@cac.washington.edu
- *
- * Date: 31 Mar 1992
- * Last Edited:
- *
- * Copyright 1991 by the University of Washington
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee 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.
- *
- *
- *
- * NOTES:
- *
- *
- */
- #include <stdio.h>
- #include <ctype.h>
- #include <math.h>
- #include "osdep.h"
- #include "estruct.h"
- #include "edef.h"
- #include "pico.h"
-
- #ifdef ATTACHMENTS
-
- /*
- * external declarations
- */
- extern struct headerentry headents[];
- extern char *gethomedir();
-
- /*
- * other useful declarations
- */
- struct hdr_line *next_line(), *prev_line();
- PATMT *NewAttach();
- char *prettysz();
- void ZotAttach();
-
-
- /*
- * 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!");
- else
- emlwrite("No Attachment comment help yet!");
-
- /* 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("\007Aborted");
- return(0);
- case TRUE: /* some comment */
- case FALSE: /* No comment */
- if(i-- == 2){
- if((gmode&MDSCUR) && !compresspath("", fn, NLINE)){
- emlwrite("\007Restricted mode allows attachments from home directory only", NULL);
- return(0);
- }
-
- 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 entry = ATTCHDR, /* which set of header lines */
- 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];
-
- 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 space for filename");
- 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");
- 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");
- return(-1);
- }
- }
-
- strcpy(tp->description, comment);
- }
- break;
- }
- }
-
- if(tp){
- bld[bi++] = tp;
- }
- else{
- if(file[0] != '['){
- if((tp = NewAttach(file, size, comment)) == NULL)
- return(-1);
- bld[bi++] = tp;
- }
- }
- }
-
- 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;
- 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", 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 */
- if((gmode&MDSCUR) && !compresspath("", fn, NLINE)){
- emlwrite("\007Restricted mode allows attachments from home directory only", NULL);
- rv = -1;
- level = TG;
- break;
- }
-
- 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");
- }
- }
- }
- 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", 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");
- 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");
- 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");
- rv = -1;
- }
- }
- break;
-
- default: /* something's very wrong */
- emlwrite("\007Attchmnt: Weirdness in ParseAttach");
- 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;
- {
- char *cp;
- PATMT *tp;
-
- if((tp=(PATMT *)malloc(sizeof(PATMT))) == NULL){
- emlwrite("No memory to add attachment");
- 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");
- 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");
- 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");
- free((char *) tp->size);
- free((char *) tp->filename);
- free((char *) tp);
- return(NULL);
- }
- strcpy(tp->description, c);
-
- return(tp);
- }
-
-
- 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???"); /* 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';
- }
- }
-