/* mime.c */
#include "ml.h"
#ifdef __STDC__
Mime_Handler *new_mime_handler(void)
#else
Mime_Handler *new_mime_handler()
#endif
{
Mime_Handler *mime_handler = (Mime_Handler *) fs_get(sizeof(Mime_Handler));
mime_handler->type = TYPEOTHER;
mime_handler->subtype = NULL;
mime_handler->view_builtin = FALSE;
mime_handler->builtin = NULL;
mime_handler->view = NULL;
mime_handler->compose = NULL;
mime_handler->next = NULL;
return(mime_handler);
}
Mime_Type *mime_types = NULL;
Filename_Map *external_filename_map = NULL;
Filename_Map builtin_filename_map[] = {
{ ".text", TYPETEXT, "plain", NULL },
{ ".txt", TYPETEXT, "plain", NULL },
{ ".ps", TYPEAPPLICATION, "postscript", NULL },
{ ".c", TYPETEXT, "plain", NULL },
{ ".html", TYPETEXT, "html", NULL },
{ ".gif", TYPEIMAGE, "gif", NULL },
{ ".tiff", TYPEIMAGE, "tiff", NULL },
{ ".jpg", TYPEIMAGE, "jpeg", NULL },
{ ".mpg", TYPEVIDEO, "mpeg", NULL },
{ ".qt", TYPEVIDEO, "quicktime", NULL },
{ ".au", TYPEAUDIO, "basic", NULL },
{ ".tar", TYPEAPPLICATION, "octet-stream", NULL },
};
Mime_Type builtin_mime_types[] = {
{ TYPETEXT, "plain", NULL },
{ TYPETEXT, "html", NULL },
{ TYPETEXT, "tab-separated-values", NULL },
{ TYPEIMAGE, "jpeg", NULL },
{ TYPEIMAGE, "gif", NULL },
{ TYPEIMAGE, "ief", NULL },
{ TYPEIMAGE, "tiff", NULL },
{ TYPEIMAGE, "x-xwindowdump", NULL },
{ TYPEIMAGE, "x-portable-anymap", NULL },
{ TYPEIMAGE, "x-portable-bitmap", NULL },
{ TYPEIMAGE, "x-portable-graymap", NULL },
{ TYPEIMAGE, "x-portable-pixmap", NULL },
{ TYPEIMAGE, "x-x11-dump", NULL },
{ TYPEIMAGE, "x-xbitmap", NULL },
{ TYPEAUDIO, "basic", NULL },
{ TYPEAUDIO, "x-sun-audio", NULL },
{ TYPEVIDEO, "mpeg", NULL },
{ TYPEVIDEO, "quicktime", NULL },
{ TYPEMULTIPART, "mixed", NULL },
{ TYPEMULTIPART, "alternative", NULL },
{ TYPEMULTIPART, "digest", NULL },
{ TYPEMULTIPART, "parallel", NULL },
{ TYPEMULTIPART, "appledouble", NULL },
{ TYPEMESSAGE, "rfc822", NULL },
{ TYPEMESSAGE, "partial", NULL },
{ TYPEMESSAGE, "external-body", NULL },
{ TYPEMESSAGE, "news", NULL },
{ TYPEAPPLICATION, "octet-stream", NULL },
{ TYPEAPPLICATION, "postscript", NULL },
{ TYPEAPPLICATION, "slate", NULL },
{ TYPEAPPLICATION, "wita", NULL },
{ TYPEAPPLICATION, "rtf", NULL },
{ TYPEAPPLICATION, "applefile", NULL },
{ TYPEAPPLICATION, "mac-binhex40", NULL },
{ TYPEAPPLICATION, "news-message-id", NULL },
{ TYPEAPPLICATION, "news-transmission", NULL },
{ TYPEAPPLICATION, "wordperfect5.1", NULL },
{ TYPEAPPLICATION, "pgp", NULL },
{ TYPEAPPLICATION, "pdf", NULL },
{ TYPEAPPLICATION, "zip", NULL },
{ TYPEAPPLICATION, "macwriteii", NULL },
{ TYPEAPPLICATION, "msword", NULL },
{ TYPEAPPLICATION, "remote-printing", NULL },
{ TYPEAPPLICATION, "x-tex", NULL },
{ TYPEAPPLICATION, "x-texinfo", NULL },
{ TYPEAPPLICATION, "x-latex", NULL },
{ TYPEAPPLICATION, "x-troff", NULL },
};
Menu attach_menu[] = {
{ NULL, "attachtype_accept", NUL_TERM,
attachtype_accept, NULL, 0, NULL, NULL, BTN_ON },
{ NULL, "attachtype_cancel", NUL_TERM,
attachtype_cancel, NULL, 0, NULL, NULL, BTN_ON },
{ NULL, "attachtype_HELP", NUL_TERM,
attachtype_help, NULL, 0, NULL, NULL, BTN_ON },
};
Menu mimecompose_menu[] = {
{ NULL, "mimecompose_accept", NUL_TERM,
mimecompose_accept, NULL, 0, NULL, NULL, BTN_ON },
{ NULL, "mimecompose_cancel", NUL_TERM,
mimecompose_cancel, NULL, 0, NULL, NULL, BTN_ON },
{ NULL, "mimecompose_HELP", NUL_TERM,
mimecompose_help, NULL, 0, NULL, NULL, BTN_ON },
};
#ifdef __STDC__
Boolean create_mime_attach_window(Widget w, BODY *body, Boolean is_multi)
#else
Boolean create_mime_attach_window(w, body, is_multi)
Widget w;
BODY *body;
Boolean is_multi;
#endif
{
Arg args[ARGLISTSIZE];
int n = 0;
char *ptr;
char *prelim_string = NULL;
Boolean valid = FALSE;
Widget form, menubar;
XtTranslations translations;
char buffer[FILEBUFFLEN];
Mime_Attach *mime_attach = (Mime_Attach *) fs_get(sizeof(Mime_Attach));
push_cursor(PIRATE_CURSOR);
mime_attach->done = 0;
if(preferences.autoPlace == TRUE) {
XtSetArg(args[n], XtNx, -1000); n ++;
XtSetArg(args[n], XtNy, -1000); n ++;
}
XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); n++;
mime_attach->shell = XtCreatePopupShell("attachtype_shell",
topLevelShellWidgetClass, w,
args, n);
n = 0;
AddDestroyCallback(mime_attach->shell);
setup_editres(mime_attach->shell);
if(pirate_icon != (Pixmap) None)
XtVaSetValues(mime_attach->shell,
XmNiconPixmap,pirate_icon,
NULL);
form = XmCreateForm(mime_attach->shell, "attachtype_form", args, n ); n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n ++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n ++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;
menubar = XmCreateMenuBar(form,"attachtype_menubar", args, n); n = 0;
XtManageChild(menubar);
create_buttons(NULL, menubar, attach_menu, XtNumber(attach_menu),
BTN_ON, (XtPointer) mime_attach, ROOTMENULEVEL);
mime_attach->desc_text =
create_text_field(form, menubar, "attachtype_description",
body->description,0, NULL, NULL);
if(body->description)
fs_give((void **) &body->description);
sprintf(buffer,"%s/%s",type_to_name(body->type),
(body->subtype) ? body->subtype : EMPTYSTR );
mime_attach->type_text =
create_text_field(form, mime_attach->desc_text, "attachtype_type",
buffer, 0,
(XtPointer) attachtype_accept,
(XtPointer) mime_attach );
XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmSTATIC); n ++;
XtSetArg(args[n], XmNlistSizePolicy,XmVARIABLE); n ++;
XtSetArg(args[n], XmNvisibleItemCount, 10 ); n ++;
XtSetArg(args[n], XmNselectionPolicy,XmSINGLE_SELECT); n ++;
mime_attach->type_list
= XmCreateScrolledList(form,"attachtype_list",args,n); n = 0;
XtAddCallback(mime_attach->type_list,
XmNsingleSelectionCallback,
(XtCallbackProc) attachtype_select, mime_attach);
XtAddCallback(mime_attach->type_list,
XmNdefaultActionCallback,
(XtCallbackProc) attachtype_accept, mime_attach);
translations =
XtParseTranslationTable(GLOBAL_modal_list_translations);
XtOverrideTranslations(mime_attach->type_list,translations);
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
XtSetArg(args[n], XmNtopWidget, mime_attach->type_text); n ++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n ++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n ++;
XtSetValues(XtParent(mime_attach->type_list), args, n); n = 0;
(void) stuff_typelist(mime_attach->type_list,is_multi);
XtManageChild(mime_attach->type_list);
XtManageChild(form);
XtManageChild(mime_attach->shell);
XtPopup(mime_attach->shell, XtGrabExclusive);
position_popup_widget(mime_attach->shell, TRUE);
modal_main_loop(&mime_attach->done);
if(body->subtype)
fs_give((void **) &body->subtype);
body->description = XmTextGetString(mime_attach->desc_text);
prelim_string = XmTextGetString(mime_attach->type_text);
if(*prelim_string != NUL_TERM) {
body->type = name_to_type(prelim_string);
ptr = strchr(prelim_string,TYPE_SEPARATOR_CHAR);
if(ptr) {
body->subtype = cpystr(ptr + 1);
valid = TRUE;
}
}
fs_give((void **) &prelim_string);
XtPopdown(mime_attach->shell);
XtDestroyWidget(mime_attach->shell);
fs_give((void **) &mime_attach);
pop_cursor();
return(valid);
}
#ifdef __STDC__
void stuff_typelist(Widget w, Boolean is_multi)
#else
void stuff_typelist(w,is_multi)
Widget w;
Boolean is_multi;
#endif
{
char buff[FILEBUFFLEN];
Mime_Type *mime_type;
int i;
XmString xstr;
if(mime_types != NULL) {
for(mime_type = mime_types; mime_type; mime_type = mime_type->next) {
if(is_multi) {
if(mime_type->type != TYPEMULTIPART)
continue;
}
else {
if(mime_type->type == TYPEMULTIPART)
continue;
}
sprintf(buff,"%s/%s",type_to_name(mime_type->type),mime_type->subtype);
xstr = XmStringCreateSimple(buff);
XmListAddItemUnselected(w,xstr,0);
XmStringFree(xstr);
}
}
else {
for(i = 0; i < XtNumber(builtin_mime_types); i ++) {
if(is_multi) {
if(builtin_mime_types[i].type != TYPEMULTIPART)
continue;
}
else {
if(builtin_mime_types[i].type == TYPEMULTIPART)
continue;
}
sprintf(buff,"%s/%s",type_to_name(builtin_mime_types[i].type),
builtin_mime_types[i].subtype);
xstr = XmStringCreateSimple(buff);
XmListAddItemUnselected(w,xstr,0);
XmStringFree(xstr);
}
}
return;
}
#ifdef __STDC__
void attachtype_select(Widget w, Mime_Attach *mime_attach,
XmListCallbackStruct *xp)
#else
void attachtype_select(w,mime_attach,xp)
Widget w;
Mime_Attach *mime_attach;
XmListCallbackStruct *xp;
#endif
{
char *str;
XmStringGetLtoR(xp->item,XmSTRING_DEFAULT_CHARSET,&str);
XmTextSetString(mime_attach->type_text,str);
fs_give((void **) &str);
return;
}
#ifdef __STDC__
void attachtype_accept(Widget w,Mime_Attach *mime_attach, XtPointer xp)
#else
void attachtype_accept(w,mime_attach,xp)
Widget w;
Mime_Attach *mime_attach;
XtPointer xp;
#endif
{
mime_attach->done = MODAL_LOOP_DONE;
return;
}
#ifdef __STDC__
void attachtype_cancel(Widget w,Mime_Attach *mime_attach, XtPointer xp)
#else
void attachtype_cancel(w,mime_attach,xp)
Widget w;
Mime_Attach *mime_attach;
XtPointer xp;
#endif
{
XmTextSetString(mime_attach->type_text,"");
mime_attach->done = MODAL_LOOP_DONE;
return;
}
#ifdef __STDC__
void attachtype_help(Widget w,Mime_Attach *mime_attach, XtPointer xp)
#else
void attachtype_help(w,mime_attach,xp)
Widget w;
Mime_Attach *mime_attach;
XtPointer xp;
#endif
{
help(mime_attach->shell, MIMEATTACHHELPFILE);
return;
}
/*
* Try and match a MIME type/subtype pair to the filename suffix,
* and set the BODY info accordingly. We supply a very limited suffix
* map which can be over-ridden (not enhanced) via an external map file.
* Returns TRUE if we matched with something. The format of the external
* file "$(LIBDIR)/mime.map" is
* .extension<SP|TAB>type/subtype<LF>
*
* Example:
*
* # This is a comment
* .html text/html
* .ps application/postscript
*
* The parser which gets this info is reasonably forgiving; and should
* allow any variations of whitespace and comment lines beginning with
* a '#' character. The extension _must_ start with a period. Matches
* are case-insensitive. We return the first match found. Since our linked
* list is built in LIFO order, the last matching entry in the file will
* always be the first found.
*
*/
#ifdef __STDC__
Boolean get_type_from_suffix(BODY *body, char *filename)
#else
Boolean get_type_from_suffix(body, filename)
BODY *body;
char *filename;
#endif
{
Boolean found = FALSE;
Filename_Map *current;
char *ptr;
int i;
if((ptr = strrchr(filename,'.')) != NULL) {
if(external_filename_map == NULL) {
for(i = 0; i < XtNumber(builtin_filename_map); i ++) {
if((strcasecmp(builtin_filename_map[i].suffix,ptr)) == STRMATCH ){
found = TRUE;
body->type = builtin_filename_map[i].type;
if(body->subtype)
fs_give((void **) &body->subtype);
body->subtype = cpystr(builtin_filename_map[i].subtype);
break;
}
}
}
else {
for(current = external_filename_map; current; current = current->next) {
if((strcasecmp(current->suffix,ptr)) == STRMATCH ) {
found = TRUE;
body->type = current->type;
if(body->subtype)
fs_give((void **) &body->subtype);
body->subtype = cpystr(current->subtype);
break;
}
}
}
}
return(found);
}
#ifdef __STDC__
void set_default_attach_type(BODY *body, char *filename)
#else
void set_default_attach_type(body,filename)
BODY *body;
char *filename;
#endif
{
char *ptr;
if(filename) {
ptr = strrchr(filename,PATH_SEPARATOR_CHAR);
if(ptr) {
body->description = cpystr(ptr + 1);
body->parameter = mail_newbody_parameter () ;
body->parameter->attribute = cpystr("name");
body->parameter->value = cpystr(ptr + 1);
body->parameter->next = NULL;
}
}
body->type = TYPETEXT;
body->subtype = cpystr(PLAINSUBTYPE);
return;
}
/*
* Takes a body, whose type info and description has already been filled in,
* and sets the contents appropriately to what's in the Binary_Buffer.
*/
#ifdef __STDC__
void add_attachment_contents_to_body(BODY *body,Binary_Buffer *binary_buffer)
#else
void add_attachment_contents_to_body(body,binary_buffer)
BODY *body;
Binary_Buffer *binary_buffer;
#endif
{
unsigned long i;
Boolean encode_it = FALSE;
unsigned line_len = 0;
unsigned long newlen;
if(body == NULL)
return;
/* Allow for no contents only if adding a new multipart leaf */
if((body->type != TYPEMULTIPART)
&& ((binary_buffer == NULL) || (binary_buffer->data == NULL)))
return;
switch(body->type) {
/*
* They say text, but we want to make sure it will go through
* the mail unmolested.
*/
case TYPETEXT:
for(i = 0; binary_buffer->data[i]; i ++) {
if(binary_buffer->data[i] >= 0x80 )
encode_it = TRUE;
if(binary_buffer->data[i] == LFCHAR)
line_len = 0;
if(++line_len > 128)
encode_it = TRUE;
}
if(i != binary_buffer->length)
encode_it = TRUE;
if(encode_it == TRUE) {
body->contents.text = (unsigned char *)
rfc822_8bit(binary_buffer->data, i, &newlen);
body->size.bytes = newlen;
body->encoding = ENCQUOTEDPRINTABLE;
}
else {
body->contents.text
= (unsigned char *) lftocrlf((char *) binary_buffer->data);
body->size.bytes = strlen((char *) body->contents.text);
body->encoding = ENC7BIT;
}
break;
case TYPEMULTIPART:
/* do nothing */
break;
case TYPEMESSAGE:
/*
* Hack to set the body parameters for external types. See notes
* below on the parse_external() function
*/
if((body->subtype)
&& (strcasecmp(body->subtype,"external-body") == STRMATCH))
parse_external(body,binary_buffer);
body->contents.msg.text = lftocrlf((char *) binary_buffer->data);
body->size.bytes = strlen(body->contents.msg.text);
body->encoding = ENC7BIT;
break;
case TYPEAPPLICATION:
case TYPEAUDIO:
case TYPEIMAGE:
case TYPEVIDEO:
case TYPEOTHER:
body->contents.text = (unsigned char *)
rfc822_binary(binary_buffer->data,
binary_buffer->length,
&body->size.bytes);
body->encoding = ENCBASE64;
break;
default:
break;
}
return;
}
/* ACCKKKKK!!!!!!!
* For message/external-body attachments, we need to somehow get
* the access parameters into the body->parameter list. Our compose program
* (I'm basing this on Metamail's "extcompose") needs to have a standard
* mail header line with all of the access stuff in it. We're assuming
* it starts at the beginning of the buffer. We're also going to destroy
* it before we're done parsing it.
* There's a lot that could go wrong here, but no checking is done.
* If the compose program does the right thing, it'll work. If not, too bad.
* Here's an example of what we might expect as input:
*
------
Content-Type: message/external-body; access-type=mail-server;
server=mailserver@podunk.edu
Content-Type: text/plain
send file.1
------
*
* Note that we only want the first "Content-Type:". The second one
* refers to the following text. It might also refer to the disposition
* of the external part itself. Can you say "kludge"? Thought so.
* A form for creating these things under our control would be easier.
*
*/
#ifdef __STDC__
void parse_external(BODY *body,Binary_Buffer *binary_buffer)
#else
void parse_external(body,binary_buffer)
BODY *body;
Binary_Buffer *binary_buffer;
#endif
{
char *ptr = (char *) binary_buffer->data;
char *p;
char *tmp;
/*
* We assume the input text is in mail header format. Tie off the
* input text at the end of the first header field; which might have
* continuation lines. When we've done that, set ptr to the character
* following this termination. We'll use it later.
*
*/
while(*ptr) {
for( ; (*ptr) && *ptr != LFCHAR; ptr ++)
;
if((*ptr) && ((*(ptr+1) == SPACECHAR) || (*(ptr+1) == TABCHAR))) {
*ptr = SPACECHAR;
ptr ++;
continue;
}
else {
if(ptr) {
*ptr = NUL_TERM;
ptr ++; /* point just beyond the end. */
}
break;
}
}
/*
* Our pointer is stashed away, and we've got an isolated header line.
* Tie off the header token name, and save the position following *it*.
*
*/
if((p = strchr((char *) binary_buffer->data,':')) != NULL) {
*p = NUL_TERM;
p ++;
}
/*
* Set up everything to call the c-client parser. Damn, this is ugly.
* But -- it saves having to write a complete mail-header parsing engine.
*
*/
/* point back to the beginning. */
tmp = (char *) binary_buffer->data;
/* The c-client wants the header to be capitalized. */
ucase(tmp);
/* Don't blame me. This is a copy of what the c-client does. */
if((tmp[0] == 'C') && (tmp[1] == 'O') && (tmp[2] == 'N') &&
(tmp[3] == 'T') && (tmp[4] == 'E') && (tmp[5] == 'N') &&
(tmp[6] == 'T') && (tmp[7] == '-')) {
/* Lie to the c-client */
body->type = 0;
fs_give((void **) &body->subtype);
/* parse the sucker. */
rfc822_parse_content_header(body,tmp+8,p);
/* We now have the parameters filled in. Wasn't that fun? */
body->type = TYPEMESSAGE; /* reset this guy */
}
/* Now make a fresh copy of any remaining body text. */
if(ptr)
tmp = cpystr(ptr);
fs_give((void **) &binary_buffer->data); /* free the original */
binary_buffer->data = (unsigned char *) tmp; /* return the new pointer */
return;
}
/*
* This and the following function convert to/from the text representation
* of MIME (major) type and the c-client's integer representation.
* It's a major hassle in this representation, because anywhere where
* type and subtype must exist together, we've got to do at least one
* conversion. And if we want to check a particular handler, we must do a
* numeric compare on type and string compare on subtype. Of course, there
* are good reasons for doing things this way, I'm just griping to whoever
* decides to actually read these comments; because they will appreciate
* and most likely need to know these subtleties.
*
*/
#ifdef __STDC__
int name_to_type(char *str)
#else
int name_to_type(str)
char *str;
#endif
{
if(!str)
return(TYPEOTHER);
if((strncasecmp(str,TEXT_TYPE_STR,strlen(TEXT_TYPE_STR))) == STRMATCH)
return(TYPETEXT);
if((strncasecmp(str,MULTI_TYPE_STR,strlen(MULTI_TYPE_STR))) == STRMATCH)
return(TYPEMULTIPART);
if((strncasecmp(str,MESSAGE_TYPE_STR,strlen(MESSAGE_TYPE_STR))) == STRMATCH)
return(TYPEMESSAGE);
if((strncasecmp(str,APPL_TYPE_STR,strlen(APPL_TYPE_STR))) == STRMATCH)
return(TYPEAPPLICATION);
if((strncasecmp(str,AUDIO_TYPE_STR,strlen(AUDIO_TYPE_STR))) == STRMATCH)
return(TYPEAUDIO);
if((strncasecmp(str,IMAGE_TYPE_STR,strlen(IMAGE_TYPE_STR))) == STRMATCH)
return(TYPEIMAGE);
if((strncasecmp(str,VIDEO_TYPE_STR,strlen(VIDEO_TYPE_STR))) == STRMATCH)
return(TYPEVIDEO);
return(TYPEOTHER);
}
#ifdef __STDC__
char *type_to_name(int type)
#else
char *type_to_name(type)
int type;
#endif
{
switch(type) {
case TYPETEXT:
return(TEXT_TYPE_STR);
break;
case TYPEMULTIPART:
return(MULTI_TYPE_STR);
break;
case TYPEMESSAGE:
return(MESSAGE_TYPE_STR);
break;
case TYPEAPPLICATION:
return(APPL_TYPE_STR);
break;
case TYPEAUDIO:
return(AUDIO_TYPE_STR);
break;
case TYPEIMAGE:
return(IMAGE_TYPE_STR);
break;
case TYPEVIDEO:
return(VIDEO_TYPE_STR);
break;
default:
return(OTHER_TYPE_STR);
break;
}
/* NOTREACHED */
}
/*
* Loads the file "$(MIMEDIR)/mime.types" into memory.
* File format is "type/subtype<LF>". Example:
*
* text/plain
* application/postscript
*
* Note that while this makes the list site-configurable (a good thing),
* people who want to muck with this file need to know what's acceptible
* (a bad thing). Don't arbitrarily add types into here which haven't been
* registered with the IANA. If you do, the subtype should be prefixed with
* "x-", such as "application/x-myfile". The master list supplied with this
* program contains two entries which were not registered as of this writing,
* but have enough common use that the "x-" rule was intentionally violated.
* "text/html" and "application/pgp". Perfect examples of why this file exists,
* and why one should follow the rules. It just screws up everybody when a
* sender and receiver can't agree on the correct typename.
*
*/
#ifdef __STDC__
void load_mime_types(void)
#else
void load_mime_types()
#endif
{
FILE *fp;
Mime_Type *mime_type = NULL;
char filename[MAXPATHLEN];
char buffer[FILEBUFFLEN];
char *ptr = NULL;
sprintf(filename,"%s/%s",preferences.mime_directory,MIMETYPESFILE);
if((fp = fopen(filename,"r")) == NULL)
return;
while((fgets(buffer,sizeof(buffer),fp)) != NULL) {
remove_trailing_white(buffer);
if((*buffer == NUL_TERM) || (*buffer == COMMENT_CHAR))
continue;
if((ptr = strchr(buffer,TYPE_SEPARATOR_CHAR)) == NULL)
continue;
mime_type = (Mime_Type *) fs_get(sizeof(Mime_Type));
mime_type->type = name_to_type(buffer);
mime_type->subtype = cpystr(ptr + 1);
mime_type->next = mime_types;
mime_types = mime_type;
}
fclose(fp);
return;
}
/*
* Load external filename_extension-to-mimetype list. It is not an error
* if this file isn't there. We'll use our limited hard-wired map in that
* case.
*/
#ifdef __STDC__
void load_mime_extension_map(void)
#else
void load_mime_extension_map()
#endif
{
FILE *fp;
Filename_Map *filename_map = NULL;
char filename[MAXPATHLEN];
char buffer[FILEBUFFLEN];
char *ptr = NULL;
char *suffix = NULL;
char *typename = NULL;
sprintf(filename,"%s/%s",preferences.mime_directory,MIMEMAPFILE);
if((fp = fopen(filename,"r")) == NULL)
return;
while((fgets(buffer,sizeof(buffer),fp)) != NULL) {
remove_trailing_white(buffer);
if((*buffer == NUL_TERM) || (*buffer == COMMENT_CHAR))
continue;
suffix = first_nonwhite(buffer);
for(typename = suffix; ((*typename) && (! isspace(*typename)));typename++);
while(isspace(*typename)) {
*typename = NUL_TERM;
typename ++;
}
if((*typename == NUL_TERM) ||
((ptr = strchr(typename,TYPE_SEPARATOR_CHAR)) == NULL))
continue;
filename_map = (Filename_Map *) fs_get(sizeof(Filename_Map));
filename_map->suffix = cpystr(suffix);
filename_map->type = name_to_type(typename);
filename_map->subtype = cpystr(ptr + 1);
filename_map->next = external_filename_map;
external_filename_map = filename_map;
}
fclose(fp);
return;
}
#ifdef __STDC__
Mime_Handler *get_handler(int type, char *subtype)
#else
Mime_Handler *get_handler(type,subtype)
int type;
char *subtype;
#endif
{
Mime_Handler *mime_handler;
if((subtype == NULL) || (*subtype == NUL_TERM))
return(NULL);
for(mime_handler = session->mime_handlers;
mime_handler;
mime_handler = mime_handler->next)
if((mime_handler->type == type) &&
(mime_handler->subtype) &&
((strcasecmp(mime_handler->subtype,subtype)) == STRMATCH))
return(mime_handler);
return(NULL);
}
/*
* Read in the mime handler config file. $(MIMEDIR)/mime.types
* This is "sort of" like metamail, but tailored for use in this program,
* so the metamail mailcap file won't really work. It has too many
* hacks. So does this. Format is:
type/subtype; view_program $COMPOSE=compose_program <LF>
* There is no white space between the type designator and the semi-colon.
* The view program is specified first. Note we don't have things like
* "needsterminal" or "copiousouput", so if the program requires a terminal,
* it needs to be started with "xterm -e {whatever}", and copiousoutput
* requires setting the scroll bars and a large window buffer on the xterm.
* Everything up to $COMPOSE= is taken as the viewer. Leading and trailing
* whitespace are trashed. Everything following the $COMPOSE= tag is the
* compose program (up to the linefeed). No continuation lines. One big line.
* Note also that the compose pipe currently doesn't allow input from stdin,
* only to a specific temp file designated as "%s". We control stderr,
* redirecting to another temporary file and logging the results. If you have
* a program with multiple pipes, and they all spit out stderr stuff, this
* will only catch the last one. You might consider wrapping the multiple
* pipes into a single shell command so that the user doesn't get terminal
* messages, only program log messages. And it's OK to have a viewer and
* no composer, or vice versa. There is also no "catch-all" handler, like
* "image/{*}". (Curly-braces used so I don't trip up "lint"). If the
* subtypes don't match, it won't get handled. We will pick
* off our internally handled types first, so it's pointless to set a handler
* for them.
*/
#ifdef __STDC__
void load_mime_handlers(void)
#else
void load_mime_handlers()
#endif
{
FILE *fp;
Mime_Handler *mime_handler = NULL;
char filename[MAXPATHLEN];
char buffer[FILEBUFFLEN];
char *ptr;
char *viewptr = NULL;
char *composeptr = NULL;
sprintf(filename,"%s/%s",preferences.mime_directory,MIMEHANDLERFILE);
if((fp = fopen(filename,"r")) == NULL)
return;
while((fgets(buffer,sizeof(buffer),fp)) != NULL) {
remove_trailing_white(buffer);
if((*buffer == NUL_TERM) || (*buffer == COMMENT_CHAR))
continue;
if((viewptr = strchr(buffer,SEMICOLONCHAR)) == NULL)
continue;
*viewptr = NUL_TERM;
viewptr ++;
viewptr = first_nonwhite(viewptr);
if(*viewptr == NUL_TERM)
continue;
if((ptr = strchr(buffer,TYPE_SEPARATOR_CHAR)) == NULL)
continue;
mime_handler = new_mime_handler();
mime_handler->type = name_to_type(buffer);
mime_handler->subtype = cpystr(ptr + 1);
composeptr = ML_Strstr(viewptr,COMPOSETAG);
if(composeptr) {
*composeptr = NUL_TERM;
composeptr += strlen(COMPOSETAG);
composeptr = first_nonwhite(composeptr);
if(*composeptr)
mime_handler->compose = cpystr(composeptr);
}
remove_trailing_white(viewptr);
if(*viewptr != NUL_TERM)
mime_handler->view = cpystr(viewptr);
mime_handler->next = session->mime_handlers;
session->mime_handlers = mime_handler;
}
fclose(fp);
return;
}
#ifdef __STDC__
int show_mime(Widget w, BODY *body, Boolean saveonly)
#else
int show_mime(w,body, saveonly)
Widget w;
BODY *body;
Boolean saveonly;
#endif
{
Mime_Handler *mime_handler;
char *command = NULL;
char *contents = NULL;
char *decode = NULL;
char *view = NULL;
char *internal_viewer = MLGetLocalized(XtNstrInternalViewer,
StrInternalViewer);
unsigned long bodylength;
unsigned long newlength;
int errors = 0;
Boolean internal = FALSE;
Boolean append_it = FALSE;
if(body == NULL)
return(SYSCALL_FAILURE);
switch(body->type) {
case TYPEMULTIPART:
return(SYSCALL_SUCCESS);
break;
case TYPEMESSAGE:
contents = cpystr((char *) body->contents.msg.text);
break;
case TYPETEXT:
case TYPEIMAGE:
case TYPEAUDIO:
case TYPEAPPLICATION:
case TYPEOTHER:
default:
contents = cpystr((char *) body->contents.text);
break;
}
mime_handler = get_handler(body->type, body->subtype);
if(((is_extmail(body)) == TRUE) || ((is_ftp(body)) == TRUE))
internal = TRUE;
if((mime_handler != NULL) && (internal == FALSE))
view = mime_handler->view;
if(view == NULL)
view = cpystr(internal_viewer);
push_cursor(WATCH_CURSOR);
bodylength = body->size.bytes;
switch(body->encoding) {
case ENCBASE64:
decode = (char *) rfc822_base64((unsigned char *) contents,
bodylength,&newlength);
break;
case ENCQUOTEDPRINTABLE:
decode = (char *) rfc822_qprint((unsigned char *) contents,
bodylength,&newlength);
break;
case ENCOTHER:
case ENC7BIT:
case ENC8BIT:
case ENCBINARY:
default:
break;
}
if(contents) {
stripcr(contents);
bodylength = strlen(contents);
}
else
bodylength = 0L;
if((body->type == TYPETEXT)
&& ((strcmp(view,internal_viewer)) == STRMATCH)
&& (saveonly == FALSE)) {
if(((strcasecmp(body->subtype,"plain")) == STRMATCH)
&& ((decode != NULL) || (contents != NULL)))
display_text(w, NULL, NULL, (decode) ? decode : contents,
DOCTYPE_PLAIN);
if(decode)
fs_give((void **) &decode);
if(contents)
fs_give((void **) &contents);
pop_cursor();
return(SYSCALL_SUCCESS);
}
if(internal == TRUE && (saveonly == FALSE)) {
if((is_extmail(body)) == TRUE)
get_external(body,contents);
if((is_ftp(body)) == TRUE)
fetch(body);
}
else {
if((preferences.mime_ask == TRUE) && (saveonly == FALSE))
command = input_string(w,MLGetLocalized(XtNmsgInputCommand,
MsgInputCommand),
view, MIMEASKHELPFILE);
else
command = cpystr((view) ? view : EMPTYSTR );
if((command != NULL) && (*command == NUL_TERM))
fs_give((void **) &command);
if((command != NULL) && (saveonly == FALSE)) {
if((strcmp(command,internal_viewer)) == STRMATCH) {
if((decode != NULL) && (strlen(decode) == newlength))
display_text(w, NULL, NULL, decode, DOCTYPE_PLAIN);
else
if((contents != NULL) && (strlen(contents) == bodylength))
display_text(w, NULL, NULL, contents, DOCTYPE_PLAIN);
}
else {
if((write_to_pipe(command, NULL,
(decode) ? decode : contents,
(decode) ? newlength : bodylength))
!= SYSCALL_SUCCESS)
errors ++;
}
}
else {
/* No viewer was found, selected, or (saveonly == TRUE) */
command = file_select(w,NULL, NULL, NULL, TRUE, &append_it);
if(command) {
errors += save_to_file(command,(decode) ? decode : contents,
(decode) ? newlength : bodylength, append_it);
if(errors)
mm_log(MLGetLocalized(XtNmsgSaveFail,MsgSaveFail), WARN);
else
mm_log(MLGetLocalized(XtNmsgSaveSuccess,MsgSaveSuccess), NIL);
}
else
errors ++;
}
}
if(command)
fs_give((void **) &command);
if(decode)
fs_give((void **) &decode);
if(contents)
fs_give((void **) &contents);
pop_cursor();
return((errors) ? SYSCALL_FAILURE : SYSCALL_SUCCESS);
}
#ifdef __STDC__
int save_to_file(char *filename,char *str,unsigned long length, Boolean append)
#else
int save_to_file(filename,str,length,append)
char *filename;
char *str;
unsigned long length;
Boolean append;
#endif
{
FILE *fp;
int errors = 0;
if(filename == NULL)
return(1);
if((fp = fopen(filename,(append == TRUE) ? "a" : "w")) == NULL)
return(1);
(void) chmod(filename,S_IRWXU);
if(fwrite(str,length,1,fp) != 1)
errors ++;
if(fclose(fp))
errors ++;
return(errors);
}
#ifdef __STDC__
Binary_Buffer *create_mime_compose_window(Widget w, BODY *body)
#else
Binary_Buffer *create_mime_compose_window(w, body)
Widget w;
BODY *body;
#endif
{
Mime_Handler *mime_handler = NULL;
Binary_Buffer *binary_buffer = NULL;
char *command = NULL;
Arg args[ARGLISTSIZE];
int n = 0;
char *ptr;
Boolean valid = FALSE;
Widget form, menubar;
XtTranslations translations;
Mime_Attach *mime_attach = (Mime_Attach *) fs_get(sizeof(Mime_Attach));
push_cursor(PIRATE_CURSOR);
mime_attach->done = 0;
mime_attach->compose = NULL;
if(preferences.autoPlace == TRUE) {
XtSetArg(args[n], XtNx, -1000); n ++;
XtSetArg(args[n], XtNy, -1000); n ++;
}
XtSetArg (args[n], XmNdeleteResponse, XmDO_NOTHING); n++;
mime_attach->shell = XtCreatePopupShell("mimecompose_shell",
topLevelShellWidgetClass, w,
args, n);
n = 0;
AddDestroyCallback (mime_attach->shell);
setup_editres(mime_attach->shell);
if(pirate_icon != (Pixmap) None)
XtVaSetValues(mime_attach->shell,
XmNiconPixmap,pirate_icon,
NULL);
form = XmCreateForm(mime_attach->shell, "mimecompose_form", args, n ); n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n ++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n ++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;
menubar = XmCreateMenuBar(form,"mimecompose_menubar", args, n); n = 0;
XtManageChild(menubar);
create_buttons(NULL, menubar, mimecompose_menu,
XtNumber(mimecompose_menu),
BTN_ON, (XtPointer) mime_attach, ROOTMENULEVEL);
mime_attach->desc_text =
create_text_field(form, menubar, "mimecompose_description",
body->description,0, NULL, NULL);
if(body->description)
fs_give((void **) &body->description);
XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmSTATIC); n ++;
XtSetArg(args[n], XmNlistSizePolicy,XmCONSTANT); n ++;
XtSetArg(args[n], XmNselectionPolicy,XmSINGLE_SELECT); n ++;
mime_attach->type_list
= XmCreateScrolledList(form,"mimecompose_list",args,n); n = 0;
XtAddCallback(mime_attach->type_list,
XmNsingleSelectionCallback,
(XtCallbackProc) mimecompose_select, mime_attach);
XtAddCallback(mime_attach->type_list,
XmNdefaultActionCallback,
(XtCallbackProc) mimecompose_accept, mime_attach);
translations =
XtParseTranslationTable(GLOBAL_modal_list_translations);
XtOverrideTranslations(mime_attach->type_list,translations);
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
XtSetArg(args[n], XmNtopWidget, mime_attach->desc_text); n ++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n ++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n ++;
XtSetValues(XtParent(mime_attach->type_list), args, n); n = 0;
(void) stuff_composelist(mime_attach->type_list);
XtManageChild(mime_attach->type_list);
XtManageChild(form);
XtManageChild(mime_attach->shell);
XtPopup(mime_attach->shell, XtGrabExclusive);
position_popup_widget(mime_attach->shell, TRUE);
modal_main_loop(&mime_attach->done);
XDefineCursor(display, XtWindow(mime_attach->shell), clock_cursor);
body->description = XmTextGetString(mime_attach->desc_text);
if((mime_attach->compose != NULL) && (*mime_attach->compose != NUL_TERM)) {
body->type = name_to_type(mime_attach->compose);
ptr = strchr(mime_attach->compose,TYPE_SEPARATOR_CHAR);
if(ptr) {
body->subtype = cpystr(ptr + 1);
valid = TRUE;
}
}
if(mime_attach->compose)
fs_give((void **) &mime_attach->compose);
if(valid) {
for(mime_handler = session->mime_handlers;
mime_handler; mime_handler = mime_handler->next) {
if((mime_handler->type == body->type) &&
((strcasecmp(mime_handler->subtype,body->subtype)) == STRMATCH))
command = mime_handler->compose;
}
}
if(command && *command)
binary_buffer = read_from_pipe(command);
XUndefineCursor(display, XtWindow(mime_attach->shell));
XtPopdown(mime_attach->shell);
XtDestroyWidget(mime_attach->shell);
fs_give((void **) &mime_attach);
pop_cursor();
return(binary_buffer);
}
#ifdef __STDC__
void stuff_composelist(Widget w)
#else
void stuff_composelist(w)
Widget w;
#endif
{
Mime_Handler *mime_handler;
char buff[FILEBUFFLEN];
XmString xstr;
for(mime_handler = session->mime_handlers;
mime_handler; mime_handler = mime_handler->next) {
if(mime_handler->compose && *mime_handler->compose) {
sprintf(buff,"%s/%s",type_to_name(mime_handler->type),
mime_handler->subtype);
xstr = XmStringCreateSimple(buff);
XmListAddItemUnselected(w,xstr,0);
XmStringFree(xstr);
}
}
return;
}
#ifdef __STDC__
void mimecompose_select(Widget w,Mime_Attach *mime_attach,
XmListCallbackStruct *xp)
#else
void mimecompose_select(w,mime_attach,xp)
Widget w;
Mime_Attach *mime_attach;
XmListCallbackStruct *xp;
#endif
{
char *str;
XmStringGetLtoR(xp->item,XmSTRING_DEFAULT_CHARSET,&str);
mime_attach->compose = str;
mime_attach->done = MODAL_LOOP_DONE;
return;
}
#ifdef __STDC__
void mimecompose_accept(Widget w, Mime_Attach *mime_attach, XtPointer xp)
#else
void mimecompose_accept(w,mime_attach,xp)
Widget w;
Mime_Attach *mime_attach;
XtPointer xp;
#endif
{
/* NOTREACHED */
}
#ifdef __STDC__
void mimecompose_cancel(Widget w, Mime_Attach *mime_attach, XtPointer xp)
#else
void mimecompose_cancel(w,mime_attach,xp)
Widget w;
Mime_Attach *mime_attach;
XtPointer xp;
#endif
{
fs_give((void **) &mime_attach->compose);
mime_attach->compose = NULL;
mime_attach->done = MODAL_LOOP_DONE;
return;
}
#ifdef __STDC__
void mimecompose_help(Widget w, Mime_Attach *mime_attach, XtPointer xp)
#else
void mimecompose_help(w,mime_attach,xp)
Widget w;
Mime_Attach *mime_attach;
XtPointer xp;
#endif
{
help(mime_attach->shell,MIMECOMPOSEHELPFILE);
return;
}
#ifdef __STDC__
char *find_param(BODY *body, char *s)
#else
char *find_param(body,s)
BODY * body;
char *s;
#endif
{
struct mail_body_parameter * params;
for (params = body->parameter; params != NULL; params = params->next) {
if (!strcasecmp(s, params->attribute)) {
return(params->value);
}
}
return(NULL);
}
#ifdef __STDC__
Boolean is_extmail(BODY *body)
#else
Boolean is_extmail(body)
BODY * body;
#endif
{
char * tmp = NULL; /* don't free */
if((body->type == TYPEMESSAGE) &&
(strcasecmp(body->subtype,"external-body") == 0)
&& ((tmp = find_param(body,"access-type")) != NULL)
&& ((strcasecmp(tmp,"mail-server")) == 0))
return(TRUE);
else
return(FALSE);
}
#ifdef __STDC__
Boolean is_ftp(BODY *body)
#else
Boolean is_ftp(body)
BODY * body;
#endif
{
char * tmp = NULL; /* don't free */
if((body->type == TYPEMESSAGE) &&
(strcasecmp(body->subtype,"external-body") == 0)
&& ((tmp = find_param(body,"access-type")) != NULL)
&& (((strcasecmp(tmp,"anon-ftp")) == 0)
|| ((strcasecmp(tmp,"ftp")) == 0)))
return(TRUE);
else
return(FALSE);
}
#ifdef __STDC__
void get_external(BODY *body, char *text)
#else
void get_external(body, text)
BODY *body;
char *text;
#endif
{
char **mailhosts = NULL;
BODY *newbody;
ADDRESS *address = NULL;
SMTPSTREAM *stream;
char * tmp = NULL; /* don't free */
char *from = NULL;
char *host = NULL;
char log[FILEBUFFLEN];
ENVELOPE *envelope = mail_newenvelope();
mailhosts = (char **) fs_get( 2 * sizeof(char *));
mailhosts[0] = cpystr(preferences.smtp_server);
mailhosts[1] = NULL;
from = cpystr(preferences.reply_address);
host = cpystr(preferences.default_domain);
if((stream = smtp_open(mailhosts,0L)) != NIL) {
newbody = mail_newbody();
newbody->contents.text = (text != NULL)
? (unsigned char *) cpystr(text) : (unsigned char *) cpystr(EMPTYSTR);
newbody->type = TYPETEXT;
newbody->encoding = ENC7BIT;
tmp = find_param(body,"server");
if(tmp) {
rfc822_parse_adrlist(&address, tmp, EMPTYSTR);
envelope->to = address;
envelope->from = text_to_address(from,host);
envelope->reply_to = copy_address(envelope->from);
envelope->return_path = mail_newaddr();
envelope->return_path->mailbox = cpystr(envelope->from->mailbox);
envelope->return_path->host = cpystr(envelope->from->host);
envelope->sender = mail_newaddr();
envelope->sender->mailbox = cpystr(local_auth.username);
envelope->sender->host = cpystr(local_auth.hostname);
envelope->subject = cpystr(EMPTYSTR);
envelope->message_id = (char *) fs_get(MESSAGE_ID_LENGTH);
sprintf(envelope->message_id,"<%s-%s.%d.%d.%s@%s>",
PROGRAM, MLVERSION, time(NULL), rand() % 10000,
local_auth.username, local_auth.hostname);
envelope->date = (char *) fs_get(MESSAGE_ID_LENGTH);
rfc822_date(envelope->date);
if (smtp_mail (stream,"MAIL",envelope,newbody)) {
sprintf(log,MLGetLocalized(XtNmsgFetchMail,MsgFetchMail),tmp);
mm_log(log, NIL);
smtp_close(stream);
}
else
mm_log(MLGetLocalized(XtNmsgFetchMailFail,MsgFetchMailFail),WARN);
mail_free_body(&newbody);
mail_free_envelope(&envelope);
}
}
if(tmp)
fs_give((void **) &tmp);
if(host)
fs_give((void **) &host);
if(from)
fs_give((void **) &from);
fs_give((void **) &mailhosts[0]);
fs_give((void **) &mailhosts);
}
#ifdef __STDC__
int do_ftp(char *site, char *user, char *pass, char *directory,
char *mode,char *name, char *filename)
#else
int do_ftp(site,user,pass,directory,mode,name,filename)
char *site;
char *user;
char *pass;
char *directory;
char *mode;
char *name;
char *filename;
#endif
{
FILE *p;
if(!name || !site || *name == '\0' || *user == '\0' || *pass == '\0')
return(EOF);
if((p = popen("ftp -n","w")) == NULL)
return(EOF);
if((fprintf(p,"open %s\n",site)) == EOF)
return(EOF);
if((fprintf(p,"user %s %s\n", user,pass)) == EOF)
return(EOF);
if(mode)
if((fprintf(p,"type %s\n",mode)) == EOF)
return(EOF);
if(directory)
if((fprintf(p,"cd %s\n",directory)) == EOF)
return(EOF);
if((fprintf(p,"get %s %s\n",name,filename)) == EOF)
return(EOF);
if((fprintf(p,"quit\n")) == EOF)
return(EOF);
if(pclose(p))
return(EOF);
return(0);
}
#ifdef __STDC__
void fetch(BODY *body)
#else
void fetch(body)
BODY *body;
#endif
{
char buffer[FILEBUFFLEN];
char *tmp = NULL;
char *site = NULL;
char *directory = NULL;
char *mode = NULL;
char *name = NULL;
Remote_Auth *remote_auth = NULL;
struct stat sb;
char user[FILEBUFFLEN];
char pass[FILEBUFFLEN];
char *filename = NULL;
char *pw = NULL;
Boolean anon = FALSE;
Boolean failed = FALSE;
Boolean append_it;
if(body == NULL)
return;
if((tmp = find_param(body,"access-type")) == NULL)
return;
if((strcasecmp(tmp,"anon-ftp")) == 0)
anon = TRUE;
else {
if((strcasecmp(tmp,"ftp")) == 0)
anon = FALSE;
else
return;
}
mm_log(MLGetLocalized(XtNmsgFetchFTP,MsgFetchFTP), NIL);
tmp = find_param(body,"name");
if(tmp)
name = cpystr(tmp);
filename = file_select(session->shell, NULL, NULL, name, TRUE, &append_it);
if(filename == NULL)
return;
tmp = find_param(body,"site");
if(tmp)
site = cpystr(tmp);
tmp = find_param(body,"directory");
if(tmp)
directory = cpystr(tmp);
tmp = find_param(body,"mode");
if(tmp)
mode = cpystr(tmp);
if(anon) {
strcpy(user,"anonymous");
sprintf(pass,"%s@%s",local_auth.username,local_auth.hostname);
}
else {
remote_auth = login(session->shell, NULL, "FTP", "FTP",
local_auth.username, NULL);
if((remote_auth == NULL)
|| (remote_auth->username == NULL)
|| (remote_auth->password == NULL)) {
free_remote_auth(remote_auth);
failed = TRUE;
}
else {
strcpy(user,remote_auth->username);
pw = scramble(remote_auth->password);
strcpy(pass,pw);
wipeout(pw);
}
}
if(!failed)
if(do_ftp(site,user,pass,directory,mode,name,filename))
failed = TRUE;
if(failed)
mm_log(MLGetLocalized(XtNmsgFTPFail,MsgFTPFail), WARN);
else {
if(stat(filename,&sb))
mm_log(MLGetLocalized(XtNmsgFTPFail,MsgFTPFail), WARN);
else {
sprintf(buffer,MLGetLocalized(XtNmsgFTPSaved,MsgFTPSaved),
sb.st_size,filename);
mm_log(buffer,NIL);
}
}
if(site)
fs_give((void **) &site);
if(directory)
fs_give((void **) &directory);
if(mode)
fs_give((void **) &mode);
if(name)
fs_give((void **) &name);
if(remote_auth)
free_remote_auth(remote_auth);
if(pw)
fs_give((void **) &pw);
free(filename);
return;
}
syntax highlighted by Code2HTML, v. 0.9.1