#if !defined(lint) && !defined(DOS) static char rcsid[] = "$Id: attach.c 7281 2004-04-05 01:06:15Z amk $"; #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" * * * Pine and Pico are registered trademarks of the University of Washington. * No commercial use of these trademarks may be made without prior written * permission of the University of Washington. * * Pine, Pico, and Pilot software and its included text are Copyright * 1989-2000 by the University of Washington. * * The full text of our legal notices is contained in the file called * CPYRIGHT, included with this distribution. * */ #include "headers.h" #include #ifdef ATTACHMENTS #ifdef ANSI int ParseAttach(struct hdr_line **,int *,char *, int,char *,char *,int,int *); PATMT *NewAttach(char *, long, char *); void ZotAttach(struct pico_atmt *); void sinserts(char *, int, char *, int); int AttachUpload(char *, char *); int AttachCancel(char *); #else int ParseAttach(); PATMT *NewAttach(); void ZotAttach(); void sinserts(); int AttachUpload(); int AttachCancel(); #endif #define HIBIT_WARN "Only ASCII characters allowed in attachment comments" /* * 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, fbrv, upload = 0; off_t attsz = 0; char bfn[NLINE]; i = 2; fn[0] = '\0'; sz[0] = '\0'; cmnt[0] = '\0'; while(i){ if(i == 2){ EXTRAKEYS menu_attach[4]; int n; menu_attach[n = 0].name = "^T"; menu_attach[n].label = "To Files"; menu_attach[n].key = (CTRL|'T'); if(gmode & MDCMPLT){ menu_attach[++n].name = "TAB"; menu_attach[n].label = "Complete"; menu_attach[n].key = (CTRL|'I'); } #if !defined(DOS) && !defined(MAC) if(Pmaster && Pmaster->upload){ /* * The Plan: ^R prompts for uploaded file's name which * is passed to the defined upload command when the user * hits Return to confirm the name. * NOTE: this is different than upload into message * text in which case the uploaded name isn't useful so * a temp file is ok (be sure to fix the file mode). */ menu_attach[++n].name = "^Y"; menu_attach[n].key = (CTRL|'Y'); menu_attach[n].label = upload ? "Read File" : "RcvUpload"; } #endif menu_attach[++n].name = NULL; KS_OSDATASET(&menu_attach[0], KS_NONE); status = mlreply(upload ? "Name to give uploaded attachment: " : "File to attach: ", fn, NLINE, QNORML, menu_attach); } else status = mlreply("Attachment comment: ", cmnt, NLINE, QNODQT, NULL); switch(status){ case HELPCH: if(Pmaster){ VARS_TO_SAVE *saved_state; saved_state = save_pico_state(); (*Pmaster->helper)(Pmaster->attach_help, "Attach Help", 1); if(saved_state){ restore_pico_state(saved_state); free_pico_state(saved_state); } pico_refresh(FALSE, 1); update(); continue; } else{ emlwrite("No Attachment %s help yet!", (i == 2) ? "file" : "comment"); sleep(3); } break; case (CTRL|'I') : if(i == 2){ char *fname, *p; int dirlen, l = NLINE; /* length of buffer space for bfn */ bfn[0] = '\0'; if(*fn && (p = strrchr(fn, C_FILESEP))){ fname = p + 1; l -= fname - fn; dirlen = p - fn; if(p == fn) strcpy(bfn, S_FILESEP); #ifdef DOS else if(fn[0] == C_FILESEP || (isalpha((unsigned char)fn[0]) && fn[1] == ':')){ if(fn[1] == ':' && p == fn+2) dirlen = fname - fn; #else else if (fn[0] == C_FILESEP || fn[0] == '~') { #endif strncpy(bfn, fn, dirlen); bfn[dirlen] = '\0'; } else sprintf(bfn, "%s%c%.*s", (gmode & MDCURDIR) ? "." : ((gmode & MDTREE) || opertree[0]) ? opertree : gethomedir(NULL), C_FILESEP, p - fn, fn); } else{ fname = fn; strcpy(bfn, (gmode & MDCURDIR) ? "." : ((gmode & MDTREE) || opertree[0]) ? opertree : gethomedir(NULL)); } if(!pico_fncomplete(bfn, fname, l - 1)) (*term.t_beep)(); } else (*term.t_beep)(); break; case (CTRL|'T'): if(i != 2){ (*term.t_beep)(); break; } *bfn = '\0'; if(*fn == '\0' || !isdir(fn, NULL, NULL)) strcpy(fn, (gmode & MDCURDIR) ? "." : ((gmode & MDTREE) || opertree[0]) ? opertree : gethomedir(NULL)); if((fbrv = FileBrowse(fn, NLINE, bfn, NLINE, sz, FB_READ | FB_ATTACH)) == 1){ if ((strlen(fn)+strlen(S_FILESEP)+strlen(bfn)) < NLINE){ strcat(fn, S_FILESEP); strcat(fn, bfn); if(upload && !AttachUpload(fn, sz)){ i = 2; /* keep prompting for file */ sleep(3); /* problem, show error! */ } else{ (void) QuoteAttach(fn); i--; /* go prompt for comment */ } } else{ /* trouble */ *fn = '\0'; AttachCancel(fn); pico_refresh(FALSE,1); update(); emlwrite("\007File name too BIG, cannot select!", NULL); sleep(3); } } else if (!fbrv) *fn = '\0'; else{ *fn = '\0'; AttachCancel(fn); pico_refresh(FALSE, 1); update(); emlwrite("\007File name too big, cannot select!", NULL); sleep(3); } /* fall thru to clean up the screen */ case (CTRL|'L'): pico_refresh(FALSE, 1); update(); continue; #if !defined(DOS) && !defined(MAC) case (CTRL|'Y'): /* upload? */ if(i == 2) upload ^= 1; /* flip mode */ else (*term.t_beep)(); break; #endif case ABORT: return(AttachCancel((upload && i == 1) ? fn : NULL)); case TRUE: /* some comment */ case FALSE: /* No comment */ if(i-- == 2){ if(upload){ fixpath(fn, NLINE); /* names relative to ~ */ status = AttachUpload(fn, sz); pico_refresh(FALSE, 1); update(); if(!status){ i = 2; /* keep prompting for file */ sleep(3); /* problem, show error! */ } } else { if(*fn == '\"' && fn[strlen(fn)-1] == '\"'){ int j; for(j = 0; fn[j] = fn[j+1]; j++) ; fn[j-1] = '\0'; } if(fn[0]){ if((gmode & MDTREE) && !compresspath(opertree, fn, NLINE)){ emlwrite( "Restricted mode allows attachments from %s only: too many ..'s", (gmode&MDSCUR) ? "home directory" : opertree); return(0); } else{ fixpath(fn, NLINE); /* names relative to ~ */ if((gmode&MDTREE) && !in_oper_tree(fn)){ emlwrite( "\007Restricted mode allows attachments from %s only", (gmode&MDSCUR) ? "home directory" : opertree); return(0); } } if((status = fexist(fn, "r", &attsz)) != FIOSUC){ fioperr(status, fn); /* file DOESN'T exist! */ return(0); } QuoteAttach(fn); strcpy(sz, prettysz(attsz)); } else return(AttachCancel((upload && i == 1) ? fn : NULL)); } } else{ mlerase(); return(1); /* mission accomplished! */ } break; default: break; } } return(0); } /* * AttachUpload - Use call back to run the external upload command. */ int AttachUpload(fn, sz) char *fn, *sz; { long l; if(gmode&MDSCUR){ emlwrite("\007Restricted mode disallows uploaded command", NULL); return(0); } if(Pmaster && Pmaster->upload && (*Pmaster->upload)(fn, &l)){ strcpy(sz, prettysz((off_t)l)); return(1); } return(0); } /* * AttachCancel - */ int AttachCancel(fn) char *fn; { emlwrite("Attach cancelled", NULL); if(fn && fn[0]) unlink(fn); /* blast uploaded file */ return(0); } extern struct headerentry *headents; /* * 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 */ nbld = 0, /* size of build array */ na, /* old number of attachmnt */ status, i, j, n = 0; char file[NLINE], /* buffers to hold it all */ size[32], comment[1024]; struct hdr_line *lp; /* current line in header */ struct headerentry *entry; PATMT *tp, **knwn = NULL, **bld; for(entry = headents; entry->name != NULL; entry++) { if(entry->is_attach) break; } if(Pmaster == NULL) return(-1); for(tp = Pmaster->attachments; tp; tp = tp->next) ki++; /* Count known attachments */ if(ki){ if((knwn = (PATMT **)malloc((ki+1) * (sizeof(PATMT *)))) == NULL){ emlwrite("\007Can't allocate space for %d known attachment array entries", (void *) (ki + 1)); rv = -1; goto exit_early; } for(i=0, tp = Pmaster->attachments; i < ki; i++, tp = tp->next){ knwn[i] = tp; /* fill table of */ /* known attachments */ } } /* * As a quick hack to avoid too many reallocs, check to see if * there are more header lines than known attachments. */ for(lp = entry->hd_text; lp ; lp = lp->next) nbld++; /* count header lines */ nbld = nbld > ki ? nbld : ki + 1; if((bld = (PATMT **)malloc(nbld * (sizeof(PATMT *)))) == NULL){ emlwrite("\007Can't allocate space for %d build array entries", (void *) nbld); rv = -1; goto exit_early; } lp = entry->hd_text; while(lp != NULL){ char fn[NLINE]; na = ++n; if(bi == nbld){ /* need to grow build array? */ if((bld = (PATMT **)realloc(bld, ++nbld * sizeof(PATMT *))) == NULL){ emlwrite("\007Can't resize build array to %d entries ", (void *) nbld); rv = -1; goto exit_early; } } if(status = ParseAttach(&lp, &offset, file, NLINE, size, comment, 1024, &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&MDTREE) && file[0] != '[' && (!in_oper_tree(file) || !compresspath(file, fn, NLINE))) /* no attachments outside ~ 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 can 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); rv = -1; goto exit_early; } } 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); rv = -1; goto exit_early; } } 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); rv = -1; goto exit_early; } } strcpy(tp->description, comment); } break; } } if(tp){ bld[bi++] = tp; } else{ if(file[0] != '['){ if((tp = NewAttach(file, atol(size), comment)) == NULL){ rv = -1; goto exit_early; } bld[bi++] = tp; } else break; } if(status < 0) tp->flags |= A_ERR; /* turn ON error bit */ else tp->flags &= ~(A_ERR); /* turn OFF error bit */ } if(bi){ for(i=0; i < bi-1; i++) /* link together newly built list */ bld[i]->next = bld[i+1]; bld[i]->next = NULL; /* tie it off */ Pmaster->attachments = bld[0]; } else Pmaster->attachments = NULL; exit_early: if(knwn){ for(i = 0; i < ki; i++){ /* kill old/unused references */ if(knwn[i]){ ZotAttach(knwn[i]); free((char *) knwn[i]); } } free((void *)knwn); } if(bld) free((void *)bld); return(rv); } /* * ParseAttach - given a header line and an offset into it, return with * the three given fields filled in. Size of fn and cmnt * buffers should be passed in fnlen and cmntlen. * 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, fnlen, sz, cmnt, cmntlen, no) struct hdr_line **lp; /* current header line */ int *off; /* offset into that line */ char *fn; /* return file name field */ int fnlen; /* fn buffer size */ char *sz, *cmnt; /* places to return fields */ int cmntlen, *no; /* attachment number */ { int j, status, bod, eod = -1, rv = 0, /* return value */ orig_offset, lbln = 0, /* label'd attachment */ hibit = 0, quoted = 0, escaped = 0; off_t attsz; /* attachment length */ char tmp[1024], c, c_lookahead, *p, *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 */ ASIZE, /* attachments size */ SWS, /* white space after size */ CMMNT, /* attachment comment */ TG} level; /* trailing garbage */ *fn = *sz = *cmnt = '\0'; /* initialize return strings */ p = tmp; orig_offset = bod = *off; level = LWS; /* start at beginning */ while(*lp != NULL){ if((c=(*lp)->text[*off]) == '\0'){ /* end of display line */ if(level == LWS && bod != *off){ (*lp)->text[bod] = '\0'; rv = 1; } lprev = *lp; if((*lp = (*lp)->next) != NULL) c = (*lp)->text[*off = bod = 0]; /* reset offset */ } if(c != '\0') c_lookahead = (*lp)->text[*off + 1]; switch(level){ case LWS: /* skip leading white space */ if(isspace((unsigned char)c) || c == ','){ c = ' '; break; } if(c == '\0'){ if(bod > 0 && *off >= bod && lprev){ lprev->text[bod - 1] = '\0'; rv = 1; } } else if(*off > bod && *lp){ /* wipe out whitespace */ (void)bcopy(&(*lp)->text[*off], &(*lp)->text[bod], strlen(&(*lp)->text[*off]) + 1); *off = bod; /* reset pointer */ rv = 1; } if(c == '\0') break; if(!isdigit((unsigned char)c)){ /* 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 == '\0' || c == ','){ /* got to end, no number yet */ *p = '\0'; sprintf(number, "%d. ", *no); *no = 0; /* no previous number! */ if(c == '\0') *lp = lprev; /* go back and look at prev */ c = (*lp)->text[*off = orig_offset]; /* reset offset */ sinserts((*lp == NULL) ? &lprev->text[*off] : &(*lp)->text[*off], 0, number, j=strlen(number)); *off += j - 1; rv = 1; p = tmp; level = WSN; /* what's next... */ break; } else if(c == '.' && isspace((unsigned char)c_lookahead)){ /* finished grabbing number */ /* if not space is not number */ /* * replace number if it's not right */ *p = '\0'; sprintf(number, "%d", *no); /* record the current... */ *no = atoi(tmp); /* and the old place in list */ if(strcmp(number, tmp)){ if(p-tmp > *off){ /* where to begin replacemnt */ j = (p-tmp) - *off; sinserts((*lp)->text, *off, "", 0); sinserts(&lprev->text[strlen(lprev->text)-j], j, number, strlen(number)); *off = 0; } else{ j = (*off) - (p-tmp); sinserts((*lp == NULL) ? &lprev->text[j] : &(*lp)->text[j], p-tmp , number, strlen(number)); *off += strlen(number) - (p-tmp); } rv = 1; } p = tmp; level = WSN; /* what's next... */ } else if(c < '0' || c > '9'){ /* Must be part of tag */ sprintf(number, "%d. ", *no); sinserts((*lp == NULL) ? &lprev->text[(*off) - (p - tmp)] : &(*lp)->text[(*off) - (p - tmp)], 0, number, j=strlen(number)); *off += j; level = TAG; /* interpret the name */ goto process_tag; /* in case already past end of tag */ } else *p++ = c; break; case WSN: /* blast whitespace */ if(isspace((unsigned char)c) || c == '\0'){ break; } else if(c == '['){ /* labeled attachment */ lbln++; } else if(c == ',' || 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 */ process_tag: /* enclosed in [] */ if(c == '\0' || (lbln && c == ']') || (quoted && p != tmp && c == '\"') || (!(lbln || quoted) && (isspace((unsigned char) c) || strchr(",(\"", c)))){ if(p == tmp){ if(c == '\"') quoted++; } else{ *p = '\0'; /* got something */ if (strlen(tmp) > fnlen) emlwrite("File name too big!",NULL); strncpy(fn, tmp, fnlen); /* store file name */ if(!lbln){ /* normal file attachment */ if((gmode & MDTREE) && !compresspath(opertree, fn, fnlen)){ emlwrite( "Attachments allowed only from %s: too many ..'s", (gmode&MDSCUR) ? "home directory" : opertree); rv = -1; level = TG; break; } else{ fixpath(fn, fnlen); if((status=fexist(fn, "r", &attsz)) != FIOSUC){ fioperr(status, fn); rv = -1; level = TG; /* munch rest of garbage */ break; } if((gmode & MDTREE) && !in_oper_tree(fn)){ emlwrite("\007Attachments allowed only from %s", (gmode&MDSCUR) ? "home directory" : opertree); rv = -1; level = TG; break; } } if(strcmp(fn, tmp)){ /* fn changed: display it */ if(*off >= p - tmp){ /* room for it? */ sinserts((*lp == NULL)? &lprev->text[(*off)-(p-tmp)] : &(*lp)->text[(*off)-(p-tmp)], p-tmp, fn, j=strlen(fn)); *off += j - (p - tmp); /* 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 = tmp; /* reset p in tmp */ level = WST; } if(!lbln && c == '(') /* no space 'tween file, size*/ level = ASIZE; else if(c == '\0' || (!(lbln || quoted) && (c == ',' || c == '\"'))){ strcpy(sz, (lblsz) ? lblsz : prettysz(attsz)); sprintf(tmp, " (%s) %s", sz, (c == '\"') ? "" : "\"\""); sinserts((*lp == NULL) ? &lprev->text[*off] : &(*lp)->text[*off], 0, tmp, j = strlen(tmp)); *off += j; rv = 1; level = (c == '\"') ? CMMNT : TG;/* cmnt or eat trash */ } } else if(!(lbln || quoted) && (c == ',' || 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((unsigned char)c)){ /* * whole attachment, comment or done! */ if(c == ',' || c == '\0' || c == '\"'){ strcpy(sz, (lblsz) ? lblsz : prettysz(attsz)); sprintf(tmp, " (%s) %s", sz, (c == '\"') ? "" : "\"\""); sinserts((*lp == NULL) ? &lprev->text[*off] : &(*lp)->text[*off], 0, tmp, j = strlen(tmp)); *off += j; rv = 1; level = (c == '\"') ? CMMNT : TG; lbln = 0; /* reset flag */ } else if(c == '('){ /* get the size */ level = ASIZE; } else{ emlwrite("\007Attchmnt: Expected '(' or '\"' after %s",fn); rv = -1; /* bag it all */ level = TG; } } break; case ASIZE: /* check size */ if(c == ')'){ /* finished grabbing size */ *p = '\0'; /* * replace sizes if they don't match! */ strcpy(sz, tmp); if(strcmp(sz, (lblsz) ? lblsz : prettysz(attsz))){ strcpy(sz, (lblsz) ? lblsz : prettysz(attsz)); if(p-tmp > *off){ /* where to begin replacemnt */ j = (p-tmp) - *off; sinserts((*lp)->text, *off, "", 0); sinserts(&lprev->text[strlen(lprev->text)-j], j, sz, strlen(sz)); *off = 0; } else{ j = (*off) - (p-tmp); sinserts((*lp == NULL) ? &lprev->text[j] : &(*lp)->text[j], p-tmp , sz, strlen(sz)); *off += strlen(sz) - (p-tmp); } rv = 1; } p = tmp; level = SWS; /* what's next... */ } else if(c == '\0' || c == ','){ *p = '\0'; emlwrite("\007Attchmnt: Size field missing ')': \"%s\"", tmp); rv = -1; level = TG; } else *p++ = c; break; case SWS: /* skip white space */ if(!isspace((unsigned char)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 = CMMNT; } break; case CMMNT: /* slurp up comment */ if((c == '\"' && !escaped) || c == '\0'){ *p = '\0'; /* cap it off */ p = tmp; /* reset p */ if (strlen(tmp) > cmntlen){ emlwrite("Comment too long!",NULL); } strncpy(cmnt,tmp,cmntlen-1); /* 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 if(c == '\\' && !escaped){ /* something escaped? */ escaped = 1; } else{ if(escaped){ if(c != '\"') /* we only quote escapes */ *p++ = '\\'; escaped = 0; } if(((*p++ = c) & 0x80) && (gmode & MDHBTIGN) && !hibit++) emlwrite(HIBIT_WARN, NULL); } break; case TG: /* get comma or final EOL */ if(eod < 0) eod = *off; if(!isspace((unsigned char)c)){ switch(c){ case '\0': if(eod != *off) lprev->text[*off = eod] = '\0'; break; case ',': if(eod != *off){ (void)bcopy(&(*lp)->text[*off], &(*lp)->text[eod], strlen(&(*lp)->text[*off]) + 1); *off = eod; rv = 1; } break; default: 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 != TAG && level != CMMNT && !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 memset(tp, 0, sizeof(PATMT)); /* 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){ tp->size = (char *)malloc(sizeof(char)*(strlen(prettysz((off_t)l))+1)); if(tp->size == NULL){ emlwrite("Can't malloc size for attachment", NULL); free((char *) tp->filename); free((char *) tp); return(NULL); } else strcpy(tp->size, prettysz((off_t)l)); } /* 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); /* callback to show user the mime type that will be used for attachment */ if(Pmaster->mimetype && (*Pmaster->mimetype)(f) > 0){ int rv ; clearcursor(); mlerase(); rv = (*Pmaster->showmsg)('x'); ttresize(); picosigs(); if(rv) /* Did showmsg corrupt the screen? */ PaintBody(0); /* Yes, repaint it */ mpresf = 1; } 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); } char * QuoteAttach(fn) char *fn; { char *p; if(*fn && strpbrk(fn, " \t,(\"")){ /* Quote it? */ p = &fn[strlen(fn)]; *(p+2) = '\0'; *(p+1) = '\"'; do *p = *(p-1); while(--p != fn); *p = '\"'; } return(fn); } void ZotAttach(p) PATMT *p; { if(!p) return; if(p->description) free((char *)p->description); if(p->filename){ if(p->flags & A_TMP) unlink(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) off_t l; { static char b[32]; if(l < 1000) sprintf(b, "%ld B", (long)l); /* xxx B */ else if(l < 10000) sprintf(b, "%1.1f KB", (float)l/1000); /* x.x KB */ else if(l < 1000000) sprintf(b, "%ld KB", (long)l/1000L); /* xxx KB */ else if(l < 10000000) sprintf(b, "%1.1f MB", (float)l/1000000); /* x.x MB */ else sprintf(b, "%ld MB", (long)l/1000000L); /* xxx MB */ return(b); } /* * sinserts - s insert into another string */ void 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'; } }