/* mlcompose.c */

#include "ml.h"

char compose_address_translations[] = "<Key>Tab: complete-address()\n";

Menu compose_file_menu[] = {
  { NULL, "load_draft", NUL_TERM, 
      compose_load_draft, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "save_draft", NUL_TERM, 
      compose_save_draft, NULL, 0, NULL, NULL, BTN_ON },

  { NULL, "print", NUL_TERM, 
      compose_print, NULL, 0, NULL, NULL, BTN_ON },

  { NULL, "insert_file", NUL_TERM,
      compose_insert_file, NULL, 0, NULL, NULL, BTN_ON },

  { NULL, "address_book", NUL_TERM,
      main_address_book, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "note_book", NUL_TERM,
      main_note_book, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "close_window", NUL_TERM, 
      compose_destroy_current,   NULL, 0, NULL, NULL, BTN_ON },
};


Menu compose_attach_menu[] = {
  { NULL, "attach_file", NUL_TERM,
      compose_attach_file, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "attach_multi", NUL_TERM,
      compose_attach_multi, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "createattach", NUL_TERM,
      compose_createattach, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "delete_part", NUL_TERM,
      compose_delete_part, NULL, 0, 
      NULL, NULL, BTN_NOATTACH|BTN_NOATTACHSELECT },
};


Menu compose_edit_menu[] = {
  { NULL, "cut_text", NUL_TERM,
      compose_cut, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "copy_text", NUL_TERM,
      compose_copy, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "paste_text", NUL_TERM,
      compose_paste, NULL, 0, NULL, NULL, BTN_ON },


  { NULL, "spell", NUL_TERM,
      compose_spell, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "attach_menu", NUL_TERM,
      NULL, compose_attach_menu, XtNumber(compose_attach_menu),
      NULL, NULL, BTN_ON },
};


Menu compose_option_menu[] = {
  { NULL, "configure", NUL_TERM,
      compose_options, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "parameters", NUL_TERM,
      compose_parameters, NULL, 0, NULL, NULL, BTN_ON },
};


Menu compose_send_menu[] = {
  { NULL, "send_message", NUL_TERM, 
      send_message, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "send_autograph", BTN_ON,
      compose_send_autograph, NULL, 0, NULL, NULL, BTN_ON },
};


Menu compose_menu[] = {
  { NULL, "file_menu", NUL_TERM, 
      NULL, compose_file_menu, XtNumber(compose_file_menu), 
      NULL, NULL, BTN_ON },
  { NULL, "edit_menu", NUL_TERM, 
      NULL, compose_edit_menu, XtNumber(compose_edit_menu), 
      NULL, NULL, BTN_ON },
  { NULL, "option_menu", NUL_TERM,
      NULL, compose_option_menu, XtNumber(compose_option_menu), 
      NULL, NULL, BTN_ON },
  { NULL, "send_menu", NUL_TERM,
      NULL, compose_send_menu, XtNumber(compose_send_menu), 
      NULL, NULL, BTN_ON },
  { NULL, "HELP", NUL_TERM, 
      compose_help, NULL, 0,
      NULL, NULL, BTN_ON },
};

Button_Menu compose_secondbuttons[] = {
  { "compose_insert_file_btn",   compose_insert_file,     NULL, BTN_ON, NULL },
  { "compose_attach_file_btn",   compose_attach_file,     NULL, BTN_ON, NULL },
  { "compose_reply_insert_btn",  compose_reply_insert,    NULL, BTN_ON, NULL },
  { "compose_send_message_btn",  send_message,            NULL, BTN_ON, NULL },
  { "compose_cancel_btn",        compose_destroy_current, NULL, BTN_ON, NULL },
};




#ifdef __STDC__
void compose_check_buttons(MLCompose *compose)
#else
void compose_check_buttons(compose)
     MLCompose *compose;
#endif
{

  if(compose && compose->window)
    update_buttons(compose->window->buttonlist,
		   compose->window->buttonstate);
  return;

}


#ifdef __STDC__
void free_MLCompose(MLCompose *compose)
#else
void free_MLCompose(compose)
     MLCompose *compose;
#endif
{
  if(compose != NULL) {
    free_buttonlist(compose->window->buttonlist);
    fs_give((void **) &compose->window);
    free_MLComposeOptions(compose->options);
    compose->options = NULL;
    if(compose->in_reply_to != NULL)
      fs_give((void **) &compose->in_reply_to);
    if(compose->reply_text != NULL)
      fs_give((void **) &compose->reply_text);
    if(compose->remail_header != NULL)
      fs_give((void **) &compose->remail_header);
    fs_give((void **) &compose);
  }
  return;
}


#ifdef __STDC__
void free_MLComposeOptions(MLComposeOptions *options)
#else
void free_MLComposeOptions(options)
     MLComposeOptions *options;
#endif
{
  if(options) {
    if(options->window) {
      if(options->window->buttonlist)
	fs_give((void **) &options->window->buttonlist);
      fs_give((void **) &options->window);
    }
    if(options->mailhost)
      fs_give((void **) &options->mailhost);
    if(options->nntphost)
      fs_give((void **) &options->nntphost);
    if(options->domain)
      fs_give((void **) &options->domain);
    if(options->replyto)
      fs_give((void **) &options->replyto);
    if(options->charset)
      fs_give((void **) &options->charset);
    if(options->sigfile)
      fs_give((void **) &options->sigfile);
    if(options->language)
      fs_give((void **) &options->language);
    if(options->outlog)
      fs_give((void **) &options->outlog);
    if(options->defcc)
      fs_give((void **) &options->defcc);
    if(options->defbcc)
      fs_give((void **) &options->defbcc);
    if(options->prefix)
      fs_give((void **) &options->prefix);


    fs_give((void **) &options);
  }
  return;
}


#ifdef __STDC__
MLComposeWindow *new_MLComposeWindow(void)
#else
MLComposeWindow *new_MLComposeWindow()
#endif
{
  MLComposeWindow *composewindow = (MLComposeWindow *)
    fs_get(sizeof(MLComposeWindow));
  composewindow->buttonlist = NULL;
  composewindow->attach_list_selection = 0;
  composewindow->toggled_newsgroups = FALSE;
  composewindow->toggled_bcc = FALSE;
  return(composewindow);
}

#ifdef __STDC__
MLComposeOptions *new_MLComposeOptions(void)
#else
MLComposeOptions *new_MLComposeOptions()
#endif
{
  MLComposeOptions *options = (MLComposeOptions *)
    fs_get(sizeof(MLComposeOptions));
  options->window          = NULL;
  options->verbose         = preferences.smtp_debug;
  options->keep_open       = preferences.keep_open;
  options->send_eight      = preferences.send_eight;
  options->word_wrap       = preferences.word_wrap;
  options->message_log     = preferences.logit;
  options->log_attachments = preferences.log_full;
  options->verbose_tmp         = preferences.smtp_debug;
  options->keep_open_tmp       = preferences.keep_open;
  options->send_eight_tmp      = preferences.send_eight;
  options->word_wrap_tmp       = preferences.word_wrap;
  options->message_log_tmp     = preferences.logit;
  options->log_attachments_tmp = preferences.log_full;



  options->mailhost        = cpystr(preferences.smtp_server);
  options->nntphost        = cpystr(preferences.nntp_server);
  options->domain          = cpystr(preferences.default_domain);
  options->replyto         = cpystr(preferences.reply_address);
  options->charset         = cpystr(preferences.charset);
  options->sigfile         = cpystr(preferences.signature_file);
  options->language        = cpystr(preferences.language);
  options->outlog          = cpystr(preferences.sendlog);
  options->defcc           = cpystr(preferences.default_cc);
  options->defbcc          = cpystr(preferences.default_bcc);
  options->prefix          = cpystr(preferences.reply_prefix);
  return(options);
}


#ifdef __STDC__
MLCompose *new_MLCompose(void)
#else
MLCompose *new_MLCompose()
#endif
{
  MLCompose *mlcompose = (MLCompose *) fs_get(sizeof(MLCompose));
  mlcompose->window         = new_MLComposeWindow();
  mlcompose->options        = new_MLComposeOptions();
  mlcompose->spellwindow    = NULL; 
  mlcompose->compose_type   = COMPOSE_NEW;
  mlcompose->message        = NULL;
  mlcompose->in_reply_to    = NULL;
  mlcompose->reply_text     = NULL;
  mlcompose->remail_header  = NULL;
  mlcompose->in_ispell      = FALSE;
  mlcompose->has_newsgroups = FALSE;
  mlcompose->parameters     = NULL;
  mlcompose->attachments    = NULL;
  mlcompose->part_list      = NULL;
  mlcompose->next           = NULL;
  mlcompose->prev           = NULL;

  return(mlcompose);
}


#ifdef __STDC__
void compose_message(Compose_Type type, Boolean news, 
			      Lview *lview, Message *message, 
			      Read_Info *read_info)
#else
void compose_message(type, news, lview, message, read_info)
     Compose_Type type;
     Boolean news;
     Lview *lview;
     Message *message;
     Read_Info *read_info;

#endif
{
  MLCompose *compose = new_MLCompose();

  /* Create Window */

  create_compose_window(compose, type, news);

  /* Link to session */

  if(session->compose) {
    compose->next = session->compose;
    session->compose->prev = compose;
  }
  session->compose = compose;

  /* Set initial options. */

  /* Get|Set known message values. */

  if(read_info != NULL)
    load_compose_from_read(compose, type, news, read_info);
  else
    load_compose_info(compose, type, news, lview, message);

  if(compose->attachments == NULL) { 
    compose->window->buttonstate |= BTN_NOATTACH;
    compose->window->buttonstate |= BTN_NOATTACHSELECT;
    
  }
  else
    make_attachment_list(compose, TRUE);

  compose_set_window_title(compose,type,news,lview,message,read_info);
  compose_check_buttons(compose);
  return;
}


#ifdef __STDC__
void create_compose_window(MLCompose *compose, Compose_Type type, Boolean news)
#else
void create_compose_window(compose, type, news)
     MLCompose *compose;
     Compose_Type type;
     Boolean news;
#endif
{

  Arg args[ARGLISTSIZE];
  int n = 0;
  XtTranslations translations;

  XtSetArg (args[n], XmNdeleteResponse, XmDO_NOTHING); n++;           
  compose->window->shell = XtCreatePopupShell("compose",
					      topLevelShellWidgetClass,
					      session->shell,
					      NULL, 0);
  AddDestroyCallback (compose->window->shell);
  setup_editres(compose->window->shell);

  if(compose_icon != (Pixmap) None)
    XtVaSetValues(compose->window->shell,
		  XmNiconPixmap,compose_icon,
		  NULL);

  compose->window->form = XmCreateForm(compose->window->shell,"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 ++;
  compose->window->menubar = XmCreateMenuBar(compose->window->form,"menubar", 
					     args, n); 
  n = 0;
  XtManageChild(compose->window->menubar);
  make_buttons(&compose->window->buttonlist,
	       NULL, compose->window->menubar,
	       compose_menu, XtNumber(compose_menu), 
	       BTN_ON, (XtPointer) compose, ROOTMENULEVEL);



  XtSetArg(args[n], XmNadjustMargin, FALSE);      n ++;
  XtSetArg(args[n], XmNmarginWidth, 0);           n ++;
  XtSetArg(args[n], XmNborderWidth, 0);           n ++;


  XtSetArg(args[n], XmNtopAttachment,   XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->menubar);  n ++;
  XtSetArg(args[n], XmNleftAttachment,  XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n ++;
  XtSetArg(args[n], XmNpacking, XmPACK_COLUMN); n ++;
  compose->window->rowcol 
    = XmCreateRowColumn(compose->window->form, "rowcol", args, n); n = 0;
  XtManageChild(compose->window->rowcol);

  make_secondbuttons(compose->window->buttonlist,
		     compose->window->rowcol, 
		     compose_secondbuttons, XtNumber(compose_secondbuttons),
		     BTN_ON, (XtPointer) compose);

  XtSetArg(args[n], XmNtraversalOn, FALSE); n ++;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->rowcol); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNborderWidth, 0); n ++;
  compose->window->to_button 
    = XmCreatePushButton(compose->window->form,"to_button", args, n); 
  n = 0;


  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->rowcol); n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNleftWidget,compose->window->to_button); n ++;
  compose->window->to_text = XmCreateTextField(compose->window->form,
					       "to_textfld", args, n); n = 0;

  XtSetArg(args[n], XmNtraversalOn, FALSE); n ++;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->rowcol); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNborderWidth, 0); n ++;
  compose->window->newsgroups_button 
    = XmCreatePushButton(compose->window->form,"newsgroups_button", 
			 args, n); 
  n = 0;

  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->rowcol); n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNleftWidget,compose->window->newsgroups_button); n ++;
  compose->window->newsgroups_text 
    = XmCreateTextField(compose->window->form,"newsgroups_textfld", 
			args, n); n = 0;

  translations = XtParseTranslationTable(GLOBAL_text_field_translations);
  XtOverrideTranslations(compose->window->to_text,translations);
  translations = 
    XtParseTranslationTable(GLOBAL_nonterminal_text_field_translations);
  XtOverrideTranslations(compose->window->to_text,translations);
  translations =
    XtParseTranslationTable(compose_address_translations);
  XtOverrideTranslations(compose->window->to_text,translations);


  XtAddCallback(compose->window->to_text, XmNmodifyVerifyCallback, 
		(XtCallbackProc) text_field_edit, NULL);

  translations = XtParseTranslationTable(GLOBAL_text_field_translations);
  XtOverrideTranslations(compose->window->newsgroups_text,translations);
  translations = 
    XtParseTranslationTable(GLOBAL_nonterminal_text_field_translations);
  XtOverrideTranslations(compose->window->newsgroups_text,translations);
  translations =
    XtParseTranslationTable(compose_address_translations);
  XtOverrideTranslations(compose->window->newsgroups_text,translations);


  XtAddCallback(compose->window->newsgroups_text, XmNmodifyVerifyCallback, 
		(XtCallbackProc) text_field_edit, NULL);

  XtAddCallback(compose->window->newsgroups_button, XmNactivateCallback,
		(XtCallbackProc) compose_toggle_news, compose );

  XtAddCallback(compose->window->to_button, XmNactivateCallback,
		(XtCallbackProc) compose_toggle_news, compose );

  if(news == TRUE) {
    XtManageChild(compose->window->newsgroups_button);
    XtManageChild(compose->window->newsgroups_text);
    compose->window->toggled_newsgroups = TRUE;
  }
  else {
    XtManageChild(compose->window->to_button);
    XtManageChild(compose->window->to_text);
    compose->window->toggled_newsgroups = FALSE;
  }

  XtSetArg(args[n], XmNtraversalOn, FALSE); n ++;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->to_text); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNborderWidth, 0); n ++;

  compose->window->subject_button = 
    XmCreatePushButton(compose->window->form,"subject_button", args, n); n = 0;
  XtManageChild(compose->window->subject_button);

  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->to_text); n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNleftWidget,compose->window->subject_button); n ++;
  compose->window->subject_text = XmCreateTextField(compose->window->form,
						    "subject_textfld", 
						    args, n);
  n = 0;
  XtManageChild(compose->window->subject_text);

  translations = XtParseTranslationTable(GLOBAL_text_field_translations);
  XtOverrideTranslations(compose->window->subject_text,translations);
  translations = 
    XtParseTranslationTable(GLOBAL_nonterminal_text_field_translations);
  XtOverrideTranslations(compose->window->subject_text,translations);

  XtAddCallback(compose->window->subject_text, XmNmodifyVerifyCallback, 
		(XtCallbackProc) text_field_edit, NULL);

  XtAddCallback(compose->window->subject_button, XmNactivateCallback,
		(XtCallbackProc) generate_subject, compose );

  XtSetArg(args[n], XmNtraversalOn, FALSE); n ++;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->subject_text); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNborderWidth, 0); n ++;

  compose->window->cc_button = XmCreatePushButton(compose->window->form,
						  "cc_button", args, n); 
  n = 0;

  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->subject_text); n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNleftWidget,compose->window->cc_button); n ++;
  compose->window->cc_text = XmCreateTextField(compose->window->form,
					       "cc_textfld", args, n); n = 0;

  translations = XtParseTranslationTable(GLOBAL_text_field_translations);
  XtOverrideTranslations(compose->window->cc_text,translations);
  translations = 
    XtParseTranslationTable(GLOBAL_nonterminal_text_field_translations);
  XtOverrideTranslations(compose->window->cc_text,translations);
  translations =
    XtParseTranslationTable(compose_address_translations);
  XtOverrideTranslations(compose->window->cc_text,translations);

  XtAddCallback(compose->window->cc_text, XmNmodifyVerifyCallback, 
		(XtCallbackProc) text_field_edit, NULL);

  XtSetArg(args[n], XmNtraversalOn, FALSE); n ++;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET);   n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->subject_text); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);    n ++;
  XtSetArg(args[n], XmNborderWidth, 0); n ++;

  compose->window->bcc_button = XmCreatePushButton(compose->window->form,
					   "bcc_button", args, n); 
  n = 0;

  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->subject_text); n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNleftWidget,compose->window->bcc_button); n ++;
  compose->window->bcc_text = XmCreateTextField(compose->window->form,
					"bcc_textfld", args, n); n = 0;

  translations = XtParseTranslationTable(GLOBAL_text_field_translations);
  XtOverrideTranslations(compose->window->bcc_text,translations);
  translations = 
    XtParseTranslationTable(GLOBAL_nonterminal_text_field_translations);
  XtOverrideTranslations(compose->window->bcc_text,translations);
  translations =
    XtParseTranslationTable(compose_address_translations);
  XtOverrideTranslations(compose->window->bcc_text,translations);

  XtAddCallback(compose->window->bcc_text, XmNmodifyVerifyCallback, 
		(XtCallbackProc) text_field_edit, NULL);

  XtAddCallback(compose->window->cc_button, XmNactivateCallback,
		(XtCallbackProc) compose_toggle_cc, compose );

  XtAddCallback(compose->window->bcc_button, XmNactivateCallback,
		(XtCallbackProc) compose_toggle_cc, compose );

  XtManageChild(compose->window->cc_button);
  XtManageChild(compose->window->cc_text);

  compose->window->toggled_bcc = FALSE;

  XtSetArg(args[n], XmNtraversalOn, FALSE); n ++;
  XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmSTATIC);       n ++;
  XtSetArg(args[n], XmNlistSizePolicy,XmCONSTANT);              n ++;
  XtSetArg(args[n], XmNselectionPolicy,XmSINGLE_SELECT);        n ++;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET);         n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->cc_text);    n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);          n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM);         n ++;
  compose->window->attach_list = 
    XmCreateScrolledList(compose->window->form,"partlist",args,n);      n = 0;
  XtManageChild(compose->window->attach_list); 



  XtAddCallback(compose->window->attach_list,
		XmNsingleSelectionCallback, 
		(XtCallbackProc) select_attach, compose);
  XtAddCallback(compose->window->attach_list,
		XmNdefaultActionCallback, 
		(XtCallbackProc) double_click_attach, compose);



  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET);         n ++;
  XtSetArg(args[n], XmNtopWidget, compose->window->attach_list);     n ++;
  XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM);        n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);          n ++;
  XtSetArg(args[n], XmNeditable, TRUE);                         n ++;
  XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT);            n ++;
  XtSetArg(args[n], XmNwordWrap, TRUE);                         n ++;
  XtSetArg(args[n], XmNcolumns,  COMPOSEWIDTH - 1);             n ++;
  XtSetArg(args[n], XmNscrollVertical, TRUE);                   n ++; 
  XtSetArg(args[n], XmNscrollHorizontal, FALSE );               n ++;

  compose->window->compose_text =
    XmCreateScrolledText(compose->window->form, "text", args, n);    n = 0;

  XtManageChild(compose->window->compose_text);
  
  translations = XtParseTranslationTable(GLOBAL_text_translations);
  XtOverrideTranslations(compose->window->compose_text, translations);

  translations = XtParseTranslationTable(GLOBAL_compose_pop_translations);
  XtOverrideTranslations(compose->window->compose_text, translations);

  XtAddCallback(compose->window->compose_text, XmNmodifyVerifyCallback, 
		(XtCallbackProc) text_hex_edit, NULL);

  XtAddCallback(compose->window->shell, XmNdestroyCallback, 
		(XtCallbackProc) compose_destroy, compose);

  XtManageChild(compose->window->form);
  XtManageChild(compose->window->shell);
  XtPopup(compose->window->shell, XtGrabNone);
  return;
}



#ifdef __STDC__
void compose_destroy_current(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_destroy_current(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  if(compose->in_ispell == TRUE)
    return;
  if(preferences.confirmDestroy == TRUE) 
    if((ml_confirm(compose->window->shell,
		   MLGetLocalized(XtNmsgDestroyEdit,
				  MsgDestroyEdit),CONFIRM_YES_NO)) == FALSE)
      return;
  compose_close_window(w,compose,xp);
  return;
}



#ifdef __STDC__
void compose_close_window(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_close_window(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  if(compose->in_ispell == TRUE)
    return;
  if(compose) {
    XtPopdown(compose->window->shell);
    XtDestroyWidget(compose->window->shell);
  }
  return;
}

#ifdef __STDC__
void compose_destroy(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_destroy(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  if(compose) {
    if(compose->next)
      compose->next->prev = compose->prev;
    if(compose == session->compose) 
      session->compose = compose->next;
    else
      compose->prev->next = compose->next;
    free_MLCompose(compose);
  }
  return;
}


#ifdef __STDC__
void compose_wrap_off(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_wrap_off(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  Arg args[ARGLISTSIZE];
  int n = 0;
  XtTranslations translations;
  char *str = NULL;

  if((compose != NULL) && (compose->options != NULL)) {
    compose->options->word_wrap = FALSE;

    str  = XmTextGetString(compose->window->compose_text);

    XtDestroyWidget(compose->window->compose_text);

    XtSetArg(args[n], XmNeditable, TRUE);                         n ++;
    XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT);            n ++;
    XtSetArg(args[n], XmNwordWrap, FALSE);                        n ++;
    XtSetArg(args[n], XmNcolumns,  COMPOSEWIDTH - 1);             n ++;
    XtSetArg(args[n], XmNscrollVertical, TRUE);                   n ++; 
    XtSetArg(args[n], XmNscrollHorizontal, TRUE );                n ++;

    compose->window->compose_text =
      XmCreateScrolledText(compose->window->form, "compose_text", args, n); 
    n = 0;
  
    translations = XtParseTranslationTable(GLOBAL_text_translations);
    XtOverrideTranslations(compose->window->compose_text, translations);

    XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET);         n ++;
    XtSetArg(args[n], XmNtopWidget, compose->window->attach_list);     n ++;
    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM);        n ++;
    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);          n ++;
    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM);         n ++;
    XtSetValues(XtParent(compose->window->compose_text), args, n); 
    n = 0;
    XtManageChild(compose->window->compose_text);

    XmTextSetString(compose->window->compose_text, str);
    fs_give((void **) &str);
  }
  return;
}

#ifdef __STDC__
void compose_wrap_on(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_wrap_on(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  Arg args[ARGLISTSIZE];
  int n = 0;
  char *str = NULL;
  XtTranslations translations;

  if((compose != NULL) && (compose->options != NULL)) {
    compose->options->word_wrap = TRUE;

    str  = XmTextGetString(compose->window->compose_text);

    XtDestroyWidget(compose->window->compose_text);


    XtSetArg(args[n], XmNeditable, TRUE);                         n ++;
    XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT);            n ++;
    XtSetArg(args[n], XmNwordWrap, TRUE);                         n ++;
    XtSetArg(args[n], XmNcolumns,  COMPOSEWIDTH - 1);             n ++;
    XtSetArg(args[n], XmNscrollVertical, TRUE);                   n ++; 
    XtSetArg(args[n], XmNscrollHorizontal, FALSE );               n ++;

    compose->window->compose_text =
      XmCreateScrolledText(compose->window->form,"compose_text", args, n);   
    n = 0;
  
    translations = XtParseTranslationTable(GLOBAL_text_translations);
    XtOverrideTranslations(compose->window->compose_text, translations);

    XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET);         n ++;
    XtSetArg(args[n], XmNtopWidget, compose->window->attach_list);     n ++;
    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM);        n ++;
    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);          n ++;
    XtSetArg(args[n], XmNrightAttachment, XmATTACH_NONE);         n ++;
    XtSetValues(XtParent(compose->window->compose_text), args, n);    n = 0;
    XtManageChild(compose->window->compose_text);

    XmTextSetString(compose->window->compose_text, str);
    fs_give((void **) &str);

    XtSetArg(args[n], XmNwordWrap, TRUE);                        n ++;
    XtSetArg(args[n], XmNscrollHorizontal, FALSE );                n ++;

    XtSetValues(compose->window->compose_text, args, n); n = 0;


    XtSetArg(args[n], XmNrightAttachment, XmATTACH_NONE);         n ++;
    XtSetValues(XtParent(compose->window->compose_text), args, n);  n = 0;
  }
  return;
}






#ifdef __STDC__
void compose_toggle_cc(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_toggle_cc(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  if(compose != NULL) {
    if(compose->window->toggled_bcc == TRUE) {
      compose->window->toggled_bcc = FALSE;
      XtUnmanageChild(compose->window->bcc_button);
      XtUnmanageChild(compose->window->bcc_text);
      XtManageChild(compose->window->cc_button);
      XtManageChild(compose->window->cc_text);
    }
    else {
      compose->window->toggled_bcc = TRUE;
      XtUnmanageChild(compose->window->cc_button);
      XtUnmanageChild(compose->window->cc_text);
      XtManageChild(compose->window->bcc_button);
      XtManageChild(compose->window->bcc_text);
    }
  }
  return;
}

#ifdef __STDC__
void compose_toggle_news(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_toggle_news(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  if(compose != NULL) {
    if(compose->window->toggled_newsgroups == TRUE) {
      compose->window->toggled_newsgroups = FALSE;
      XtUnmanageChild(compose->window->newsgroups_button);
      XtUnmanageChild(compose->window->newsgroups_text);
      XtManageChild(compose->window->to_button);
      XtManageChild(compose->window->to_text);
    }
    else { 
      compose->window->toggled_newsgroups = TRUE;
      XtUnmanageChild(compose->window->to_button);
      XtUnmanageChild(compose->window->to_text);
      XtManageChild(compose->window->newsgroups_button);
      XtManageChild(compose->window->newsgroups_text);
    }
  }
  return;
}


#ifdef __STDC__
void compose_set_window_title(MLCompose *compose, Compose_Type type,
			      Boolean news, Lview *lview, 
			      Message *message, Read_Info *read_info)
#else
void compose_set_window_title(compose, type, news, lview, message, read_info)
     MLCompose *compose;
     Compose_Type type;
     Boolean news;
     Lview *lview;
     Message *message;
     Read_Info *read_info;
#endif
{
  char *basestr = NULL;

  switch (type) {
  case COMPOSE_REPLY:
    basestr = MLGetLocalized(XtNmsgReplySender,MsgReplySender);
    break;
  case COMPOSE_REPLYALL:
    basestr = MLGetLocalized(XtNmsgReplyAll,MsgReplyAll);
    break;
  case COMPOSE_FORWARD:
  case COMPOSE_FORWARDATTACH:
    basestr = MLGetLocalized(XtNmsgForward,MsgForward);
    break;
  case COMPOSE_REMAIL:
    basestr = MLGetLocalized(XtNmsgRemail,MsgRemail);
    break;
  case COMPOSE_NEW:
  default:
    basestr = MLGetLocalized(XtNmsgComposeNew,MsgComposeNew);
    break;
  }

  XtVaSetValues(compose->window->shell,	XmNtitle, basestr, NULL);
  return;
}



/*
 * This is a fat function, and does almost all the dirty work. When it 
 * comes back, the message should be on its way.
 *
 * It takes a compose structure which
 * points to an open compose window, extracts the stuff from
 * the windows (which are presumably more current than the Compose_Info
 * structures), connects any attachments, and sends it off. It also 
 * determines whether it is appropriate to send news, mail or both.
 * On success, the current session is killed; the compose window is 
 * restored to any previous (or next) composings, or otherwise closes 
 * the window. On failure the message is left intact so the user can 
 * take some form of action to correct the condition, or perhaps save 
 * the work, or whatever.
 */

#ifdef __STDC__
void send_message(Widget w, MLCompose *compose, XtPointer xp)
#else
void send_message(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{

  SMTPSTREAM *smtp_stream = NIL;
  SMTPSTREAM *nntp_stream = NIL;
  ENVELOPE *envelope = NULL;
  ENVELOPE *news_envelope = NULL;
  BODY *body = NULL;
  BODY *currbody = NULL;
  PART *part = NULL;
  char **mailhosts = NULL;
  char **newshosts = NULL;
  Boolean intl     = FALSE;
  char *chk;
  unsigned long len;
  char *default_host = NULL;
  long openflags = 0L;
  struct mail_body_parameter *params;

  Boolean sendnews    = FALSE;
  Boolean sendmail    = TRUE;
  Boolean retry       = FALSE; 
  Boolean attach_text = FALSE;
  char *from          = NULL;
  char *to            = NULL;
  char *newsgroups    = NULL;
  char *subject       = NULL;
  char *cc            = NULL;
  char *bcc           = NULL;
  char *temp_text     = NULL;
  char *text          = NULL;
  char *fixed_subject = NULL;
  char msgno_string[32];

  int errors          = 0;

  if((CheckConnection()) == FALSE) {
    mm_log(MLGetLocalized(XtNmsgNotConnected,MsgNotConnected),WARN);
    return;
  }

  if((compose->options->domain != NULL) 
     && (*compose->options->domain != NUL_TERM))
    default_host = cpystr(compose->options->domain);
  else 
    default_host = cpystr(compose->options->mailhost);

  push_cursor(WATCH_CURSOR);

  if(! default_host)
    default_host = cpystr(EMPTYSTR);

  mailhosts = (char **) fs_get( 2 * sizeof(char *));
  mailhosts[0] = cpystr(compose->options->mailhost);
  mailhosts[1] = NULL;

  newshosts = (char **) fs_get( 2 * sizeof(char *));
  newshosts[0] = cpystr(compose->options->nntphost);
  newshosts[1] = NULL;

  from       = cpystr(compose->options->replyto);

  /* Collect message information from window. */

  newsgroups = GetTextField(compose->window->newsgroups_text);
  to         = GetTextField(compose->window->to_text);
  subject    = GetTextField(compose->window->subject_text);
  cc         = GetTextField(compose->window->cc_text);
  bcc        = GetTextField(compose->window->bcc_text);
  text       = XmTextGetString(compose->window->compose_text);



  /* See if the text has international chars we might need to encode */

  for(chk = text; (*chk != NUL_TERM) ; chk ++ ) 
    if(((unsigned char) *chk) >= (unsigned char) 0x80)
      intl = TRUE;

  /* 
   * Do two conversions. First translate paragraphs to LF terminated
   * text. Then turn LF to CRLF for sending. The reason for doing this
   * in two steps is so we can allocate approximately the right amount 
   * of space in each function, and not have to overkill the memory 
   * requirements. It also compartmentalizes the functionality.
   *
   */

  if(compose->options->word_wrap == TRUE)
    temp_text = wrap_text(text, COMPOSEWIDTH - 1);
  else
    temp_text = cpystr(text);
  fs_give((void **) &text);

  text = lftocrlf(temp_text);

  fs_give((void **) &temp_text);

  if((intl == TRUE) && (compose->options->send_eight == FALSE))
    temp_text = (char *)
      rfc822_8bit((unsigned char *) text, (unsigned long) strlen(text), &len);
  else
    temp_text = NULL;

  if((newsgroups != NULL) && (*(first_nonwhite(newsgroups)) != NUL_TERM))
    sendnews = TRUE;

  if(   (*(first_nonwhite(to))  == NUL_TERM) 
     && (*(first_nonwhite(cc))  == NUL_TERM) 
     && (*(first_nonwhite(bcc)) == NUL_TERM))
    sendmail = FALSE;

  /* No recipients. Make sure it fails. */
  if((sendnews == FALSE) && (sendmail == FALSE))
    retry = TRUE;

  if(compose->options->verbose == TRUE)
    openflags |= SOP_DEBUG;

  if(sendnews == TRUE) {
    nntp_stream = nntp_open(newshosts, openflags);
    if(nntp_stream == NIL) {
      mm_log(MLGetLocalized(XtNmsgOpenNNTPFailed,MsgOpenNNTPFailed),WARN);
      retry = TRUE;
    }
  }

  if(compose->options->send_eight == TRUE)
    openflags |= SOP_ESMTP;

  if(sendmail == TRUE) {
    smtp_stream = smtp_open(mailhosts, openflags);
    if(smtp_stream == NIL) {
      mm_log(MLGetLocalized(XtNmsgOpenSMTPFailed,MsgOpenSMTPFailed),WARN);
      retry = TRUE;
    }
  }

  /* 
   *  We've got our neccessary stream(s) open. Now build the message.
   */
 
  body = mail_newbody();
  envelope = mail_newenvelope();

  /* Don't send any empty multiparts. Won't work */

  fix_empty_attachments(compose);


  if(compose->attachments) {
    body->contents.part = compose->attachments;
    body->type = TYPEMULTIPART;
    body->encoding = ENC7BIT;

    /* 
     * Our text portion (if there is one)
     * will become the first part of the multipart 
     */
    
    if(*text != NUL_TERM) {
      attach_text = TRUE;
      part = mail_newbody_part();
      currbody = &(part->body);
      part->next = body->contents.part;
      body->contents.part = part;
    }
    else
      currbody = NULL;
  }
  else   /* Not a multipart. Just the text message. */
    currbody = body;


  /* 
   * Set the parameters for CHARSET and LANGUAGE in the parent
   * compose structure. We only want to do this once; and this function
   * might be re-entered if options->keep_open is set.
   */

  if(intl) {
    for(params = compose->parameters; params != NULL; params = params->next)
      if((params->attribute != NULL) 
	 && (strcasecmp(params->attribute,CHARSET_STR) == STRMATCH))
	break;

    /*
     * NULL params means "CHARSET" wasn't found, 
     * so initialize it (if not US-ASCII). 
     */

    if(params == NULL) {
      if((strcasecmp(compose->options->charset,US_ASCII_STR) != STRMATCH)) {
	params = mail_newbody_parameter();
	params->attribute = cpystr(CHARSET_STR);
	params->value = cpystr(compose->options->charset);
	if(compose->parameters)
	  params->next = compose->parameters;
	compose->parameters = params;
      }
    }
  }

  /* 
   *  Now do the same (though a bit different logic) for LANGUAGE.
   */

  if(*compose->options->language) {
    for(params = compose->parameters; params != NULL; params = params->next)
      if((params->attribute != NULL) 
	 && (strcasecmp(params->attribute,"LANGUAGE") == STRMATCH))
	break;

    if(params == NULL) {
      params = mail_newbody_parameter();
      params->attribute = cpystr("LANGUAGE");
      params->value = cpystr(compose->options->language);
      if(compose->parameters)
	params->next = compose->parameters;
      compose->parameters = params;
    }
  }

  /*
   * Attach our text to the body.
   * If we have 8-bit chars, we start off with 8bit encoding
   * for NNTP. After that's sent we'll have to check and perhaps
   * switch to Q-P if desired or the mailstream doesn't support it.
   */

  if(currbody) {
    currbody->type = TYPETEXT;
    currbody->encoding = (intl == TRUE) ? ENC8BIT : ENC7BIT;
    currbody->contents.text = (unsigned char *) text;
    currbody->size.bytes = (unsigned long) strlen((char *) text);
    currbody->parameter = copy_params(compose->parameters, intl);
  }


  /*
   * Now build the appropriate envelope(s).
   * Warning: The c-client appears to trash the calling text strings 
   * in rfc822_parse_addr(), which text_to_address() calls... So, don't 
   * parse the same  string twice to get a duplicate. Use copy_address() 
   * instead.
   */

  envelope->from        = text_to_address(from,default_host);
  envelope->to          = text_to_address(to,  default_host);
  envelope->cc          = text_to_address(cc,  default_host);
  envelope->bcc         = text_to_address(bcc, default_host);

  envelope->reply_to    = copy_address(envelope->from); 

  envelope->return_path = mail_newaddr();

  if(envelope->from) {
    if(envelope->from->mailbox)
      envelope->return_path->mailbox = cpystr(envelope->from->mailbox);
    if(envelope->from->host)
      envelope->return_path->host    = cpystr(envelope->from->host);
  }

  /* 
   * The sender field is our real bonafide Unix name.
   * This is done to thwart forgeries. This address is also used
   * for delivery bounces. Making certain that it can be used for
   * this purpose is a local system administration issue. That is,
   * if incoming SMTP is not allowed on the system, it should be 
   * MX forwarded to a mail hub which can process the errors.
   */

  if(compose->compose_type == COMPOSE_REMAIL) {
    envelope->remail = cpystr(compose->remail_header);
    body->type = TYPETEXT;
    if(body->subtype)
      fs_give((void **) &body->subtype);
    body->subtype = NULL;
    body->encoding = ENC7BIT;
  }
  else {
    envelope->sender = mail_newaddr();
    envelope->sender->mailbox = cpystr(local_auth.username);
    envelope->sender->host    = cpystr(local_auth.hostname);
  }

  envelope->subject     = cpystr(subject);

  if(compose->in_reply_to)
    envelope->in_reply_to = cpystr(compose->in_reply_to);


  /* 
   * Generate a message-id and date. Note that we also supply the
   * real Unix credentials here as the second stage of
   * forgery suppression. 
   */

  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);

  /* Allow legitimate schizoids (undocumented) */

  compose_smart_reply(envelope, newsgroups);

  
  if((sendnews == TRUE) && (nntp_stream != NIL)) {

    /* 
     * We create a separate envelope for news posts, containing
     * newsgroups line, and stripping out to,cc,bcc. We don't need to,
     * but partitioning like this will prevent older pine clients
     * from reporting "bogus newsgroups". The reasons are amusing if you
     * look closely at c-client/rfc822.c.
     */

    news_envelope = mail_newenvelope();
    news_envelope->from        = copy_address(envelope->from);
    news_envelope->reply_to    = copy_address(envelope->reply_to);
    news_envelope->return_path = copy_address(envelope->return_path);
    news_envelope->sender      = copy_address(envelope->sender);
    news_envelope->subject     = cpystr(envelope->subject);
    news_envelope->message_id  = cpystr(envelope->message_id);
    news_envelope->date        = cpystr(envelope->date);


    if(compose->in_reply_to) {
      news_envelope->in_reply_to 
	= (char *) fs_get((2 * strlen(compose->in_reply_to)) + 32);
      sprintf(news_envelope->in_reply_to,"%s\r\nReferences: %s",
	      compose->in_reply_to,
	      compose->in_reply_to);
    }

    news_envelope->newsgroups  = cpystr(newsgroups);
    errors = nntp_mail(nntp_stream, news_envelope, body);
    if(errors != T ) {
      mm_log(MLGetLocalized(XtNmsgSendNNTPFailed,MsgSendNNTPFailed),WARN);
      retry = TRUE;
    }
    else
      mm_log(MLGetLocalized(XtNmsgNewsSent,MsgNewsSent),NIL);
    smtp_close(nntp_stream);
  }

  if((sendmail == TRUE) && (smtp_stream != NIL)) {

    /* 
     * OK. Now we've got to see if 8-bit text is allowed, and otherwise
     * do some last minute patching of the message if it's got 8-bit
     * text.
     */

    if((intl == TRUE) && (compose->options->send_eight == TRUE)) {
      if(! smtp_stream->ok_8bitmime) {
	mm_log(MLGetLocalized(XtNmsgSendEightFailure,MsgSendEightFailure),
	       WARN);
	compose->options->send_eight = FALSE;
      }
    }

    if(compose->options->send_eight == TRUE)
      fs_give((void **) &temp_text);   /* Well, we sure don't need you now */
    else  {
      if((intl == TRUE) && (currbody != NULL)) {
	if (temp_text == NULL)
	  temp_text = (char *)
	  rfc822_8bit((unsigned char *) text, 
		      (unsigned long) strlen(text), &len);
	currbody->encoding = ENCQUOTEDPRINTABLE;
	fs_give((void **) &text);
	currbody->contents.text = (unsigned char *) temp_text;
	currbody->size.bytes = (unsigned long) strlen((char *) temp_text);
      }
      iso_encode_address(envelope->from);
      iso_encode_address(envelope->to);
      iso_encode_address(envelope->cc);
      iso_encode_address(envelope->bcc);
      iso_encode_address(envelope->reply_to);
      iso_encode_address(envelope->return_path);
      if(envelope->subject)
	fs_give((void **) &envelope->subject);
      envelope->subject = iso_encode_subject(subject);
    }

    errors = smtp_mail(smtp_stream,"MAIL", envelope, body);
    if(errors != T ) {
      mm_log(MLGetLocalized(XtNmsgSendSMTPFailed,MsgSendSMTPFailed),WARN);
      retry = TRUE;
    }
    else 
      mm_log(MLGetLocalized(XtNmsgMailSent,MsgMailSent),NIL);
    smtp_close(smtp_stream);
  }

  if(retry == FALSE) {
    if((sendnews == TRUE) && (sendmail == TRUE))
      envelope->newsgroups = cpystr(newsgroups);
    log_message(compose, body, envelope);
  }

  if(sendnews == TRUE)
    mail_free_envelope(&news_envelope);
  if(sendmail == TRUE)
    mail_free_envelope(&envelope);

  /* 
   * If the send failed, protect all our attachments. mail_free_body()
   * is recursive. If the first part is our text, we'll let it go,
   * since it will be rebuilt next time through. This also means we
   * don't explicity free it. It will be nuked as a result of
   * mail_free_body().
   */

  if((retry == TRUE) || (compose->options->keep_open == TRUE)) {
    if(attach_text == TRUE) 
      body->contents.part->next = NIL;
    else 
      body->contents.part = NIL;
  }
  else {

    /* Set answered flag on original message if appropriate */

    if(((compose->compose_type == COMPOSE_REPLY) ||
       (compose->compose_type == COMPOSE_REPLYALL))
       && ((compose->message != NULL)
	   && compose->message->mailstream && compose->message->msgno)) {
      sprintf(msgno_string,"%lu",compose->message->msgno);
      push_cursor(WATCH_CURSOR);
      mail_setflag(compose->message->mailstream,
		   msgno_string, ANSWERED_FLAG );
      mm_flags(compose->message->mailstream,
	       compose->message->msgno);

      pop_cursor();
    }
  }

  /*
   * Clean up everything and go home.
   */

  mail_free_body(&body);    /* and therefore our allocated text */

  fs_give((void **) &from);
  fs_give((void **) &to);
  fs_give((void **) &newsgroups);
  fs_give((void **) &subject);
  fs_give((void **) &cc);
  fs_give((void **) &bcc);

  fs_give((void **) &mailhosts[0]);
  fs_give((void **) &mailhosts);
  fs_give((void **) &newshosts[0]);
  fs_give((void **) &newshosts);

  fs_give((void **) &default_host);

  if((retry == FALSE) && (compose->options->keep_open == FALSE)) {
    /*
     * Our attachments are already gone. Make sure we don't
     * free them a second time.
     */
    compose->attachments = NIL;
    compose_close_window(w,compose,xp);
  }

  pop_cursor();
  return;
}

/*
 * Called externally from mm_expunge() on an expunge event. There is no
 * guarantee that a compose structure exists. What we do is locate a
 * message structure which matches and zero it out in the compose structure.
 * This guarantees we won't be setting any flags on it later. There will
 * only be such a message structure on replies. Since these are just
 * pointers to messages found in the mailbox, we don't need to manage
 * the message numbers for higher numbered messages here. That's done
 * in the main window expunge loop. Be careful not to destroy the
 * structure before this gets called.
 */


#ifdef __STDC__
void compose_expunge(MAILSTREAM *mailstream, unsigned long msgno)
#else
void compose_expunge(mailstream,msgno)
     MAILSTREAM *mailstream;
     unsigned long msgno;
#endif
{
  MLCompose *compose;
  if(session->compose != NULL) {
    for(compose = session->compose ; compose; compose = compose->next) {
      if((compose->message != NULL) 
	 && (compose->message->mailstream == mailstream)  
	 && (compose->message->msgno == msgno))
	compose->message = NULL;
    }
  }
  return;
}





#ifdef __STDC__
void load_compose_from_read(MLCompose *compose, Compose_Type type,
			    Boolean news, Read_Info *read_info)
#else
void load_compose_from_read(compose,type,news,read_info)
     MLCompose *compose;
     Compose_Type type;
     Boolean news;
     Read_Info *read_info;
#endif
{

  char *to;  
  char *reply_subject; 
  char *cc;            
  char *msgid;
  char *ptr1;
  char *ptr2;

  char *fulltext;
  char *fulleverything;
  PART *part;
  BODY *body;
  char *tmp;

  if((type == COMPOSE_NEW) || (read_info == NULL))
    return;

  compose->compose_type = type;

  push_cursor(WATCH_CURSOR);

  switch(type) {

  case COMPOSE_REPLYALL:
    ptr1 = get_header_field_contents(HEADER_NEWSGROUPS, read_info->header);
    if(ptr1) {
      XmTextSetString(compose->window->newsgroups_text, ptr1);
      fs_give((void **) &ptr1);
    }

    ptr1 = get_header_field_contents(HEADER_TO,read_info->header);
    if(ptr1 == NULL)
      ptr1 = cpystr(EMPTYSTR);
    ptr2 = get_header_field_contents(HEADER_CC,read_info->header);
    if(ptr2 == NULL)
      ptr2 = cpystr(EMPTYSTR);
    cc = (char *) fs_get(strlen(ptr1) + strlen(ptr2) + 8 );
    *cc = NUL_TERM;
    
    if(*ptr1 != NUL_TERM) {
      strcpy(cc,ptr1);
      if(*ptr2 != NUL_TERM)
	strcat(cc,", ");
    }
    if(*ptr2 != NUL_TERM)
      strcat(cc,ptr2);
    fs_give((void **) &ptr1);
    fs_give((void **) &ptr2);
    XmTextSetString(compose->window->cc_text, cc);
    fs_give((void **) &cc);
    
    /* fall through to set other info */
    
  case COMPOSE_REPLY:

    to = get_header_field_contents(HEADER_REPLY_TO,read_info->header);
    if(to == NULL)
      to = get_header_field_contents(HEADER_FROM, read_info->header);
    if(to) {
      XmTextSetString(compose->window->to_text, to);
      fs_give((void **) &to);
    }

    reply_subject = get_reply_subject(read_info->header);
    if(reply_subject != NULL) {
      XmTextSetString(compose->window->subject_text, reply_subject);
      fs_give((void **) &reply_subject);
    }

    msgid = get_header_field_contents(HEADER_MESSAGE_ID, read_info->header);
    compose->in_reply_to = msgid;

    /* References !!! */

    if(read_info->alt_text)
      compose->reply_text = cpystr(read_info->alt_text);
    else
      if(read_info->current_text)
	compose->reply_text = cpystr(read_info->current_text);

    compose->message = read_info->message;

    break;

  case COMPOSE_FORWARD:
    if(read_info->alt_text) {
      ptr1 = get_forward_text(read_info->alt_text);
      if(ptr1) {
	XmTextSetString(compose->window->compose_text, ptr1);
	fs_give((void **) &ptr1);
      }
    }
    else {
      if(read_info->current_text) {
	ptr1 = get_forward_text(read_info->current_text);
	if(ptr1) {
	  XmTextSetString(compose->window->compose_text, ptr1);
	  fs_give((void **) &ptr1);
	}
      }
    }

    ptr1 = get_forward_subject(read_info->header);
    if(ptr1) {
      XmTextSetString(compose->window->subject_text, ptr1);
      fs_give((void **) &ptr1);
    }
    break;

  case COMPOSE_FORWARDATTACH:

    if((read_info->mailstream == NULL) 
       || (read_info->msgno == 0L)) {
      mm_log(MLGetLocalized(XtNmsgForwardExpunged,MsgForwardExpunged),WARN);
      break;
    }

    fulltext = mail_fetchtext(read_info->mailstream,read_info->msgno);
    if(fulltext == NULL)
      break;
    stripcr(fulltext);
    fulleverything = (char *) fs_get(((read_info->header) 
				     ? strlen(read_info->header) : 1) 
				     + strlen(fulltext) + 256);
    strcpy(fulleverything, (read_info->header) ? read_info->header : EMPTYSTR);
    strcat(fulleverything,LFSTR);
    strcat(fulleverything,fulltext);
    part = mail_newbody_part();
    body = &part->body;
    
    body->type = TYPEMESSAGE;
    body->subtype = cpystr("RFC822");
    body->description = get_forward_subject(read_info->header);
    body->encoding = ENC7BIT;
    body->contents.msg.text = fulleverything;
    compose->attachments = part;
    break;
    
  case COMPOSE_REMAIL:
    if(read_info->mailstream && read_info->msgno) {
      compose->remail_header 
	= cpystr(mail_fetchheader(read_info->mailstream,read_info->msgno));
      ptr1 = cpystr(mail_fetchtext(read_info->mailstream, read_info->msgno));
      if(ptr1) {
	stripcr(ptr1);
	XmTextSetString(compose->window->compose_text, ptr1);
	fs_give((void **) &ptr1);
      }
    }
    break;
    
  default:
    break;
  }

  if((compose->options->defcc != NULL)
     && (*compose->options->defcc != NUL_TERM)) {
    ptr1 = XmTextGetString(compose->window->cc_text);
    if(*ptr1 != NUL_TERM)
      AppendText(compose->window->cc_text,", ");
    AppendText(compose->window->cc_text,compose->options->defcc);
    fs_give((void **) &ptr1);
  }
  if((compose->options->defbcc != NULL) 
     && (*compose->options->defbcc != NUL_TERM)) {
    ptr1 = XmTextGetString(compose->window->bcc_text);
    if(*ptr1 != NUL_TERM)
      AppendText(compose->window->bcc_text,", ");
    AppendText(compose->window->bcc_text,compose->options->defbcc);
    fs_give((void **) &ptr1);
  }
  pop_cursor();
  return;
}



#ifdef __STDC__
void load_compose_info(MLCompose *compose, Compose_Type type,
		       Boolean news, Lview *lview, Message *message)
#else
void load_compose_info(compose,type, news, lview, message)
     MLCompose *compose;
     Compose_Type type;
     Boolean news;
     Lview *lview;
     Message *message;
#endif
{

  char *tmp;
  char *to;  
  char *reply_subject; 
  char *cc;            
  char *msgid;
  char *ptr1;
  char *ptr2;

  char *fulleverything;
  PART *oldpart;
  PART *part;
  BODY *body;

  char *fetched_header = NULL;
  char *fetched_text = NULL;

  Message_List *message_list;

  compose->compose_type = type;

  push_cursor(WATCH_CURSOR);

  if(message) {

    fetched_header = cpystr(mail_fetchheader(message->mailstream,
					     message->msgno));
    
    stripcr(fetched_header);
    
    if(fetched_header[strlen(fetched_header) - 1] == LFCHAR)
      fetched_header[strlen(fetched_header) - 1] = NUL_TERM;
    
    fetched_text = cpystr(mail_fetchtext(message->mailstream,
					 message->msgno));
    
    stripcr(fetched_text);
  }
  
  switch(type) {
    
  case COMPOSE_REPLYALL:
    ptr1 = get_header_field_contents(HEADER_NEWSGROUPS, fetched_header);
    if(ptr1) {
      XmTextSetString(compose->window->newsgroups_text,ptr1);
      fs_give((void **) &ptr1);
    }

    ptr1 = get_header_field_contents(HEADER_TO,fetched_header);
    if(ptr1 == NULL)
      ptr1 = cpystr(EMPTYSTR);
    ptr2 = get_header_field_contents(HEADER_CC,fetched_header);
    if(ptr2 == NULL)
      ptr2 = cpystr(EMPTYSTR);
    cc = (char *) fs_get(strlen(ptr1) + strlen(ptr2) + 8 );
    *cc = NUL_TERM;
    
    if(*ptr1 != NUL_TERM) {
      strcpy(cc,ptr1);
      if(*ptr2 != NUL_TERM)
	strcat(cc,", ");
    }
    if(*ptr2 != NUL_TERM)
      strcat(cc,ptr2);
    fs_give((void **) &ptr1);
    fs_give((void **) &ptr2);
    XmTextSetString(compose->window->cc_text,cc);
    fs_give((void **) &cc);
    /* fall through to set other info */
    
  case COMPOSE_REPLY:
    to = get_header_field_contents(HEADER_REPLY_TO,fetched_header);
    if(to == NULL)
      to = get_header_field_contents(HEADER_FROM, fetched_header);
    if(to) {
      XmTextSetString(compose->window->to_text, to);
      fs_give((void **) &to);
    }

    reply_subject = get_reply_subject(fetched_header);
    if(reply_subject) {
      XmTextSetString(compose->window->subject_text, reply_subject);
      fs_give((void **) &reply_subject);
    }
    msgid = get_header_field_contents(HEADER_MESSAGE_ID, fetched_header);
    compose->in_reply_to = msgid;

    if(fetched_text)
      compose->reply_text = cpystr(fetched_text);
    compose->message = message;
    break;

  case COMPOSE_FORWARD:

    get_multi_forward_info(lview,compose);
    break;
    
  case COMPOSE_FORWARDATTACH:

    for(message_list = lview->message_list;
	message_list; message_list = message_list->next) {
      if(message_list->selected == TRUE) {
	if(fetched_header)
	  fs_give((void **) &fetched_header);
	if(fetched_text)
	  fs_give((void **) &fetched_text);
	fetched_header 
	  = cpystr(mail_fetchheader(message_list->message->mailstream,
				    message_list->message->msgno));
	
	stripcr(fetched_header);
	
	if(fetched_header[strlen(fetched_header) - 1] == LFCHAR)
	  fetched_header[strlen(fetched_header) - 1] = NUL_TERM;
	
	fetched_text 
	  = cpystr(mail_fetchtext(message_list->message->mailstream,
				  message_list->message->msgno));
	
	stripcr(fetched_text);
	
	
	fulleverything = (char *) fs_get(strlen(fetched_header) +
					 strlen(fetched_text) + 256);
	strcpy(fulleverything,fetched_header);
	strcat(fulleverything,LFSTR);
	strcat(fulleverything,fetched_text);
	part = mail_newbody_part();

	body = &part->body;
	
	body->type = TYPEMESSAGE;
	body->subtype = cpystr("RFC822");
	body->description = get_forward_subject(fetched_header);
	body->encoding = ENC7BIT;
	body->contents.msg.text = fulleverything;
	if(compose->attachments == NULL)
	  compose->attachments = part;
	else {
	  for(oldpart = compose->attachments; oldpart->next;
	      oldpart = oldpart->next)
	    ;
	  oldpart->next = part;
	}
	fs_give((void **) &fetched_header);
	fs_give((void **) &fetched_text);
      }
    }
    break;
    
  case COMPOSE_REMAIL:
    compose->message = message;
    if(fetched_header) 
      compose->remail_header = cpystr(fetched_header);
    if(fetched_text)
      XmTextSetString(compose->window->compose_text,fetched_text);
    break;
    
  default:
    break;
  }

  if(fetched_header)  
    fs_give((void **) &fetched_header);
  if(fetched_text)
    fs_give((void **) &fetched_text);

  if((compose->options->defcc != NULL)
     && (*compose->options->defcc != NUL_TERM)) {
    ptr1 = XmTextGetString(compose->window->cc_text);
    if(*ptr1 != NUL_TERM)
      AppendText(compose->window->cc_text,", ");
    AppendText(compose->window->cc_text,compose->options->defcc);
    fs_give((void **) &ptr1);
  }
  if((compose->options->defbcc != NULL) 
     && (*compose->options->defbcc != NUL_TERM)) {
    ptr1 = XmTextGetString(compose->window->bcc_text);
    if(*ptr1 != NUL_TERM)
      AppendText(compose->window->bcc_text,", ");
    AppendText(compose->window->bcc_text,compose->options->defbcc);
    fs_give((void **) &ptr1);
  }

  pop_cursor();
  return;
}


#ifdef __STDC__
void get_multi_forward_info(Lview *lview, MLCompose *compose)
#else
void get_multi_forward_info(lview,compose)
  Lview *lview;
  MLCompose *compose;
#endif
{
  Message_List *message_list;
  unsigned long count = 0L;
  Message *message = NULL;
  char *old_header = NULL;
  char *old_text = NULL;
  char *new_header = NULL;
  char *new_text = NULL;
  char *tmp_header = NULL;
  char *tmp_text = NULL;
  char *fetched_header = NULL;
  char *fetched_text = NULL;
  char *ret = NULL;
  char *foo = NULL;


  for(message_list = lview->message_list; message_list; 
      message_list = message_list->next) {
    if(message_list->selected) {
      message = message_list->message;
      count ++;
    }
  }

  if(count == 0)
    return;

  if((count == 1) && (message)) {
    fetched_header = cpystr(mail_fetchheader(message->mailstream,
					     message->msgno));
    stripcr(fetched_header);
    if(fetched_header[strlen(fetched_header) - 1] == LFCHAR)
      fetched_header[strlen(fetched_header) - 1] = NUL_TERM;
    
    fetched_text = cpystr(mail_fetchtext(message->mailstream,
					 message->msgno));
    stripcr(fetched_text);


    foo = get_forward_text(fetched_text);
    XmTextSetString(compose->window->compose_text, foo);
    fs_give((void **) &foo);

    foo = get_forward_subject(fetched_header);
    XmTextSetString(compose->window->subject_text,foo);
    fs_give((void **) &foo);

    fs_give((void **) &fetched_header);
    fs_give((void **) &fetched_text);

    return;
  }

  count = 0;

  XmTextSetString(compose->window->subject_text,
		  MLGetLocalized(XtNstrMultiForwardSubject,
				 StrMultiForwardSubject));

  old_header = cpystr(MLGetLocalized(XtNstrMultiForwardHeader,
				     StrMultiForwardHeader));

  old_text = cpystr(EMPTYSTR);

  for(message_list = lview->message_list; message_list; 
      message_list = message_list->next) {
    if(message_list->selected) {
      message = message_list->message;
      count ++;

      fetched_header = cpystr(mail_fetchheader(message->mailstream,
					       message->msgno));
      stripcr(fetched_header);
      if(fetched_header[strlen(fetched_header) - 1] == LFCHAR)
	fetched_header[strlen(fetched_header) - 1] = NUL_TERM;
      
      tmp_header = get_short_forward_line(fetched_header,count);
      new_header = (char *)fs_get(strlen(old_header) + strlen(tmp_header) + 1);
      strcpy(new_header,old_header);
      strcat(new_header,tmp_header);
      fs_give((void **) &old_header);
      fs_give((void **) &tmp_header);
      fs_give((void **) &fetched_header);
      old_header = new_header;

      fetched_text = cpystr(mail_fetchtext(message->mailstream,
					   message->msgno));
			    
      stripcr(fetched_text);
      tmp_text = get_forward_text(fetched_text);
      new_text = (char *) fs_get(strlen(old_text) + strlen(tmp_text) + 2);
      strcpy(new_text,old_text);
      strcat(new_text,LFSTR);
      strcat(new_text,tmp_text);
      fs_give((void **) &old_text);
      fs_give((void **) &tmp_text);
      fs_give((void **) &fetched_text);
      old_text = new_text;


    }
  }

  ret = (char *) fs_get(strlen(old_header) + strlen(old_text) + 1);
  strcpy(ret,old_header);
  strcat(ret,old_text);

  fs_give((void **) &old_header);
  fs_give((void **) &old_text);

  XmTextSetString(compose->window->compose_text,ret);
  fs_give((void **) &ret);

  return;
}




#ifdef __STDC__
void compose_help(Widget w, MLCompose *compose, XtPointer xp)
#else 
void compose_help(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  help(compose->window->shell, COMPOSEHELPFILE);
  return;
}



/* Option setting callbacks. */



#ifdef __STDC__
void compose_keep_open(Widget w, MLCompose *compose, XtPointer xp)
#else 
void compose_keep_open(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  if((compose != NULL) && (compose->options != NULL)) {
    compose->options->keep_open = TRUE;
  }
  return;
}


#ifdef __STDC__
void compose_eight(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_eight(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  if((compose != NULL) && (compose->options != NULL)) {
    compose->options->send_eight = TRUE;
  }
  return;
}

#ifdef __STDC__
void compose_seven(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_seven(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  if((compose != NULL) && (compose->options != NULL)) {
    compose->options->send_eight = FALSE;
  }
  return;
}


/* 
 * Turn on the mail debugging flag. We can also do it globally
 * from preferences.smtp_debug. This sets it for the current
 * message only. There is no way to turn it off again.
 */

#ifdef __STDC__
void compose_watch_delivery(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_watch_delivery(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  if((compose != NULL) && (compose->options != NULL)) {
    compose->options->verbose = TRUE;
  }
  return;
}



#ifdef __STDC__
struct mail_body_parameter *copy_params(struct mail_body_parameter *src, 
					Boolean intl)
#else
struct mail_body_parameter *copy_params(src, intl)
     struct mail_body_parameter *src;
     Boolean intl;
#endif
{
  struct mail_body_parameter *curr;
  struct mail_body_parameter *new;
  struct mail_body_parameter *prev = NULL;
  struct mail_body_parameter *ret = NULL;

  if(src == NULL)
    return(NULL);

  for(curr = src; curr; curr = curr->next) {

    /*  No 8-bit characters were found. Remove any charset value
     *  so as to default to us-ascii. This is called for in the
     *  MIME specification.
     */

    if((intl == FALSE) && (curr != NULL)
       && (curr->attribute != NULL) 
       && ((strcasecmp(curr->attribute,"charset")) == STRMATCH))
      continue;
    new = mail_newbody_parameter();
    new->attribute = cpystr(curr->attribute);
    new->value = cpystr(curr->value);
    if(ret == NULL)
      ret = new;
    if(prev)
      prev->next = new;
    prev = new;
  }

  return(ret);
}



#ifdef __STDC__
void fix_empty_attachments(MLCompose *compose)
#else
void fix_empty_attachments(compose)
     MLCompose *compose;
#endif
{
  Part_List *part_list;
  BODY *body;
  PART *part;
  BODY *newbody;
  Boolean changed = FALSE;
  
  for(part_list = compose->part_list;
      part_list; part_list = part_list->next) {
    body = &(part_list->part->body);

    if((body->type == TYPEMULTIPART) && (body->contents.part == NULL)) {
      /* 
       * Somebody tried to send an empty part. It would be rather
       * painful to try and prune it, so we'll gratuitously supply
       * a part for them. Otherwise, the c-client will core dump.
       *
       */
      part = mail_newbody_part();
      body->contents.part = part;
      newbody = &part->body;
      newbody->type = TYPETEXT;
      newbody->encoding = ENC7BIT;
      newbody->description = cpystr(MLGetLocalized(XtNstrFakePart,
						   StrFakePart));
      newbody->subtype = cpystr("plain");
      newbody->contents.text = (unsigned char *) cpystr(EMPTYSTR);
      newbody->size.bytes = 0;
      changed = TRUE;
    }
  }
  if(changed == TRUE) {
    mm_log(MLGetLocalized(XtNmsgEmptyMultipart,MsgEmptyMultipart),WARN);
    make_attachment_list(compose, FALSE);
  }
  return;
}


#ifdef __STDC__
void log_message(MLCompose *compose, BODY *body, ENVELOPE *envelope)
#else
void log_message(compose, body, envelope)
     MLCompose *compose;
     BODY *body;
     ENVELOPE *envelope;
#endif
{
  extern  STRINGDRIVER mail_string;
  STRING bs;
  char *mailboxname;
  char tmp[16 * FILEBUFFLEN];
  char tmpfile[MAXPATHLEN];
  char log[FILEBUFFLEN];
  Binary_Buffer *binary_buffer;
  FILE *fp;
  Server_Config *config;
  char *host;
  Boolean changed = FALSE; /* ignored */

  if((compose != NULL) && (compose->options != NULL)) {

    if((*compose->options->outlog == NUL_TERM)
       || (compose->options->message_log == FALSE))
      return;
    mailboxname = fix_netmailboxpath(compose->options->outlog,&changed);
    if(compose->options->log_attachments == FALSE) {
      log_short_message(compose,mailboxname,body,envelope);
      return;
    }

    tmpnam(tmpfile);
    if((fp = fopen(tmpfile,"w")) == NULL) {
      sprintf(log,MLGetLocalized(XtNmsgCannotWriteFile,MsgCannotWriteFile),
	      tmpfile);
      mm_log(log,WARN);
      fs_give((void **) &mailboxname);
      return;
    }
    chmod(tmpfile,S_IRWXU);
    rfc822_output(tmp,envelope, body, ml_file_soutr, fp);
    fclose(fp);
    
    binary_buffer = load_binary_file(tmpfile);
    unlink(tmpfile);

    if(binary_buffer) {
      if(binary_buffer->data) {
	INIT(&bs,mail_string,binary_buffer->data, binary_buffer->length);
	if((mail_append_full(NIL,mailboxname, SEEN_FLAG, 
			     NULL, &bs)) == NIL)
	  mm_log(MLGetLocalized(XtNmsgAppendFailed,MsgAppendFailed),WARN);
      }
      free_binary_buffer(binary_buffer);
    }
  }
  return;
}

#ifdef __STDC__
long ml_file_soutr(void *stream, char *s)
#else
long ml_file_soutr(stream,s)
     void *stream;
     char *s;
#endif
{
  FILE *fp = stream;
  char c, *t;

  if (s[0] == '.') fputs(".",fp);
				/* find lines beginning with a "." */
  while (t = ML_Strstr (s,"\015\012.")) {
    c = *(t += 3);		/* remember next character after "." */
    *t = '\0';			/* tie off string */
				/* output prefix */
    if ((fputs(s,fp)) == EOF)
      return NIL;
    *t = c;			/* restore delimiter */
    s = t - 1;			/* push pointer up to the "." */
  }
  if(*s) {
    if((fputs(s,fp)) == EOF)
      return(NIL);
    else
      return(T);
  }
  else
    return(T);

}


#ifdef __STDC__
void cat4(char *dest, char *s1, char *s2, char *s3, char *s4)
#else
void cat4(dest,s1,s2,s3,s4)
     char *dest;
     char *s1;
     char *s2;
     char *s3;
     char *s4;
#endif
{
  strcat(dest,s1);
  strcat(dest,s2);
  strcat(dest,s3);
  strcat(dest,s4);
  return;
}



#ifdef __STDC__
void log_short_message(MLCompose *compose, char *mailboxname,
		       BODY *body, ENVELOPE *envelope)
#else
void log_short_message(compose, mailboxname, body, envelope)
     MLCompose *compose;
     char *mailboxname;
     BODY *body;
     ENVELOPE *envelope;
#endif
{
  extern  STRINGDRIVER mail_string;
  STRING bs;
  char *from;
  char *newsgroups;
  char *to;
  char *subject;
  char *cc;
  char *bcc;
  char *text;
  char *date;
  char *temp_text;
  char *bigbuf;
  char *ret;
  Server_Config *server_config;
  char *host;

  from       = cpystr(compose->options->replyto);
  newsgroups = GetTextField(compose->window->newsgroups_text);
  to         = GetTextField(compose->window->to_text);
  subject    = GetTextField(compose->window->subject_text);
  cc         = GetTextField(compose->window->cc_text);
  bcc        = GetTextField(compose->window->bcc_text);
  temp_text  = XmTextGetString(compose->window->compose_text);
  date = (char *) fs_get(MESSAGE_ID_LENGTH);
  rfc822_date(date);

  if(compose->options->word_wrap == TRUE)
    text = wrap_text(temp_text, COMPOSEWIDTH - 1);
  else
    text = cpystr(temp_text);

  fs_give((void **) &temp_text);

  bigbuf = (char *) fs_get(  ((from)       ? strlen(from)       : 0) 
			   + ((newsgroups) ? strlen(newsgroups) : 0)
			   + ((to)         ? strlen(to)         : 0)
			   + ((subject)    ? strlen(subject)    : 0)
			   + ((cc)         ? strlen(cc)         : 0)
			   + ((bcc)        ? strlen(bcc)        : 0)
			   + ((date)       ? strlen(date)       : 0)
			   + ((envelope->in_reply_to) ?
			      strlen(envelope->in_reply_to)     : 0)
			   + ((envelope->message_id) ?
			      strlen(envelope->message_id)      : 0)
			   + ((text)       ? strlen(text)       : 0)
			   +                                    512);

  *bigbuf = NUL_TERM;

  if(from) {
    if(*from)
      cat4(bigbuf,HEADER_FROM,SPACESTR,from,LFSTR);
    fs_give((void **) &from);
  }
  if(newsgroups) {
    if(*newsgroups)
      cat4(bigbuf,HEADER_NEWSGROUPS,SPACESTR,newsgroups,LFSTR);
    fs_give((void **) &newsgroups);
  }
  if(to) {
    if(*to) 
      cat4(bigbuf,HEADER_TO,SPACESTR,to,LFSTR);
    fs_give((void **) &to);
  }
  if(subject) {
    if(*subject) 
      cat4(bigbuf,HEADER_SUBJECT,SPACESTR,subject,LFSTR);
    fs_give((void **) &subject);
  }
  if(cc) {
    if(*cc)
      cat4(bigbuf,HEADER_CC,SPACESTR,cc,LFSTR);
    fs_give((void **) &cc);
  }
  if(bcc) {
    if(*bcc)
      cat4(bigbuf,HEADER_BCC,SPACESTR,bcc,LFSTR);
    fs_give((void **) &bcc);
  }
  if(date) {
    if(*date) 
      cat4(bigbuf,HEADER_DATE,SPACESTR,date,LFSTR);
    fs_give((void **) &date);
  }
  if(envelope->in_reply_to) {
    if(*envelope->in_reply_to)
      cat4(bigbuf,HEADER_IN_REPLY_TO,SPACESTR,envelope->in_reply_to,LFSTR);
  }
  if(envelope->message_id) {
    if(*envelope->message_id)
      cat4(bigbuf,HEADER_MESSAGE_ID,SPACESTR,envelope->message_id,LFSTR);
  }
  strcat(bigbuf,LFSTR);

  if(text) {
    if(*text) 
      strcat(bigbuf,text);
    strcat(bigbuf,LFSTR);
    fs_give((void **) &text);
  }

  ret = lftocrlf(bigbuf);
  fs_give((void **) &bigbuf);

  if(ret) {
    INIT(&bs,mail_string,ret,strlen(ret));
    if((mail_append_full(NIL, mailboxname, SEEN_FLAG, NULL, &bs)) == NIL)
      mm_log(MLGetLocalized(XtNmsgAppendFailed,MsgAppendFailed),WARN);
    fs_give((void **) &ret);
  }

  if(mailboxname)
    fs_give((void **) &mailboxname);
  return;

}

#ifdef __STDC__
char *param_to_str(PARAMETER *parameter)
#else
char *param_to_str(parameter)
     PARAMETER *parameter;
#endif
{
  char buffer[FILEBUFFLEN];
  PARAMETER *p;

  if(parameter == NULL)
    return(cpystr(EMPTYSTR));

  strcpy(buffer,EMPTYSTR);
  for(p = parameter; p; p = p->next) {
    if((p->attribute == NULL) || (p->value == NULL))
      continue;
    if(*buffer != NUL_TERM)
      strcat(buffer,SPACESTR);

    strcat(buffer,p->attribute);
    strcat(buffer,EQUALSTR);
    strcat(buffer,p->value);

  }
  return(cpystr(buffer));
}

#ifdef __STDC__
PARAMETER *str_to_param(char *str)
#else
PARAMETER *str_to_param(str)
     char *str;
#endif
{

  char *p;
  char *ptr;
  char *last = str;
  PARAMETER *parameter = NULL;
  PARAMETER *first     = NULL;
  PARAMETER *lastp     = NULL;
  Boolean reached_end  = FALSE;
  Boolean bad_input    = FALSE;

  if((str == NULL) || (*str == NUL_TERM))
    return(NULL);

  for(ptr = str; *ptr != NUL_TERM ; ptr ++) {
    if(reached_end == TRUE)
      break;
    if(*ptr != EQUALCHAR)
      continue;

    *ptr = NUL_TERM;
    ptr ++;
    parameter = mail_newbody_parameter();
    parameter->attribute = cpystr(last);
    p = ptr;
    if(*p == DQUOTECHAR) {
      p ++;
      while((*p != NUL_TERM) && (*p != DQUOTECHAR))
	p ++;
      while((*p != NUL_TERM) && (*p != SPACECHAR))
	p ++;
    }
    else {
      while ((*p != NUL_TERM) && (*p != SPACECHAR))
	p ++;
    }
    if(*p == NUL_TERM)
      reached_end = TRUE;
    *p = NUL_TERM;
    parameter->value = cpystr(ptr);
    if(first == NULL) {
      first = parameter;
      lastp = parameter;
    }
    else {
      lastp->next = parameter;
      lastp = parameter;
    }
    if(reached_end == TRUE)
      break;
    else {
      last = p + 1;
      ptr = p + 1;
      while((*last != NUL_TERM) && (isspace(*last)))
	last ++;
      if(*last == NUL_TERM)
	break;
    }
  }
  
  /* Now check for bad input.... */
  
  for(parameter = first; parameter != NULL; parameter = parameter->next) {
    if((parameter->attribute == NULL) || (parameter->value == NULL))
      bad_input = TRUE;
  }
  
  if(bad_input) {
    mm_log(MLGetLocalized(XtNmsgInvalidParams,MsgInvalidParams),ERROR);
    mail_free_body_parameter(&first);
  }

  return(first);
}


#ifdef __STDC__
void compose_delete_part(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_delete_part(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{

  Part_List *part_list;
  BODY *body = NULL;

  if((compose->window->attach_list_selection == 0) 
     || (compose->part_list == NULL))
    return;
  
  /* point to the Part_List of the selected part.*/

  for(part_list = compose->part_list; 
      part_list->partnumber != compose->window->attach_list_selection; 
      part_list = part_list->next);
    

  /* First see if it's a multipart which still has contents */

  body = &(part_list->part->body);
  if(body->type == TYPEMULTIPART 
     && (body->contents.part != NULL)) {
    mm_log(MLGetLocalized(XtNmsgNonEmptyMulti,MsgNonEmptyMulti),WARN);
    return;
  }

  /* first part in our attachment list */

  if(part_list->part == compose->attachments) {
    compose->attachments = part_list->part->next;
    part_list->part->next = NULL;
    mail_free_body_part(&part_list->part);
    make_attachment_list(compose, FALSE);
    return;
  }

  /* Some other part. See if our previous neighbor points to us. */

  if((part_list->prev) && (part_list->prev->part->next == part_list->part)) {
    part_list->prev->part->next = part_list->part->next;
    part_list->part->next = NULL;
  }
  else {
    /* Hmmm. Heirarchical. Find the parent. */
    body = (part_list->prev) ? &(part_list->prev->part->body) : NULL;
    if((body) && (body->type == TYPEMULTIPART))
      body->contents.part = part_list->part->next;
    part_list->part->next = NULL;
  }

  mail_free_body_part(&part_list->part);
  make_attachment_list(compose, FALSE);
  return;
}


/* 
 * Callback when an item in the attachment list is selected. 
 */

#ifdef __STDC__
void select_attach(Widget w, MLCompose *compose, XmListCallbackStruct *xp)
#else
void select_attach(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XmListCallbackStruct *xp;
#endif
{
    
  if(xp->item_position == compose->window->attach_list_selection) {
    compose->window->attach_list_selection = 0;
    compose->window->buttonstate |= BTN_NOATTACHSELECT;
  }
  else {
    compose->window->attach_list_selection = xp->item_position;
    compose->window->buttonstate &= ~(BTN_NOATTACHSELECT);
  }
  
  compose_check_buttons(compose);
  return;
}


#ifdef __STDC__
void double_click_attach(Widget w, MLCompose *compose, 
			 XmListCallbackStruct *xp)
#else
void double_click_attach(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XmListCallbackStruct *xp;
#endif
{
  Part_List *part_list;
  BODY *body;

  if(! xp->item_position)
    return;

  for(part_list = compose->part_list; 
      part_list && part_list->partnumber != xp->item_position; 
      part_list = part_list->next);

  if(! part_list)
    return;

  /* Make sure the window shows it as selected */

  XmListDeselectAllItems(compose->window->attach_list);
  compose->window->attach_list_selection = xp->item_position;
  compose->window->buttonstate &= ~(BTN_NOATTACHSELECT);
  XmListSelectPos(compose->window->attach_list,xp->item_position,FALSE);

  compose_check_buttons(compose);
  body = &(part_list->part->body);

  /* invoke message handler */

  show_mime(compose->window->shell, body, FALSE);
  return;
}


#ifdef __STDC__
void make_attachment_list(MLCompose *compose, Boolean init)
#else
void make_attachment_list(compose, init)
     MLCompose *compose;
     Boolean init;
#endif
{
  if(compose != NULL) {
    /* Erase current list contents and start over */


    XmListDeselectAllItems(compose->window->attach_list);
    XmListDeleteAllItems(compose->window->attach_list); 

    compose->window->attach_list_selection = 0;
    compose->window->buttonstate |= BTN_NOATTACHSELECT;

    /* No contents. Check buttons and get outa' here */
    
    if(compose->attachments == NULL) { 
      compose->window->buttonstate |= BTN_NOATTACH;

      compose_check_buttons(compose);
      return;
    }

    if(compose->part_list) {
      destroy_part_list(compose->part_list);
    }

    compose->part_list = NULL;

    /* 
     * Recursively add strings to the attachment list widget 
     * and add to our part_list structure. We use this to look up
     * specific parts from the list management functions without having
     * to go through all this recursive hullabaloo all over again.
     */

    add_attach_strings(compose->window->attach_list, compose,
		       compose->attachments,0);

    compose->window->buttonstate &= ~(BTN_NOATTACH);

    compose_check_buttons(compose);
  }
  return;
}

#ifdef __STDC__
void destroy_part_list(Part_List *part_list)
#else
void destroy_part_list(part_list)
  Part_List *part_list;
#endif
{
  if(part_list == NULL)
    return;
  destroy_part_list(part_list->next);
  if(part_list->partstr)
    fs_give((void **) &part_list->partstr);
  fs_give((void **) &part_list);
  part_list = NULL;
  return;
}


#ifdef __STDC__
void add_attach_strings(Widget wid, MLCompose *compose, PART *part, int level)
#else
void add_attach_strings(wid,compose,part,level)
     Widget wid;
     MLCompose *compose;
     PART *part;
     int level;
#endif
{
  int len;
  XmString xstr;
  PART *curr;

  char buffer[FILEBUFFLEN];
  Part_List *part_list;
  Part_List *curr_part_list;
  struct mail_body_parameter *params;

  if(part == NULL)
    return;

  for(curr = part; curr; curr = curr->next) {
    strcpy(buffer,EMPTYSTR);
    for(len = 0; len < level; len ++)
      strcat(buffer,SPACESTR);
    strcat(buffer,type_to_name(curr->body.type));
    strcat(buffer,TYPE_SEPARATOR_STR);
    strcat(buffer,(curr->body.subtype) ? curr->body.subtype : NOSUBTYPE );
    for(len = strlen(buffer); len < (COMPOSEWIDTH/2); len ++)
      strcat(buffer,SPACESTR);
    strncat(buffer,
	   (curr->body.description) ? curr->body.description : EMPTYSTR,
	    COMPOSEWIDTH);
    buffer[COMPOSEWIDTH - 2] = NUL_TERM;
    for(params = curr->body.parameter; 
	params; params = params->next) {
      if((int) (strlen(buffer) + strlen(params->attribute) 
	  + strlen(params->value) + 2) >= FILEBUFFLEN)
	break;
      strcat(buffer,SPACESTR);
      strcat(buffer,params->attribute);
      strcat(buffer,"=");
      strcat(buffer,params->value);
    }

    xstr = XmStringCreateSimple(buffer);
    XmListAddItemUnselected(wid,xstr,0);
    XmStringFree(xstr);
    
    part_list = (Part_List *) fs_get(sizeof(Part_List));
    part_list->next = NULL;
    part_list->part = curr;
    part_list->partstr = NULL;

    if(compose->part_list == NULL) {
      compose->part_list = part_list;
      part_list->partnumber = 1;
      part_list->prev = NULL;
    }
    else {
      for(curr_part_list = compose->part_list; curr_part_list->next;
	curr_part_list = curr_part_list->next);

      curr_part_list->next = part_list;
      part_list->prev = curr_part_list;
      part_list->partnumber = curr_part_list->partnumber + 1;
    }

    if(curr->body.type == TYPEMULTIPART)
      add_attach_strings(wid,compose,curr->body.contents.part,level+1);

  }
  return;
}

#ifdef __STDC__
void compose_insert_file(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_insert_file(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  char *filename;
  Binary_Buffer *binary_buffer;

  filename = file_select(compose->window->shell, NULL, 
			 NULL, NULL, FALSE, NULL);
  if(filename == NULL)
     return;
  
  push_cursor(WATCH_CURSOR);

  binary_buffer = load_binary_file(filename);
  if(binary_buffer) {
    text_blast(compose->window->compose_text,(char *)binary_buffer->data);
    free_binary_buffer(binary_buffer);
  }

  fs_give((void **) &filename);
  pop_cursor();
  return;
}


/* 
 * Append signature file into current text window and call 
 * send_message(). We throw in an extra linefeed between the two
 * just in case the last line of the message doesn't have one.
 */


#ifdef __STDC__
void compose_send_autograph(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_send_autograph(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  FILE *fp;
  char *filename;
  char buffer[FILEBUFFLEN];

  filename = cpystr(compose->options->sigfile);
  
  if((*filename != NUL_TERM) && (fp = fopen(filename,"r")) != NULL) {
    AppendText(compose->window->compose_text,LFSTR);
    while((fgets(buffer,sizeof(buffer),fp)) != NULL)
      AppendText(compose->window->compose_text,buffer);
    fclose(fp);
  }
  fs_give((void **) &filename);
  send_message(w,compose,xp);
  return;
}



#ifdef __STDC__
void compose_attach_file(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_attach_file(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  char *filename = NULL;
  Binary_Buffer *binary_buffer;
  PART *part;
  BODY *body;
  Boolean auto_attach;
  Boolean keep_going;

  filename = file_select(compose->window->shell, NULL, 
			 NULL, NULL, FALSE, NULL);
  if(filename == NULL)
    return;

  binary_buffer = load_binary_file(filename);

  if(binary_buffer) {

    part = mail_newbody_part();
    body = &part->body;
    
    set_default_attach_type(body,filename);

    if((auto_attach = get_type_from_suffix(body,filename)) == FALSE)
      keep_going = 
	create_mime_attach_window(compose->window->shell,body,FALSE);
  
    if(auto_attach || keep_going)
      link_attachment(compose,part,body,binary_buffer); 
    else 
      mail_free_body_part(&part);
    
    free_binary_buffer(binary_buffer);
    
  }

  fs_give((void **) &filename);
  return;
}

#ifdef __STDC__
void compose_attach_multi(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_attach_multi(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  PART *part;
  BODY *body;


  part = mail_newbody_part();
  body = &part->body;
  
  body->type = TYPEMULTIPART;
  body->subtype = cpystr("mixed");

  if(create_mime_attach_window(compose->window->shell,body,TRUE))
    link_attachment(compose,part,body,NULL);
  else
    mail_free_body_part(&part);
  return;
}


#ifdef __STDC__
void compose_createattach(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_createattach(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  PART *part;
  BODY *body;
  Binary_Buffer *binary_buffer; 

  part = mail_newbody_part();
  body = &part->body;
  
  binary_buffer = create_mime_compose_window(compose->window->shell,body);

  if(binary_buffer) {
    link_attachment(compose,part,body,binary_buffer);
    free_binary_buffer(binary_buffer);
  }
  else
    mail_free_body_part(&part);
  return;
}



/* 
 * We've been called by an address text field widget. At least that's the
 * assumption, so don't try and change it. All we have is the widget.
 * It contains something presumably that we will have to complete from
 * the address book. But it might contain other addresses, and the one to be
 * completed could be anywhere. We presume that it is under the cursor.
 * We then split up the string into three parts. The part before the current
 * word (comma delimited), and anything afterward (delimited by comma or a 
 * space). Any of these parts might be empty. After we extract our pattern,
 * we look it up, and replace it with the address book address. More on 
 * that later. We then combine it with any or all of the other parts, and 
 * make sure we have comma delimiters (plus a gratuitous space) between 
 * all the fields. On failure we leave it alone, and advance to the next
 * tab group, since the most likely key binding for this will be a tab key.
 * Therefore, the tab completes, if possible, and in all other cases moves to 
 * the next tab group.
 */

#ifdef __STDC__
void complete_address(Widget w, XtPointer xp)
#else
void complete_address(w,xp)
     Widget w;
     XtPointer xp;
#endif
{
  char *text = GetTextField(w);
  XmTextPosition pos = XmTextGetInsertionPosition(w);
  char *begin = NULL;
  char *end = NULL;
  char *pattern = NULL;
  char *lookup = NULL;
  XmTextPosition newpos = 0;
  Boolean found = FALSE;

  if(*text == NUL_TERM) {
    fs_give((void **) &text);
    XmProcessTraversal(w,XmTRAVERSE_NEXT_TAB_GROUP);
    return;
  }

  /* point to current position. */

  pattern = text + ((pos > 0) ? (pos - 1) : 0 );

  /* find the beginning of this address */

  while((pattern > text) && (*pattern != ','))
	pattern --;

  /* We're at the beginning */

  if(pattern == text) {
    begin = NULL;
  }

  /* 
   * Not the beginning, so set the begin pointer to it,
   * tie it off, and point pattern just beyond it.
   */

  else {
    *pattern = NUL_TERM;
    pattern = first_nonwhite(pattern + 1);
    begin = text;
  }

  /* 
   * find any  subsequent addresses. If we find one, tie off
   * our pattern string, and point end at the next address.
   */

  end = strpbrk(pattern,", ");
  if(end) {
    *end = NUL_TERM;
    end = first_nonwhite(end + 1);
  }

  /* 
   * Try and match it. If we do, rebuild the visible text. Set the
   * cursor at the end of the address we just completed. 
   */


  if((lookup = lookup_group(pattern)) != NULL) {
    XmTextSetString(w,(begin) ? begin : EMPTYSTR);
    AppendText(w,(begin) ? ", " : EMPTYSTR );
    AppendText(w,lookup);
    newpos = XmTextGetInsertionPosition(w);
    if(end) {
      AppendText(w,", ");
      AppendText(w,end);
    }
    XmTextSetInsertionPosition(w,newpos);
    fs_give((void **) &lookup);
    found = TRUE;
  }
  else {
    if((lookup = lookup_address(pattern)) != NULL) {
      XmTextSetString(w,(begin) ? begin : EMPTYSTR);
      AppendText(w,(begin) ? ", " : EMPTYSTR );
      AppendText(w,lookup);
      newpos = XmTextGetInsertionPosition(w);
      if(end) {
	AppendText(w,", ");
	AppendText(w,end);
      }
      XmTextSetInsertionPosition(w,newpos);
      fs_give((void **) &lookup);
      found = TRUE;
    }
  }

  if(found == FALSE)
    XmProcessTraversal(w,XmTRAVERSE_NEXT_TAB_GROUP);
  fs_give((void **) &text);
  return;
}


#ifdef __STDC__
char *lookup_address(char *str)
#else
char *lookup_address(str) 
  char *str;
#endif
{
  int cnt = 0;
  
  Address_Book_Info *address_book_info;
  Address_Book_Info *saved = NULL;

  for(address_book_info = session->addresses;
      address_book_info ; address_book_info = address_book_info->prev) {
    if((strcasecmp((char *) address_book_info->name,str)) == STRMATCH) {
      /* Exact match. Use it. */
      cnt = 1;
      saved = address_book_info;
      break;
    }
    else {
      if((strncasecmp((char *) address_book_info->name,
		      str,strlen(str))) == STRMATCH) {
	cnt ++;
	saved = address_book_info;
      }
    }
  }

  if(cnt == 1)
    return(cpystr((char *) saved->address));

  if(cnt > 1)                   /* Not completed */
    return(address_popup(str));

  return(NULL);
}


#ifdef __STDC__
char *lookup_group(char *str)
#else
char *lookup_group(str) 
  char *str;
#endif
{
  char buffer[FILEBUFFLEN];
  char *ptr;
  char *end;
  char *result = NULL;
  char *now = NULL;
  Address_Book_Info *address_book_info;

  for(address_book_info = session->addresses;
      address_book_info ; address_book_info = address_book_info->prev) {
    if((address_book_info->groups == NULL) 
       || (*address_book_info->groups == NUL_TERM)) 
      continue;
    strcpy(buffer,(char *) address_book_info->groups);
    ptr = end = buffer;
    while((*ptr != NUL_TERM) && (end)) {
      if((end = strpbrk(ptr,", ")) != NULL) {
	*end = NUL_TERM;
	end ++;
      }
      if((strcasecmp(ptr,str)) == STRMATCH) {
	if(result) {
	  now = fs_get(strlen(result) 
		       + ((address_book_info->address) 
			  ? strlen((char *) address_book_info->address) 
			  : 1) + 4);
	  strcpy(now,result);
	  strcat(now,", ");
	  strcat(now,(address_book_info->address) 
		 ? (char *) address_book_info->address : EMPTYSTR);
	  fs_give((void **) &result);
	  result = now;
	}
	else
	  result = cpystr((address_book_info->address)
			  ? (char *) address_book_info->address
			  : EMPTYSTR);
      }
      if(end) {
	ptr = end;
	while((*ptr != NUL_TERM) 
	      && ((*ptr == SPACECHAR) || (*ptr == ',')))
	  ptr ++;
      }
    }
  }

  return(result);
}






#ifdef __STDC__
char *address_popup(char *str)
#else
char *address_popup(str)
     char *str;
#endif
{
  XBell(display, 500);
  return(NULL);
}


#ifdef __STDC__
void generate_subject(Widget w, MLCompose *compose, XtPointer xp)
#else
void generate_subject(w, compose, xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  Binary_Buffer *binary_buffer;
  unsigned char *ptr;

  silent_pipe = TRUE;

  if((access(PATH_FORTUNE, X_OK)) == SYSCALL_SUCCESS) {
    binary_buffer = read_from_pipe(FORTUNE_CMD);
    if(binary_buffer) {
      if(binary_buffer->data) {
	for(ptr = binary_buffer->data; *ptr != NUL_TERM; ptr ++)
	  if((*ptr == LFCHAR) || (*ptr == TABCHAR))
	    *ptr = SPACECHAR;
	XmTextSetString(compose->window->subject_text, binary_buffer->data);
      }
      free_binary_buffer(binary_buffer);
    }
  }
  silent_pipe = FALSE;
  return;
}

#ifdef __STDC__
void compose_spell(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_spell(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  FILE *fp = NULL;
  char *text = NULL;
  String_List *string_list = NULL;
  String_List *prev        = NULL;
  String_List *base        = NULL;
  char *tmp_file           = NULL;
  char *otext              = NULL;
  Binary_Buffer *bb = NULL;
  char filename[MAXPATHLEN];
  char buffer[FILEBUFFLEN];
  char command[FILEBUFFLEN];
  int count = 0;
  Boolean has_stop = FALSE;

  if(access(SPELLSTOPFILE,R_OK) == SYSCALL_SUCCESS)
    has_stop = TRUE;

  if(preferences.useIspell == FALSE) {
    if((compose->spellwindow != NULL) 
       && (compose->spellwindow->is_realized == TRUE)) {
      de_iconify(compose->spellwindow->shell);
      return;
    }
  }


  otext = XmTextGetString(compose->window->compose_text);
  if(otext == NULL) 
    return;
  if(*otext == NUL_TERM) {
    fs_give((void **) &otext);
    return;
  }

  /* Just in case they didn't end the message properly.... */

  text = (char *) fs_get(strlen(otext) + 8);
  strcpy(text,otext);
  strcat(text,LFSTR);
  fs_give((void **) &otext);


  tmp_file = (char *) tmpnam(filename);

  if(preferences.useIspell == TRUE) {
    /* run ispell in an xterm */
    if((fp = fopen(tmp_file,"w")) != NULL) {
      (void) chmod(tmp_file,S_IRWXU);
      fwrite(text,strlen(text),1,fp);
      fclose(fp);
      sprintf(command,ISPELL_COMMAND,tmp_file);
      XtVaSetValues(compose->window->compose_text,XmNeditable, FALSE, NULL);
      compose->in_ispell = TRUE;
      if((write_to_pipe(command,NULL, NULL, 0)) == SYSCALL_SUCCESS) {
	bb = load_binary_file(tmp_file);
	if(bb != NULL) {
	  if(bb->data != NULL)
	    XmTextSetString(compose->window->compose_text,(char *)bb->data);
	  free_binary_buffer(bb);
	}
      }
      XtVaSetValues(compose->window->compose_text,XmNeditable, TRUE, NULL);
      compose->in_ispell = FALSE;
    }
    else {
      fs_give((void **) &text);
      fs_give((void **) &tmp_file);
      return;
    }
    unlink(tmp_file);
    return;
  }

  /* otherwise we use /bin/spell */

  sprintf(command, SPELL_COMMAND,
	  (has_stop == TRUE) ? '+' : SPACECHAR,
	  (has_stop == TRUE) ? SPELLSTOPFILE : EMPTYSTR,
	  tmp_file); 

  push_cursor(WATCH_CURSOR);

  sprintf(buffer,MLGetLocalized(XtNmsgExecuting,MsgExecuting),command);
  mm_log(buffer, NIL);

  if((fp = popen(command,"w")) != NULL) {
    (void) chmod(tmp_file,S_IRWXU);
    fwrite(text,strlen(text),1,fp);
    pclose(fp);
  }
  else {
    fs_give((void **) &text);
    fs_give((void **) &tmp_file);
    pop_cursor();
    return;
  }

  if((fp = fopen(tmp_file,"r")) != NULL) {
    while((fgets(buffer,sizeof(buffer),fp)) != NULL) {
      buffer[strlen(buffer) - 1] = NUL_TERM;
      if(*buffer == NUL_TERM)
	continue;
      count ++;
      string_list = new_string_list();
      string_list->string = cpystr(buffer);
      if(prev == NULL)
	base = string_list;
      else
	prev->next = string_list;
      prev = string_list;
    }
    fclose(fp);
  }
  else
    mm_log(MLGetLocalized(XtNmsgSpellNoRead,MsgSpellNoRead),NIL);

  fs_give((void **) &text);
  unlink(tmp_file); 
  pop_cursor();
  sprintf(buffer,MLGetLocalized(XtNmsgNCorrections,MsgNCorrections),count);
  mm_log(buffer, NIL);
  if(base)
    make_text_spell_window(compose->window->compose_text, base);
  return;
}

#ifdef __STDC__
void link_attachment(MLCompose *compose, PART *part, BODY *body,
		     Binary_Buffer *binary_buffer)
#else
void link_attachment(compose,part,body,binary_buffer)
     MLCompose *compose;
     PART *part;
     BODY *body;
     Binary_Buffer *binary_buffer;
#endif
{
  Part_List *part_list;
  PART *current;
  BODY *subbody;
  Boolean attached = FALSE;
  
  add_attachment_contents_to_body(body,binary_buffer);

  /* 
   * See if there is a selected attachment. Then check to see if
   * it is a multipart. If so, then we add to that multipart.
   */

  if(compose->window->attach_list_selection && compose->part_list) {
    for(part_list = compose->part_list;
	part_list->partnumber != compose->window->attach_list_selection;
	part_list = part_list->next);
    subbody = &(part_list->part->body);
    if(subbody && subbody->type == TYPEMULTIPART) {
      current = subbody->contents.part;
      if(! current)
	subbody->contents.part = part;
      else {
	while(current->next)
	  current = current->next;
	current->next = part;
      }
      attached = TRUE;
    }
  }

  /* 
   * Wasn't a subpart, or we couldn't attach it as one. Attach to the
   * root part.
   */

  if(! attached) {
    if(compose->attachments == NULL) {
      compose->attachments = part;
    }
    else {
      current = compose->attachments;
      while(current->next)
	current = current->next;
      current->next = part;
    }
  }
  
  make_attachment_list(compose, TRUE);
  return;
}


#ifdef __STDC__
void compose_parameters(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_parameters(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  Part_List *part_list;
  PARAMETER **old = NULL;
  PARAMETER *new = NULL;
  char *oldstr = NULL;
  char *newstr = NULL;

  /* See if there is a selected attachment. */

  if(compose->window->attach_list_selection && compose->part_list) {
    for(part_list = compose->part_list;
	part_list->partnumber != compose->window->attach_list_selection;
	part_list = part_list->next)
      ;
    old = &part_list->part->body.parameter;
  }
  if(old == NULL)
    old = &compose->parameters;

  oldstr = param_to_str(*old);

  newstr = input_string(compose->window->shell,
			MLGetLocalized(XtNmsgPromptParams,MsgPromptParams),
			oldstr, PARAMSHELPFILE);
  if(newstr == NULL)
    return;
  if(*newstr == NUL_TERM) {
    fs_give((void **) &newstr);
    fs_give((void **) &oldstr);
    mail_free_body_parameter(old);
  }
  else {
    new = str_to_param(newstr);
    if(new != NULL) {
      mail_free_body_parameter(old);
      *old = new;
    }
  }
  fs_give((void **) &oldstr);
  fs_give((void **) &newstr);
  return;

}

/*
 * This is just to calculate how much space to allocate for storing
 * a text field. (Used in the print function). We over-allocate by
 * a little bit to make up for psuedo-tabs and line breaks.
 */

#ifdef __STDC__
unsigned long compose_field_approx_size(char *prefix, char *str, 
					unsigned long len)
#else
unsigned long compose_field_approx_size(prefix, str, len)
     char *prefix;
     char *str;
     unsigned long len;
#endif
{
  unsigned long retval = 0L;

  if(!str)
    return(0L);

  if(len == 0L)
    return((unsigned long) strlen(str));

  if(prefix) 
    retval = strlen(prefix);
  retval += (unsigned long) strlen(str) + 
    ((unsigned long) strlen(str) / len ) + (unsigned long) 16L;

  return(retval);
}

/* 
 * This is an undocumented interface. 
 * If you need it, you'll figure
 * it out.
 */

#ifdef __STDC__
void compose_smart_reply(ENVELOPE *env, char *newsgroups)
#else
void compose_smart_reply(env, newsgroups)
     ENVELOPE *env;
     char *newsgroups;
#endif
{
  FILE *fp;
  char *tmp_file;
  char mybuf[FILEBUFFLEN];
  char filename[MAXPATHLEN];
  char command[FILEBUFFLEN];
  ADDRESS *curr;

  if(!preferences.smart_reply || *preferences.smart_reply == NUL_TERM)
    return;

  if(!strstr(preferences.smart_reply,"%s"))
    return;

  tmp_file = (char *) tmpnam(filename);

  sprintf(command,preferences.smart_reply,tmp_file);

  if((fp = fopen(tmp_file,"w")) != NULL) {
    (void) chmod(tmp_file,S_IRWXU);

    fprintf(fp,"%s ",HEADER_TO);
    for(curr = env->to; curr; curr = curr->next) {
      local_make_addr_str(curr,mybuf);
      fprintf(fp,"%s ",mybuf);
      
    }
    fprintf(fp,LFSTR);

    fprintf(fp,"%s ",HEADER_CC);
    for(curr = env->cc; curr; curr = curr->next) {
      local_make_addr_str(curr,mybuf);
      fprintf(fp,"%s ",mybuf);
      
    }
    fprintf(fp,LFSTR);

    fprintf(fp,"%s ",HEADER_BCC);
    for(curr = env->bcc; curr; curr = curr->next) {
      local_make_addr_str(curr,mybuf);
      fprintf(fp,"%s ",mybuf);
      
    }
    fprintf(fp,LFSTR);

    fprintf(fp,"%s %s\n", HEADER_NEWSGROUPS, 
	    (newsgroups) ? newsgroups : EMPTYSTR);

    fprintf(fp,"%s %s\n", HEADER_SUBJECT, 
	    (env->subject) ? env->subject : EMPTYSTR);

    fclose(fp);
  }

  /* 
   *  External process generates one line (or nothing).
   *  If something, use it as the reply-to.
   */

  *mybuf = NUL_TERM;

  if((fp = popen(command,"r")) != NULL) {
    fgets(mybuf,sizeof(mybuf),fp);
    mybuf[strlen(mybuf) - 1] = NUL_TERM;
    if(*mybuf != NUL_TERM) {
      mail_free_address(&env->reply_to);
      env->reply_to = text_to_address(mybuf,EMPTYSTR);
    }
    pclose(fp);
  }

  unlink(tmp_file);
  return;
}

#ifdef __STDC__
void compose_print(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_print(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  char *s;
  char *wrapped_text;
  unsigned long size = 0L;

  char *to           = NULL;
  char *newsgroups   = NULL;
  char *subject      = NULL;
  char *cc           = NULL;
  char *bcc          = NULL;
  char *message_text = NULL;


  if(!preferences.print_command || *preferences.print_command == NUL_TERM) {
    mm_log(MLGetLocalized(XtNmsgNoPrintCommand,MsgNoPrintCommand),WARN);
    return;
  }

  push_cursor(WATCH_CURSOR);

  to            = GetTextField(compose->window->to_text);
  newsgroups    = GetTextField(compose->window->newsgroups_text);
  subject       = GetTextField(compose->window->subject_text);
  cc            = GetTextField(compose->window->cc_text);
  bcc           = GetTextField(compose->window->bcc_text);
  message_text  = GetTextField(compose->window->compose_text);

  s = fs_get(  strlen(to) + strlen(newsgroups) + strlen(subject)
	     + strlen(cc) + strlen(bcc)        + strlen(message_text)
	     + 1024);


  *s = NUL_TERM;

#define HEADER_MARGIN (COMPOSEWIDTH - 20)

  if(*to != NUL_TERM) {
    strcat(s,MLGetLocalized(XtNheaderTo,HeaderTo));
    strcat(s,TABSTR);
    wrapped_text = wrap_text(to, HEADER_MARGIN);
    strcat(s, wrapped_text);
    strcat(s, LFSTR);
  }
  if(*newsgroups != NUL_TERM) {
    strcat(s,MLGetLocalized(XtNheaderNewsgroups,HeaderNewsgroups));
    strcat(s,TABSTR);
    wrapped_text = wrap_text(newsgroups, HEADER_MARGIN);
    strcat(s, wrapped_text);
    strcat(s, LFSTR);
  }
  if(*subject != NUL_TERM) {
    strcat(s,MLGetLocalized(XtNheaderSubject,HeaderSubject));
    strcat(s,TABSTR);
    wrapped_text = wrap_text(subject, HEADER_MARGIN);
    strcat(s, wrapped_text);
    strcat(s, LFSTR);
  }
  if(*cc != NUL_TERM) {
    strcat(s,MLGetLocalized(XtNheaderCc,HeaderCc));
    strcat(s,TABSTR);
    wrapped_text = wrap_text(cc, HEADER_MARGIN);
    strcat(s, wrapped_text);
    strcat(s, LFSTR);
  }
  if(*bcc != NUL_TERM) {
    strcat(s,MLGetLocalized(XtNheaderBcc,HeaderBcc));
    strcat(s,TABSTR);
    wrapped_text = wrap_text(bcc, HEADER_MARGIN);
    strcat(s, wrapped_text);
    strcat(s, LFSTR);
  }
  strcat(s, LFSTR);

  if(*message_text != NUL_TERM) {
    wrapped_text = wrap_text(message_text, COMPOSEWIDTH - 1);
    strcat(s, wrapped_text);
    strcat(s, LFSTR);
  }

  if((write_to_pipe(preferences.print_command, NULL,s,strlen(s))) 
     != SYSCALL_SUCCESS)
    mm_log(MLGetLocalized(XtNmsgPrintFail,MsgPrintFail), WARN);
  else
    mm_log(MLGetLocalized(XtNmsgPrintSuccess,MsgPrintSuccess), NIL);
  
  fs_give((void **) &s);
  fs_give((void **) &to);
  fs_give((void **) &newsgroups);
  fs_give((void **) &subject);
  fs_give((void **) &cc);
  fs_give((void **) &bcc);
  fs_give((void **) &message_text);

  pop_cursor();

  return;
}



#ifdef __STDC__
void compose_mailbox_close(MAILSTREAM *mailstream)
#else
void compose_mailbox_close(mailstream)
     MAILSTREAM *mailstream;
#endif
{
  MLCompose *compose;

  if(session->compose == NULL)
    return;

  /* 
   * Run through the list of compose sessions searching for our mailstream. 
   * If it's ours, zero the message structure so it won't be referenced again.
   * (The message can still be composed -- we just won't set any flags at 
   * the end).
   */

  for(compose = session->compose ; compose; compose = compose->next) {
    if((compose->message != NULL) 
       && (compose->message->mailstream == mailstream)) {
      compose->message = NULL;
    }
  }
}


#ifdef __STDC__
void compose_options(Widget w, MLCompose *compose,  XtPointer xp)
#else
void compose_options(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  compose_preferences(compose);
  return;

}

Menu copts_menu[] = {

  { NULL, "accept", NUL_TERM,
      cwin_accept, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "save", NUL_TERM,
      cwin_save, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "dismiss", NUL_TERM,
      cwin_dismiss, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "HELP", NUL_TERM,
      cwin_help, NULL, 0, NULL, NULL, BTN_ON },

};

#ifdef __STDC__
void compose_preferences(MLCompose *compose)
#else
void compose_preferences(compose)
     MLCompose *compose;
#endif
{
  
  Arg args[ARGLISTSIZE];
  int n = 0;
  MLComposeOptions *options;
  MLComposeOptionsWindow *win;
  
  if(compose->options->window != NULL) {
    win = compose->options->window;
    if(win->destroyed == FALSE) {
      if(win->is_realized == TRUE)
	de_iconify(win->shell);
      else
	XtPopup(win->shell, XtGrabNone);
      return;
    }
  }

  options = compose->options;
  options->window = (MLComposeOptionsWindow *)
    fs_get(sizeof(MLComposeOptionsWindow));


  win = options->window;
  win->buttonlist = NULL;
  win->destroyed = FALSE;
  win->is_realized = FALSE;

  if(preferences.autoPlace == TRUE) {
    XtSetArg(args[n], XtNx, -1000); n ++;
    XtSetArg(args[n], XtNy, -1000); n ++;
  }

  XtSetArg (args[n], XmNdeleteResponse, XmDO_NOTHING); n++;        
  win->shell = XtCreatePopupShell("composeOptions",
				  topLevelShellWidgetClass, 
				  compose->window->shell,
				  args, n); 
  AddDestroyCallback (win->shell);
  n = 0;

  setup_editres(win->shell);

  if(ml_icon != (Pixmap) None)
    XtVaSetValues(win->shell,
		  XmNiconPixmap,ml_icon,
		  NULL);


  win->form = XmCreateForm(win->shell, "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 ++;
  win->menubar = XmCreateMenuBar(win->form, "menubar", args, n); n = 0;
  XtManageChild(win->menubar);


  make_buttons(&win->buttonlist, NULL, 
	       win->menubar, copts_menu,
	       XtNumber(copts_menu),
	       BTN_ON, (XtPointer) compose, 
	       ROOTMENULEVEL);



  win->mailhost =
    create_text_field(win->form, win->menubar,
		      "mailhost", 
		      options->mailhost,
		      0, NULL, NULL);

  win->nntphost =
    create_text_field(win->form, win->mailhost,
		      "nntphost", 
		      options->nntphost,
		      0, NULL, NULL);


  win->domain =
    create_text_field(win->form, win->nntphost,
		      "domain", 
		      options->domain,
		      0, NULL, NULL);


  win->replyto =
    create_text_field(win->form, win->domain,
		      "replyto",
		      options->replyto,
		      0, NULL, NULL);

  win->sigfile =
    create_text_field(win->form, win->replyto,
		      "sigfile",
		      options->sigfile,
		      0, NULL, NULL);

  win->outlog =
    create_text_field(win->form, win->sigfile,
		      "outlog",
		      options->outlog,
		      0, NULL, NULL);


  win->defcc =
    create_text_field(win->form, win->outlog,
		      "defcc",
		      options->defcc,
		      0, NULL, NULL);

  win->defbcc =
    create_text_field(win->form, win->defcc,
		      "defbcc", 
		      options->defbcc,
		      0, NULL, NULL);


  win->charset =
    create_text_field(win->form, win->defbcc,
		      "charset", 
		      options->charset,
		      0, NULL, NULL);

 

  win->language =
    create_text_field(win->form, win->charset,
		      "language", 
		      options->language,
		      0, NULL, NULL);


  XtSetArg(args[n], XmNadjustMargin, FALSE);      n ++;
  XtSetArg(args[n], XmNmarginWidth, 0);           n ++;
  XtSetArg(args[n], XmNborderWidth, 0);           n ++;

  XtSetArg(args[n], XmNnumColumns, 3);                       n ++;
  XtSetArg(args[n], XmNtopAttachment,   XmATTACH_WIDGET);    n ++;
  XtSetArg(args[n], XmNtopWidget, win->language);            n ++;
  XtSetArg(args[n], XmNleftAttachment,  XmATTACH_FORM);      n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM);      n ++;
  XtSetArg(args[n], XmNorientation,     XmVERTICAL);         n ++;
  XtSetArg(args[n], XmNpacking, XmPACK_COLUMN);              n ++;
  win->rowcol = XtCreateWidget("rowcol",
				   xmRowColumnWidgetClass,
				   win->form, args, n );
  n = 0;
  XtManageChild(win->rowcol);
  XtSetArg(args[n], XmNborderWidth, 0);           n ++;
  XtSetArg(args[n], XmNtraversalOn, FALSE);       n ++;
  win->verbose = XmCreateToggleButton(win->rowcol, "verbose_toggle",
				      args, n); n = 0;
  XtManageChild(win->verbose);

  XtAddCallback(win->verbose, XmNvalueChangedCallback, 
		(XtCallbackProc) cwin_verbose, compose);

  XtSetArg(args[n], XmNborderWidth, 0);           n ++;
  XtSetArg(args[n], XmNtraversalOn, FALSE);       n ++;
  win->keep_open = XmCreateToggleButton(win->rowcol, "keep_open_toggle",
					args, n); n = 0;
  XtManageChild(win->keep_open);

  XtAddCallback(win->keep_open, XmNvalueChangedCallback, 
		(XtCallbackProc) cwin_keep_open, compose);

  XtSetArg(args[n], XmNborderWidth, 0);           n ++;
  XtSetArg(args[n], XmNtraversalOn, FALSE);       n ++;
  win->send_eight = XmCreateToggleButton(win->rowcol, "send_eight_toggle",
					 args, n); n = 0;
  XtManageChild(win->send_eight);

  XtAddCallback(win->send_eight, XmNvalueChangedCallback, 
		(XtCallbackProc) cwin_send_eight, compose);

  XtSetArg(args[n], XmNborderWidth, 0);           n ++;
  XtSetArg(args[n], XmNtraversalOn, FALSE);       n ++;
  win->word_wrap = XmCreateToggleButton(win->rowcol, "word_wrap_toggle",
					args, n); n = 0;
  XtManageChild(win->word_wrap);

  XtAddCallback(win->word_wrap, XmNvalueChangedCallback, 
		(XtCallbackProc) cwin_word_wrap, compose);



  XtSetArg(args[n], XmNborderWidth, 0);           n ++;
  XtSetArg(args[n], XmNtraversalOn, FALSE);       n ++;
  win->message_log = XmCreateToggleButton(win->rowcol, "message_log_toggle",
					  args, n); n = 0;
  XtManageChild(win->message_log);

  XtAddCallback(win->message_log, XmNvalueChangedCallback, 
		(XtCallbackProc) cwin_message_log, compose);

  XtSetArg(args[n], XmNborderWidth, 0);           n ++;
  XtSetArg(args[n], XmNtraversalOn, FALSE);       n ++;
  win->log_attachments = XmCreateToggleButton(win->rowcol, 
					      "log_attachments_toggle",
					      args, n); n = 0;
  XtManageChild(win->log_attachments);

  XtAddCallback(win->log_attachments, XmNvalueChangedCallback, 
		(XtCallbackProc) cwin_log_attachments, compose);



  XtAddCallback(win->shell, XmNpopdownCallback,
		(XtCallbackProc) cwin_popdown, compose);

  XtAddCallback(win->shell, XmNdestroyCallback,
		(XtCallbackProc) cwin_destroy, compose);

  set_initial_buttons(compose);


  XtManageChild(win->form);
  XtManageChild(win->shell);
  XtPopup(win->shell, XtGrabNone);
  position_popup_widget(win->shell, FALSE);
  win->is_realized = TRUE; 
  return;
}


#ifdef __STDC__
void cwin_help(Widget w, MLCompose *compose, XtPointer xp)
#else
void cwin_help(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  help(compose->options->window->shell,COPTHELPFILE);
  return;
}

#ifdef __STDC__
void set_initial_buttons(MLCompose *compose)
#else
void set_initial_buttons(compose)
     MLCompose *compose;
#endif
{
  MLComposeOptions *options = compose->options;
  MLComposeOptionsWindow *win = compose->options->window;

  XmToggleButtonSetState(win->verbose,options->verbose,FALSE);
  XmToggleButtonSetState(win->keep_open,options->keep_open,FALSE);
  XmToggleButtonSetState(win->send_eight,options->send_eight,FALSE);
  XmToggleButtonSetState(win->word_wrap,options->word_wrap,FALSE);
  XmToggleButtonSetState(win->message_log,options->message_log,FALSE);
  XmToggleButtonSetState(win->log_attachments,options->log_attachments,FALSE);
  return;
}


#ifdef __STDC__
void cwin_accept(Widget w, MLCompose *compose, XtPointer xp)
#else
void cwin_accept(w, compose, xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  get_compose_options(compose);

  cwin_dismiss(w,compose,xp);
  return;
}



#ifdef __STDC__
void cwin_save(Widget w, MLCompose *compose, XtPointer xp)
#else
void cwin_save(w, compose, xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  MLComposeOptions *options = compose->options;
  get_compose_options(compose);

  fs_give((void **) &preferences.smtp_server);
  preferences.smtp_server = cpystr(options->mailhost);
  fs_give((void **) &preferences.nntp_server);
  preferences.nntp_server = cpystr(options->nntphost);
  fs_give((void **) &preferences.default_domain);
  preferences.default_domain = cpystr(options->domain);
  fs_give((void **) &preferences.reply_address);
  preferences.reply_address = cpystr(options->replyto);
  fs_give((void **) &preferences.charset);
  preferences.charset = cpystr(options->charset);
  fs_give((void **) &preferences.signature_file);
  preferences.signature_file = cpystr(options->sigfile);
  fs_give((void **) &preferences.language);
  preferences.language = cpystr(options->language);
  fs_give((void **) &preferences.sendlog);
  preferences.sendlog = cpystr(options->outlog);
  fs_give((void **) &preferences.default_cc);
  preferences.default_cc = cpystr(options->defcc);
  fs_give((void **) &preferences.default_bcc);
  preferences.default_bcc = cpystr(options->defbcc);

  preferences.smtp_debug = options->verbose;
  preferences.keep_open  = options->keep_open;
  preferences.send_eight = options->send_eight;
  preferences.word_wrap  = options->word_wrap;
  preferences.logit      = options->message_log;
  preferences.log_full   = options->log_attachments;

  save_defaults();
  cwin_dismiss(w,compose,xp);

}


#ifdef __STDC__
void cwin_dismiss(Widget w, MLCompose *compose, XtPointer xp)
#else
void cwin_dismiss(w, compose, xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  XtPopdown(compose->options->window->shell);
  compose->options->window->is_realized = FALSE;
  return;

}

#ifdef __STDC__
void cwin_popdown(Widget w, MLCompose *compose, XtPointer xp)
#else
void cwin_popdown(w, compose, xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  compose->options->window->is_realized = FALSE;
  return;
}

#ifdef __STDC__
void cwin_destroy(Widget w, MLCompose *compose, XtPointer xp)
#else
void cwin_destroy(w, compose, xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  compose->options->window->is_realized = FALSE;
  compose->options->window->destroyed = TRUE;
  fs_give((void **) &compose->options->window->buttonlist);
  return;

}





#ifdef __STDC__
void get_compose_options(MLCompose *compose)
#else
void get_compose_options(compose)
     MLCompose *compose;
#endif
{
  MLComposeOptions *options = compose->options;

  fs_give((void **) &options->mailhost);
  options->mailhost = GetTextField(options->window->mailhost);
  fs_give((void **) &options->nntphost);
  options->nntphost = GetTextField(options->window->nntphost);
  fs_give((void **) &options->domain);
  options->domain = GetTextField(options->window->domain);
  fs_give((void **) &options->replyto);
  options->replyto = GetTextField(options->window->replyto);
  fs_give((void **) &options->sigfile);
  options->sigfile = GetTextField(options->window->sigfile);
  fs_give((void **) &options->charset);
  options->charset = GetTextField(options->window->charset);
  fs_give((void **) &options->language);
  options->language = GetTextField(options->window->language);
  fs_give((void **) &options->outlog);
  options->outlog = GetTextField(options->window->outlog);
  fs_give((void **) &options->defcc);
  options->defcc = GetTextField(options->window->defcc);
  fs_give((void **) &options->defbcc);
  options->defbcc = GetTextField(options->window->defbcc);

  if(options->word_wrap != options->word_wrap_tmp)
    if(options->word_wrap_tmp == TRUE)
      compose_wrap_on(NULL,compose,NULL);
    else
      compose_wrap_off(NULL,compose,NULL);

  options->verbose = options->verbose_tmp;
  options->keep_open = options->keep_open_tmp;
  options->send_eight = options->send_eight_tmp;
  options->word_wrap = options->word_wrap_tmp;
  options->message_log = options->message_log_tmp;
  options->log_attachments = options->log_attachments_tmp;

  return;

}



#ifdef __STDC__
void cwin_verbose(Widget w,MLCompose *compose,
		  XmToggleButtonCallbackStruct *xp)
#else
void cwin_verbose(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XmToggleButtonCallbackStruct *xp;
#endif
{
  MLComposeOptions *options = compose->options;
  if(xp->set == TRUE)
    options->verbose_tmp = TRUE;
  else
    options->verbose_tmp = FALSE;
  return;
}


#ifdef __STDC__
void cwin_keep_open(Widget w,MLCompose *compose,
		  XmToggleButtonCallbackStruct *xp)
#else
void cwin_keep_open(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XmToggleButtonCallbackStruct *xp;
#endif
{
  MLComposeOptions *options = compose->options;
  if(xp->set == TRUE)
    options->keep_open_tmp = TRUE;
  else
    options->keep_open_tmp = FALSE;
  return;
}

#ifdef __STDC__
void cwin_send_eight(Widget w,MLCompose *compose,
		  XmToggleButtonCallbackStruct *xp)
#else
void cwin_send_eight(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XmToggleButtonCallbackStruct *xp;
#endif
{
  MLComposeOptions *options = compose->options;
  if(xp->set == TRUE)
    options->send_eight_tmp = TRUE;
  else
    options->send_eight_tmp = FALSE;
  return;
}

#ifdef __STDC__
void cwin_word_wrap(Widget w,MLCompose *compose,
		  XmToggleButtonCallbackStruct *xp)
#else
void cwin_word_wrap(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XmToggleButtonCallbackStruct *xp;
#endif
{
  MLComposeOptions *options = compose->options;
  if(xp->set == TRUE)
    options->word_wrap_tmp = TRUE;
  else
    options->word_wrap_tmp = FALSE;
  return;
}

#ifdef __STDC__
void cwin_message_log(Widget w,MLCompose *compose,
		  XmToggleButtonCallbackStruct *xp)
#else
void cwin_message_log(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XmToggleButtonCallbackStruct *xp;
#endif
{
  MLComposeOptions *options = compose->options;
  if(xp->set == TRUE)
    options->message_log_tmp = TRUE;
  else
    options->message_log_tmp = FALSE;
  return;
}

#ifdef __STDC__
void cwin_log_attachments(Widget w,MLCompose *compose,
		  XmToggleButtonCallbackStruct *xp)
#else
void cwin_log_attachments(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XmToggleButtonCallbackStruct *xp;
#endif
{
  MLComposeOptions *options = compose->options;
  if(xp->set == TRUE)
    options->log_attachments_tmp = TRUE;
  else
    options->log_attachments_tmp = FALSE;
  return;
}



#ifdef __STDC__
void compose_reply_insert(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_reply_insert(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  Read *read;
  XmTextPosition left, right;
  char *the_text = NULL;
  char *tmp;
  Boolean found = FALSE;
  char buffer[FILEBUFFLEN];

  push_cursor(WATCH_CURSOR);

  for(read = session->read; read != NULL; read = read->next) {
    if(read->is_realized == TRUE) {
      XmTextGetSelectionPosition(read->read_text,&left,&right);
      if(left != right) {
	tmp = XmTextGetSelection(read->read_text);
	the_text = copywrap(tmp, 
			    (COMPOSEWIDTH - 2) 
			    - strlen(preferences.reply_prefix));
	fs_give((void **) &tmp);
	found = TRUE;
      }
    }
  }
  if(! found) {
    if(compose->reply_text) {
      /* add the attribution and original message to the reply body */
      memset(buffer, 0, FILEBUFFLEN);
      make_attribution_line(compose->message,buffer);
      if(*buffer) {
	strcat(buffer,"\n");
	text_blast(compose->window->compose_text,
		   copywrap(buffer,COMPOSEWIDTH));
      }
      the_text = copywrap(compose->reply_text, (COMPOSEWIDTH - 2) 
			  - strlen(preferences.reply_prefix));
      found ++;
    }
  }
  if((found)  && (*the_text != NUL_TERM)) {
    tmp = add_prefix(the_text);
    left = XmTextGetInsertionPosition(compose->window->compose_text);
    right = left + strlen(tmp);
    text_blast(compose->window->compose_text,tmp);
    fs_give((void **) &tmp);
  }
  if(the_text)
    fs_give((void **) &the_text);

  pop_cursor();
  return;
}


#ifdef __STDC__
void compose_cut(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_cut(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  XmTextCut(compose->window->compose_text, 
	     XtLastTimestampProcessed(display));
  return;
}

#ifdef __STDC__
void compose_copy(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_copy(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  XmTextCopy(compose->window->compose_text, 
	     XtLastTimestampProcessed(display));
  return;

}

#ifdef __STDC__
void compose_paste(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_paste(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  XmTextPaste(compose->window->compose_text);
  return;
}


#ifdef __STDC__
void compose_save_draft(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_save_draft(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  FILE *fp;
  char *filename;
  Boolean append_it;
  char *tmp;

  filename = file_select(compose->window->shell, NULL, DRAFT_FILEPAT, 
			 DRAFT_TEXT,
			 TRUE, &append_it);
  if(filename == NULL)
     return;

  push_cursor(WATCH_CURSOR);


  if((fp = fopen(filename,(append_it == TRUE) ? "a" : "w")) != NULL) {
    (void) chmod(filename,S_IRWXU);
    fprintf(fp,"%s\n",ML_DRAFT_HEADER);

    fprintf(fp,"%s\t%s\n",HEADER_FROM,compose->options->replyto);

    tmp = XmTextGetString(compose->window->to_text);
    if(*tmp)
      fprintf(fp,"%s\t%s\n",HEADER_TO,tmp);
    fs_give((void **) &tmp);

    tmp = XmTextGetString(compose->window->newsgroups_text);
    if(*tmp)
      fprintf(fp,"%s\t%s\n",HEADER_NEWSGROUPS,tmp);
    fs_give((void **) &tmp);

    tmp = XmTextGetString(compose->window->subject_text);
    if(*tmp)
      fprintf(fp,"%s\t%s\n",HEADER_SUBJECT,tmp);
    fs_give((void **) &tmp);

    tmp = XmTextGetString(compose->window->cc_text);
    if(*tmp)
      fprintf(fp,"%s\t%s\n",HEADER_CC,tmp);
    fs_give((void **) &tmp);

    tmp = XmTextGetString(compose->window->bcc_text);
    if(*tmp)
      fprintf(fp,"%s\t%s\n",HEADER_BCC,tmp);
    fs_give((void **) &tmp);

    fprintf(fp,"\n");

    tmp = XmTextGetString(compose->window->compose_text);
    fprintf(fp,"%s",tmp);
    fs_give((void **) &tmp);
    fclose(fp);
  }

  fs_give((void **) &filename);
  pop_cursor();
  return;
}


#ifdef __STDC__
void compose_load_draft(Widget w, MLCompose *compose, XtPointer xp)
#else
void compose_load_draft(w,compose,xp)
     Widget w;
     MLCompose *compose;
     XtPointer xp;
#endif
{
  FILE *fp;
  char *filename;
  char buffer[2 * FILEBUFFLEN];
  struct stat st;
  int c;
  char *str;
  char *dst;

  filename = file_select(compose->window->shell, NULL, DRAFT_FILEPAT, 
			 DRAFT_TEXT, FALSE, NULL);
  if(filename == NULL)
     return;

  if(stat(filename,&st) != SYSCALL_SUCCESS) {
    fs_give((void **) &filename);
    return;
  }

  if((fp = fopen(filename,"r")) != NULL) {
    fgets(buffer,sizeof(buffer),fp);
    buffer[strlen(buffer) - 1] = NUL_TERM;
    if(strcmp(buffer,ML_DRAFT_HEADER) != STRMATCH) {
      fs_give((void **) &filename);
      mm_log(MLGetLocalized(XtNmsgNotDraft,MsgNotDraft),ERROR);
      return;
    }
    push_cursor(WATCH_CURSOR);
    XtVaSetValues(compose->window->shell, 
		  XmNtitle, MLGetLocalized(XtNstrDraft,StrDraft), NULL);


    while(fgets(buffer,sizeof(buffer),fp) != NULL) {
      buffer[strlen(buffer) - 1] = NUL_TERM;
      if(! strlen(buffer))
	break;

      if(strncmp(buffer,HEADER_FROM,strlen(HEADER_FROM)) == STRMATCH) {
	fs_give((void **) &compose->options->replyto);
	compose->options->replyto = cpystr(buffer + strlen(HEADER_FROM) + 1);
      }

      if(strncmp(buffer,HEADER_TO,strlen(HEADER_TO)) == STRMATCH) 
	AppendText(compose->window->to_text,buffer + strlen(HEADER_TO) + 1);

      if(strncmp(buffer,HEADER_NEWSGROUPS,
		 strlen(HEADER_NEWSGROUPS)) == STRMATCH) 
	AppendText(compose->window->newsgroups_text,
		   buffer + strlen(HEADER_NEWSGROUPS) + 1);

      if(strncmp(buffer,HEADER_SUBJECT,strlen(HEADER_SUBJECT)) == STRMATCH) 
	AppendText(compose->window->subject_text,
			buffer + strlen(HEADER_SUBJECT) + 1);
      if(strncmp(buffer,HEADER_CC,strlen(HEADER_CC)) == STRMATCH) 
	AppendText(compose->window->cc_text,buffer + strlen(HEADER_CC) + 1);
      if(strncmp(buffer,HEADER_BCC,strlen(HEADER_BCC)) == STRMATCH) 
	AppendText(compose->window->bcc_text,buffer + strlen(HEADER_BCC) + 1);
    }

    str = (char *) fs_get(st.st_size + 1);
    dst = str;

    while((c = fgetc(fp)) != EOF) { 
      *dst = c;
      dst ++;
    }
    *dst = NUL_TERM;
    fclose(fp);
    AppendText(compose->window->compose_text,str);
    fs_give((void **) &str);
  }


  fs_give((void **) &filename);
  pop_cursor();
  return;
}


#ifdef __STDC__
void compose_pop(Widget w, XtPointer xp)
#else
void compose_pop(w,xp)
     Widget w;
     XtPointer xp;
#endif
{
  MLCompose *compose;
  for(compose = session->compose; compose; compose = compose->next) {
    if(compose->window->compose_text == w) {
      compose_destroy_current(w,compose,NULL);
      break;
    }
  }
  return;
}




syntax highlighted by Code2HTML, v. 0.9.1