/* 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