static char rcsid[] = "@(#)$Id: reply.c,v 1.9 2006/06/25 10:41:05 hurtta Exp $"; /****************************************************************************** * The Elm (ME+) Mail System - $Revision: 1.9 $ $State: Exp $ * * Author: Kari Hurtta * or Kari Hurtta *****************************************************************************/ #include "def_list.h" #include "s_me.h" #include "mailerlib.h" static unsigned char *s2us P_((char *str)); static unsigned char *s2us(str) char *str; { return (unsigned char *)str; } DEBUG_VAR(Debug,__FILE__,"mail"); static int reply_to_combined P_((struct menu_context *page, struct menu_context *prompt_area, struct MailboxView *mailbox, int index, struct header_rec *current_header, FILE *F, struct AliasView *aview, const struct url *url_to, struct addr_item *to_addr, struct addr_item *cc_addr)); static int reply_to_combined(page,prompt_area,mailbox,index,current_header, F,aview,url_to,to_addr,cc_addr) struct menu_context *page; struct menu_context *prompt_area; struct MailboxView *mailbox; int index; struct header_rec *current_header; FILE *F; struct AliasView *aview; const struct url *url_to; struct addr_item *to_addr; struct addr_item *cc_addr; { struct mailer_info *mailer_info = get_mailer_info(); struct mailing_headers headers; int r = 0; zero_mailing_headers(&headers); if (!mailer_info) { DPRINT(Debug,5,(&Debug, "reply_to_combined: No mailer_info\n")); return 0; } if (url_to) { /* When replying, possible body given on URL is ignored */ r = set_mailing_headers_from_url(&headers,url_to,mailer_info); if (!r) { DPRINT(Debug,5,(&Debug, "reply_to_combined: set_mailing_headers_from_url failed!\n")); goto fail; } } expanded_address_from_items(& headers.to, to_addr); expanded_address_from_items(& headers.cc, cc_addr); if (!headers.subject && current_header->subject) { struct string * X = skip_ascii_head_from_string(current_header-> subject, s2us("Re: "),1); headers.subject = format_string(FRM("Re: %S"),X); free_string(&X); } r = send_msg_middle2(&headers,index,MAIL_EDIT_MSG | MAIL_REPLYING, NO,mailer_info,mailbox,aview, page,prompt_area,NULL); if (!r) { DPRINT(Debug,5,(&Debug, "reply_to_combined: send_msg_middle2 failed!\n")); goto fail; } fail: free_mailing_headers(&headers); free_mailer_info(&mailer_info); DPRINT(Debug,5,(&Debug, "reply_to_combined=%d\n",r)); return r; } static int reply_to_url_mailing P_((struct menu_context *page, struct menu_context *prompt_area, struct MailboxView *mailbox, int index, struct header_rec *current_header, FILE *F, const struct url *url, struct AliasView *aview, int ask_send)); static int reply_to_url_mailing(page,prompt_area,mailbox,index,current_header, F,url,aview,ask_send) struct menu_context *page; struct menu_context *prompt_area; struct MailboxView *mailbox; int index; struct header_rec *current_header; FILE *F; CONST struct url *url; struct AliasView *aview; int ask_send; { struct mailer_info *mailer_info = get_mailer_info(); struct mailing_headers headers; int r; zero_mailing_headers(&headers); if (!mailer_info) { DPRINT(Debug,5,(&Debug, "reply_to_url_mailing: No mailer_info\n")); return 0; } /* When replying, possible body given on URL is ignored */ r = set_mailing_headers_from_url(&headers,url,mailer_info); if (!r) { DPRINT(Debug,5,(&Debug, "reply_to_url_mailing: set_mailing_headers_from_url failed!\n")); goto fail; } if (!headers.subject && current_header->subject) { struct string * X = skip_ascii_head_from_string(current_header-> subject, s2us("Re: "),1); headers.subject = format_string(FRM("Re: %S"),X); free_string(&X); } r = send_msg_middle2(&headers,index, MAIL_EDIT_MSG | MAIL_REPLYING | (ask_send ? MAIL_ASK_SEND : 0), NO,mailer_info,mailbox,aview, page,prompt_area,NULL); if (!r) { DPRINT(Debug,5,(&Debug, "reply_to_url_mailing: send_msg_middle2 failed!\n")); goto fail; } fail: free_mailing_headers(&headers); free_mailer_info(&mailer_info); DPRINT(Debug,5,(&Debug, "reply_to_url_mailing=%d\n",r)); return r; } static int reply_to_url P_((struct menu_context *page, struct menu_context *prompt_area, struct MailboxView *mailbox, int index, struct header_rec *current_header, FILE *F, const struct url *url, struct AliasView *aview, int ask_send)); static int reply_to_url(page,prompt_area,mailbox,index,current_header, F,url,aview,ask_send) struct menu_context *page; struct menu_context *prompt_area; struct MailboxView *mailbox; int index; struct header_rec *current_header; FILE *F; CONST struct url *url; struct AliasView *aview; int ask_send; { int r = 0; enum url_type T = get_url_type(url); struct string *url_text = raw_from_url(url); switch (T) { case url_unknown: if (url_text) lib_error(CATGETS(elm_msg_cat, MeSet, MeUnknownUrlReply, "Unsupported URL type. Can not reply to %S"), url_text); goto failure; case url_mailing: r = reply_to_url_mailing(page,prompt_area,mailbox,index,current_header, F,url,aview,ask_send); } failure: if (url_text) free_string(& (url_text)); return r; } /* ----------------------------------------------------------------------- */ struct menu_anon_param { struct header_rec * current_header; }; enum { reply_mp_islist, reply_mp_param, reply_mp_COUNT }; #if ANSI_C #define S_(x) static x; #else #define S_(x) #endif S_(subpage_simple_redraw sb_update_reply_title) static int sb_update_reply_title(ptr,list) struct menu_context *ptr; struct menu_param *list; { menu_ClearScreen(ptr); menu_print_format_center(ptr,0, CATGETS(elm_msg_cat, MeSet, MeGenericReplyTitle, "Generic reply")); return 1; /* title completed */ } S_(subpage_simple_redraw sb_update_reply_menu) static int sb_update_reply_menu(ptr,list) struct menu_context *ptr; struct menu_param *list; { int islist = mp_lookup_integer(list,reply_mp_islist); menu_ClearScreen(ptr); if (islist) menu_print_format_center(ptr,0, CATGETS(elm_msg_cat, MeSet, MeGenericReplyMenu1, "Reply to a)ll, l)ist, list o)wner, au(t)hor (from address) or go to i)index")); else menu_print_format_center(ptr,0, CATGETS(elm_msg_cat, MeSet, MeGenericReplyNoList1, "Reply to a)ll, au(t)hor (from address) or go to i)index")); return 1; /* title completed */ } S_(subpage_simple_redraw sb_update_reply_info) static int sb_update_reply_info(ptr,list) struct menu_context *ptr; struct menu_param *list; { struct menu_anon_param *A = mp_lookup_anon(list,reply_mp_param); int line = 0; int LINES, COLUMNS; menu_get_sizes(ptr, &LINES, &COLUMNS); menu_ClearScreen(ptr); if (A->current_header->from) { int ph = give_dt_enumerate_as_int(&phrase_display_mode); struct addr_item *frm; for (frm = A->current_header->from; frm->addr && frm->fullname; frm++) { if (frm == A->current_header->from) menu_PutLineX(ptr,line,0,CATGETS(elm_msg_cat, MeSet, MeListInfoAuthor, "Author : ")); else menu_PutLineX(ptr,line,0,FRM( " : ")); if (string_len(frm->fullname) > 0) { if (ph) menu_Writechar(ptr,'"'); menu_Write_to_screen(ptr,FRM("%S"),frm->fullname); if (ph) menu_Writechar(ptr,'"'); menu_Writechar(ptr,' '); } menu_Write_to_screen(ptr,FRM("<%s>"),frm->addr); if (string_len(frm->comment) > 0) { menu_Writechar(ptr,' '); menu_Writechar(ptr,'('); menu_Write_to_screen(ptr,FRM("%S"),frm->comment); menu_Writechar(ptr,')'); } line++; } line++; } if (A->current_header->list_info) { if (A->current_header->list_info->list_phrase) { menu_PutLineX(ptr,line,0,CATGETS(elm_msg_cat, MeSet, MeListInfoListName, "List name : %S"), A->current_header->list_info->list_phrase); line++; } if (A->current_header->list_info->list_id) { menu_PutLineX(ptr,line,0,CATGETS(elm_msg_cat, MeSet, MeListInfoListID, "List ID : %s"), A->current_header->list_info->list_id); line++; } if (A->current_header->list_info->list_post_no) { if (line) line++; if (A->current_header->list_info->list_post_no_comment) { menu_print_format_center(ptr,line,CATGETS(elm_msg_cat, MeSet, MeListInfoListNoPostX, "Mailing list do not accept postings (%S)"), A->current_header->list_info->list_post_no_comment); } else { menu_print_format_center(ptr,line,CATGETS(elm_msg_cat, MeSet, MeListInfoListNoPost, "Mailing list do not accept postings.")); } line += 2; } if (A->current_header->list_info->list_post_len > 0) { int i; if (line) line++; for ( i = 0; i < A->current_header->list_info->list_post_len; i++) { struct string * X = raw_from_url (A->current_header->list_info->list_post[i].url); if (!X) X = format_string(FRM("?ERROR?")); if (0 == i) { if (A->current_header->list_info->list_post[i].comment) menu_PutLineX(ptr,line,0,CATGETS(elm_msg_cat, MeSet, MeListInfoListPost, "List post : <%S> %S"), X,A->current_header->list_info->list_post[i].comment); else menu_PutLineX(ptr,line,0,CATGETS(elm_msg_cat, MeSet, MeListInfoListPost0, "List post : <%S>"), X); } else { if (A->current_header->list_info->list_post[i].comment) menu_PutLineX(ptr,line,0,FRM( " : <%S> (%S)"), X,A->current_header->list_info->list_post[i].comment); else menu_PutLineX(ptr,line,0,FRM( " : <%S>"), X); } free_string(&X); line++; } } if (A->current_header->list_info->list_owner_len > 0) { int i; for ( i = 0; i < A->current_header->list_info->list_owner_len; i++) { struct string * X = raw_from_url (A->current_header->list_info->list_owner[i].url); if (!X) X = format_string(FRM("?ERROR?")); if (0 == i) { if (A->current_header->list_info->list_owner[i].comment) menu_PutLineX(ptr,line,0,CATGETS(elm_msg_cat, MeSet, MeListInfoListOwner, "List owner : <%S> (%S)"), X,A->current_header->list_info->list_owner[i].comment); else menu_PutLineX(ptr,line,0,CATGETS(elm_msg_cat, MeSet, MeListInfoListOwner0, "List owner : <%S>"), X); } else { if (A->current_header->list_info->list_owner[i].comment) menu_PutLineX(ptr,line,0,FRM( " : <%S> (%S)"), X,A->current_header->list_info->list_owner[i].comment); else menu_PutLineX(ptr,line,0,FRM( " : <%S>"), X); } free_string(&X); line++; } } } return 1; /* info completed */ } static void set_reply_screen P_((struct menu_context *page, struct screen_parts *LOC, struct menu_param *LIST)); static void set_reply_screen(page,LOC, LIST) struct menu_context *page; struct screen_parts *LOC; struct menu_param *LIST; { int LINES, COLUMNS; menu_get_sizes(page,&LINES, &COLUMNS); /* 1) Title part */ if (! LOC->title_page) LOC->title_page = new_menu_subpage(page,0,2,sb_update_reply_title, LIST); else menu_subpage_relocate(LOC->title_page,page,0,2); /* 2) Info part */ if (! LOC->header_page) LOC->header_page = new_menu_subpage(page,2,LINES-8, sb_update_reply_info,LIST); else menu_subpage_relocate(LOC->header_page,page,2,LINES-8); /* 3) Menu part */ if (LOC->menu_page) menu_subpage_relocate(LOC->menu_page,page,LINES-6,2); else LOC->menu_page = new_menu_subpage(page,LINES-6,2, sb_update_reply_menu,LIST); /* 4) Prompt part */ if (LOC->prompt_page) menu_subpage_relocate(LOC->prompt_page,page,LINES-4,4); else LOC->prompt_page = new_menu_subpage(page,LINES-4,4, subpage_simple_noredraw,LIST); } static void check_reply_screen P_((struct screen_parts *LOC, struct menu_param *list)); static void check_reply_screen(LOC,list) struct screen_parts *LOC; struct menu_param *list; { /* Title area */ if (menu_resized(LOC->title_page)) { DPRINT(Debug,1, (&Debug, "title page resized\n")); } if (menu_need_redraw(LOC->title_page)) { DPRINT(Debug,1, (&Debug, "title page redraw???\n")); sb_update_reply_title(LOC->title_page,list); } /* Menu area */ if (menu_resized(LOC->menu_page)) { DPRINT(Debug,1, (&Debug, "menu page resized\n")); } if (menu_need_redraw(LOC->menu_page)) { DPRINT(Debug,1, (&Debug, "menu page redraw\n")); sb_update_reply_menu(LOC->menu_page,list); } /* Prompt area */ if (menu_resized(LOC->prompt_page)) { DPRINT(Debug,1, (&Debug, "prompt page resized\n")); } if (menu_need_redraw(LOC->prompt_page)) { DPRINT(Debug,7, (&Debug, "prompt page redraw\n")); menu_ClearScreen(LOC->prompt_page); show_last_error(); /* for those operations that have to * clear the footer except for a message. */ } if (menu_resized(LOC->header_page)) { DPRINT(Debug,1, (&Debug, "info page resized\n")); menu_trigger_redraw(LOC->header_page); } if (menu_need_redraw(LOC->header_page)) { DPRINT(Debug,7, (&Debug, "info page redraw\n")); sb_update_reply_info(LOC->header_page,list); } } static void reply_to_all P_((struct header_rec *current_header, FILE *F,int index, struct MailboxView *mailbox, struct AliasView *aview, struct menu_context *page, struct menu_context *prompt_area)); static void reply_to_all(current_header,F,index,mailbox, aview,page,prompt_area) struct header_rec *current_header; FILE *F; int index; struct MailboxView *mailbox; struct AliasView *aview; struct menu_context *page; struct menu_context *prompt_area; { struct list_info *L = current_header->list_info; CONST struct url * url = NULL; struct addr_item * rt = NULL; struct addr_item * cc = NULL; int r; if (L) { int i; for (i = 0; i < L->list_post_len; i++) { struct url * url0 = L->list_post[i].url; if (url_mailing == get_url_type(url0)) { url = url0; break; } } } rt = handle_reply_to(current_header,F); cc = get_and_expand_everyone(current_header,rt); /* Add author (From address) to CC (rt may include Reply-To address) */ append_addresses_to_addr_list(&cc,current_header->from); r = reply_to_combined(page,prompt_area,mailbox,index,current_header,F, aview,url,rt,cc); if (r) { if (me_retcode) { DPRINT(Debug,5,(&Debug, "reply_to_all: Setting replied flag\n")); current_header->status |= REPLIED; current_header->status_chgd = TRUE; } } if (rt) free_addr_items(rt); if (cc) free_addr_items(cc); } #if ANSI_C static subpage_simple_redraw sb_update_title_list_post_selection; #endif static int sb_update_title_list_post_selection(ptr,list) struct menu_context *ptr; struct menu_param *list; { menu_ClearScreen(ptr); menu_print_format_center(ptr,0, CATGETS(elm_msg_cat, MeSet, MeGenericReplyTitleListPost, "List-Post selection for reply")); return 1; /* title completed */ } static void reply_to_list P_((struct header_rec *current_header, FILE *F,int index, struct MailboxView *mailbox, struct AliasView *aview, struct menu_context *page, struct menu_context *prompt_area)); static void reply_to_list(current_header,F,index,mailbox, aview,page,prompt_area) struct header_rec *current_header; FILE *F; int index; struct MailboxView *mailbox; struct AliasView *aview; struct menu_context *page; struct menu_context *prompt_area; { int r; struct list_info *L = current_header->list_info; CONST struct url * url = NULL; if (!L) return; if (L->list_post_no) { lib_error(CATGETS(elm_msg_cat, MeSet, MeGenericReplyNoPosting, "Mailing list do not accept postings!")); return; } if (L->list_post_len < 1) { lib_error(CATGETS(elm_msg_cat, MeSet, MeGenericReplyNoListPost, "No information for posting to list.")); return; } url = select_post_url(L->list_post, L->list_post_len, page,sb_update_title_list_post_selection); if (!url) return; r = reply_to_url(page,prompt_area,mailbox,index,current_header, F,url,aview,0); if (r) { if (me_retcode) { DPRINT(Debug,5,(&Debug, "reply_to_list: Setting replied flag\n")); current_header->status |= REPLIED; current_header->status_chgd = TRUE; } } } #if ANSI_C static subpage_simple_redraw sb_update_title_list_owner_selection; #endif static int sb_update_title_list_owner_selection(ptr,list) struct menu_context *ptr; struct menu_param *list; { menu_ClearScreen(ptr); menu_print_format_center(ptr,0, CATGETS(elm_msg_cat, MeSet, MeGenericReplyTitleListOwner, "List-Owner selection for reply")); return 1; /* title completed */ } static void reply_to_list_owner P_((struct header_rec *current_header, FILE *F,int index, struct MailboxView *mailbox, struct AliasView *aview, struct menu_context *page, struct menu_context *prompt_area)); static void reply_to_list_owner(current_header,F,index,mailbox, aview,page,prompt_area) struct header_rec *current_header; FILE *F; int index; struct MailboxView *mailbox; struct AliasView *aview; struct menu_context *page; struct menu_context *prompt_area; { int r; struct list_info *L = current_header->list_info; CONST struct url * url = NULL; if (!L) return; if (L->list_owner_len < 1) { lib_error(CATGETS(elm_msg_cat, MeSet, MeGenericReplyNoListOwner, "No information for posting to list owner.")); return; } url = select_post_url(L->list_owner, L->list_owner_len, page,sb_update_title_list_owner_selection); if (!url) return; r= reply_to_url(page,prompt_area,mailbox,index,current_header, F,url,aview,1); if (r) { if (me_retcode) { DPRINT(Debug,5,(&Debug, "reply_to_list_owner: Setting replied flag\n")); current_header->status |= REPLIED; current_header->status_chgd = TRUE; } } } static void reply_to_author P_((struct header_rec *current_header, FILE *F,int index, struct MailboxView *mailbox, struct AliasView *aview, struct menu_context *page, struct menu_context *prompt_area)); static void reply_to_author(current_header,F,index,mailbox, aview,page,prompt_area) struct header_rec *current_header; FILE *F; int index; struct MailboxView *mailbox; struct AliasView *aview; struct menu_context *page; struct menu_context *prompt_area; { int r; r = reply_to_combined(page,prompt_area,mailbox,index,current_header,F, aview,NULL,current_header->from,NULL); if (r) { if (me_retcode) { DPRINT(Debug,5,(&Debug, "reply_to_author: Setting replied flag\n")); current_header->status |= REPLIED; current_header->status_chgd = TRUE; } } } /* Returns 'i' if should go index */ int generic_reply(index,mailbox,aview,parent_page, old_prompt) int index; struct MailboxView *mailbox; struct AliasView *aview; struct menu_context *parent_page; struct menu_context *old_prompt; { FILE *F = NULL; struct header_rec *current_header = NULL; int form_letter; int ch; int retch = 0; int update = 0; int screen_initialized = 0; struct menu_anon_param A; struct menu_context *page = NULL; struct screen_parts LOC = { NULL, NULL, NULL, NULL }; struct menu_param PARAM[reply_mp_COUNT+1] = { { mp_integer, 0 }, { mp_anon_param,0 }, { mp_END,0 } }; struct menu_context *this_page = NULL; struct menu_context *this_prompt = NULL; if (!give_message_data(mailbox,index, ¤t_header,&F,NULL, NO_mime_parse)) return 0; if (! current_header || !F) return 0; form_letter = (current_header->status & FORM_LETTER); if (form_letter) return 0; mp_list_set_integer(PARAM,reply_mp_islist, current_header->list_info != NULL); A.current_header = current_header; mp_list_set_anon(PARAM,reply_mp_param,&A); /* Make possible type two character commands without printing of info screen .. this waits a second for command */ if (old_prompt) { int lin,col; this_page = parent_page; this_prompt = old_prompt; menu_Write_to_screen(old_prompt,FRM(": ")); menu_GetXYLocation(old_prompt,&lin,&col); menu_CleartoEOS(old_prompt); show_last_error(); menu_MoveCursor(old_prompt,lin,col); ch = menu_ReadCh(old_prompt, REDRAW_MARK|READCH_CURSOR|READCH_resize| READCH_sig_char|READCH_poll); if (ch == EOF || ch == TERMCH_interrupt_char) goto OUT; if (ch != TIMEOUT_MARK) goto peek_key; } for (;;) { if (! screen_initialized) { page = new_menu_context(); set_reply_screen(page,&LOC,PARAM); this_page = page; this_prompt = LOC.prompt_page; screen_initialized = 1; } if (menu_resized(page)) { set_reply_screen(page,&LOC,PARAM); update = 1; } if (update || menu_need_redraw(page)) { menu_ClearScreen(page); /* Call refresh routines of children */ menu_redraw_children(page); update = 0; show_last_error(); } check_reply_screen(&LOC,PARAM); { int lin,col; menu_ClearLine(LOC.prompt_page,0); menu_PutLineX (LOC.prompt_page,0, 0, CATGETS(elm_msg_cat, MeSet, MeGenericReplyPrompt, "Generic reply: ")); menu_GetXYLocation(LOC.prompt_page,&lin,&col); menu_CleartoEOS(LOC.prompt_page); show_last_error(); menu_MoveCursor(LOC.prompt_page,lin,col); ch = menu_ReadCh(LOC.prompt_page, REDRAW_MARK|READCH_CURSOR|READCH_resize| READCH_sig_char); menu_CleartoEOS(LOC.prompt_page); set_error(""); /* clear error buffer */ } peek_key: switch (ch) { case RESIZE_MARK: DPRINT(Debug,4, (&Debug, " ... resizing\n")); continue; case ctrl('L'): case REDRAW_MARK: DPRINT(Debug,4, (&Debug, " ... redrawing\n")); menu_ClearScreen(this_page); /* Clear possible redraw mark */ /* Call refresh routines of children */ menu_redraw_children(this_page); if (menu_need_redraw(this_prompt)) menu_ClearScreen(this_prompt); /* Clear redraw mark from prompt_area*/ update = 1; continue; case ctrl('J'): case ctrl('M'): goto OUT; case 'a': menu_Write_to_screen(this_prompt, CATGETS(elm_msg_cat, MeSet, MeGenericReplyToAll, "Reply to all")); FlushBuffer(); reply_to_all(current_header,F,index,mailbox,aview, this_page,this_prompt); goto OUT; case 'l': menu_Write_to_screen(this_prompt, CATGETS(elm_msg_cat, MeSet, MeGenericReplyToList, "Reply to list")); FlushBuffer(); if (current_header->list_info) reply_to_list(current_header,F,index,mailbox,aview, this_page,this_prompt); else { lib_error(CATGETS(elm_msg_cat, MeSet, MeGenericReplyNotAList, "Mailing list information is not a available.")); } goto OUT; case 'o': menu_Write_to_screen(this_prompt, CATGETS(elm_msg_cat, MeSet, MeGenericReplyToListOwner, "Reply to list owner")); FlushBuffer(); if (current_header->list_info) reply_to_list_owner(current_header,F,index,mailbox,aview, this_page,this_prompt); else { lib_error(CATGETS(elm_msg_cat, MeSet, MeGenericReplyNotAList, "Mailing list information is not a available.")); } goto OUT; case 't': case 'f': menu_Write_to_screen(this_prompt, CATGETS(elm_msg_cat, MeSet, MeGenericReplyToAuthor, "Reply to author")); FlushBuffer(); reply_to_author(current_header,F,index,mailbox,aview, this_page,this_prompt); goto OUT; case 'i': menu_Write_to_screen(this_prompt, CATGETS(elm_msg_cat, MeSet, MeGenericReplyIndex, "Go to index")); FlushBuffer(); retch = 'i'; /* FALLTHRU */ case 'q': case 'x': case TERMCH_interrupt_char: goto OUT; case EOF: retch = EOF; goto OUT; default: lib_error(CATGETS(elm_msg_cat, MeSet, MeGenericReplyUnknownCommand, "Unknown command: %c"), ch); continue; } } OUT: error_wait(); if (screen_initialized) { free_mailbox_screen(&LOC); erase_menu_context(&page); /* Force default return to parent page ... */ menu_set_default(parent_page); menu_trigger_redraw(parent_page); } else if (old_prompt) menu_trigger_redraw(old_prompt); return retch; } /* * Local Variables: * mode:c * c-basic-offset:4 * buffer-file-coding-system: iso-8859-1 * End: */