#if !defined(lint) && !defined(DOS) static char rcsid[] = "$Id: help.c 1032 2008-04-11 00:30:04Z hubert@u.washington.edu $"; #endif /* * ======================================================================== * Copyright 2013-2021 Eduardo Chappa * Copyright 2006-2008 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * ======================================================================== */ #include "headers.h" #include "help.h" #include "keymenu.h" #include "status.h" #include "mailview.h" #include "mailindx.h" #include "mailcmd.h" #include "reply.h" #include "signal.h" #include "radio.h" #include "send.h" #include "../pith/state.h" #include "../pith/conf.h" #include "../pith/filter.h" #include "../pith/msgno.h" #include "../pith/pipe.h" #include "../pith/util.h" #include "../pith/detoken.h" #include "../pith/list.h" #include "../pith/margin.h" typedef struct _help_scroll { unsigned keys_formatted:1; /* Has full keymenu been formatted? */ char **help_source; /* Source of displayed help text */ } HELP_SCROLL_S; static struct { unsigned crlf:1; char **line, *offset; } g_h_text; typedef struct _help_print_state { int page; char *title; int title_len; } HPRT_S; static HPRT_S *g_hprt; static char att_cur_msg[] = "\ Reporting a bug...\n\ \n\ If you think that the \"current\" message may be related to the bug you\n\ are reporting you may include it as an attachment. If you want to\n\ include a message but you aren't sure if it is the current message,\n\ cancel this bug report, go to the folder index, place the cursor on\n\ the message you wish to include, then return to the main menu and run\n\ the bug report command again. Answer \"Y\" when asked the question\n\ \"Attach current message to report?\"\n\ \n\ This bug report will also automatically include your pine\n\ configuration file, which is helpful when investigating the problem."; #define GRIPE_OPT_CONF 0x01 #define GRIPE_OPT_MSG 0x02 #define GRIPE_OPT_LOCAL 0x04 #define GRIPE_OPT_KEYS 0x08 /* * Internal prototypes */ int helper_internal(HelpType, char *, char *, int); int help_processor(int, MSGNO_S *, SCROLL_S *); void help_keymenu_tweek(SCROLL_S *, int); void print_all_help(void); void print_help_page_title(char *, size_t, HPRT_S *); int print_help_page_break(long, char *, LT_INS_S **, void *); int help_bogus_input(UCS); int gripe_newbody(struct pine *, BODY **, long, int); ADDRESS *gripe_token_addr(char *); char *gripe_id(char *); void att_cur_drawer(void); int journal_processor(int, MSGNO_S *, SCROLL_S *); int help_popup(SCROLL_S *, int); #ifdef _WINDOWS int help_subsection_popup(SCROLL_S *, int); #endif /*---------------------------------------------------------------------- Get the help text in the proper format and call scroller Args: text -- The help text to display (from pine.help --> helptext.c) title -- The title of the help text Result: format text and call scroller The pages array contains the line number of the start of the pages in the text. Page 1 is in the 0th element of the array. The list is ended with a page line number of -1. Line number 0 is also the first line in the text. -----*/ int helper_internal(HelpType text, char *frag, char *title, int flags) { char **shown_text, **external_text = NULL, *help_text = NULL, **rv; int cmd = MC_NONE, is_external; long offset = 0L; char *error = NULL, tmp_title[MAX_SCREEN_COLS + 1]; STORE_S *store; HANDLE_S *handles = NULL, *htmp; HELP_SCROLL_S hscroll; gf_io_t pc; dprint((1, "\n\n ---- HELPER ----\n")); /* assumption here is that HelpType is char ** */ shown_text = text; is_external = 0; if(shown_text && *shown_text && !struncmp(*shown_text, "x-alpine-http:", 14)){ int status; help_text = http_get(*shown_text + 14, &status); if(status != HTTP_OK){ shown_text = NO_HELP; if(help_text) fs_give((void **) &help_text); return MC_FINISH; } else{ is_external = 1; if(help_text == NULL) external_text = NO_HELP; else{ char *s, *t; int nlines; for(nlines = 0, s = help_text; s != NULL; nlines++){ s = strchr(s, '\n'); if(s != NULL) s++; } rv = external_text = fs_get((nlines + 1)*sizeof(char *)); for(s = help_text; s != NULL; s = t){ t = strchr(s, '\n'); if(t != NULL) *t++ = '\0'; *rv++ = cpystr(s); } *rv = NULL; } } } if(F_ON(F_BLANK_KEYMENU,ps_global)){ FOOTER_ROWS(ps_global) = 3; clearfooter(ps_global); ps_global->mangled_screen = 1; } if(flags & HLPD_NEWWIN){ fix_windsize(ps_global); init_sigwinch(); } /* * At this point, shown_text is a charstarstar with html * Turn it into a charstar with digested html */ do{ init_helper_getc(is_external ? external_text : shown_text); init_handles(&handles); memset(&hscroll, 0, sizeof(HELP_SCROLL_S)); hscroll.help_source = is_external ? external_text : shown_text; if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){ gf_set_so_writec(&pc, store); gf_filter_init(); if(!struncmp(hscroll.help_source[0], "", 6) || is_external) gf_link_filter(gf_html2plain, gf_html2plain_opt("x-alpine-help:", ps_global->ttyo->screen_cols, non_messageview_margin(), &handles, NULL, GFHP_LOCAL_HANDLES)); else gf_link_filter(gf_wrap, gf_wrap_filter_opt( ps_global->ttyo->screen_cols, ps_global->ttyo->screen_cols, NULL, 0, GFW_HANDLES | GFW_SOFTHYPHEN)); error = gf_pipe(helper_getc, pc); gf_clear_so_writec(store); if(!error){ SCROLL_S sargs; struct key_menu km; struct key keys[24]; for(htmp = handles; htmp; htmp = htmp->next) if(htmp->type == URL && htmp->h.url.path && (htmp->h.url.path[0] == 'x' || htmp->h.url.path[0] == '#')) htmp->force_display = 1; /* This is mostly here to get the curses variables * for line and column in sync with where the * cursor is on the screen. This gets warped when * the composer is called because it does it's own * stuff */ ClearScreen(); memset(&sargs, 0, sizeof(SCROLL_S)); sargs.text.text = so_text(store); sargs.text.src = CharStar; sargs.text.desc = _("help text"); if((sargs.text.handles = handles) != NULL) while(sargs.text.handles->type == URL && !sargs.text.handles->h.url.path && sargs.text.handles->next) sargs.text.handles = sargs.text.handles->next; if(!(sargs.bar.title = title)){ if(!struncmp(hscroll.help_source[0], "", 6) || is_external){ char *p; int i; /* if we're looking at html, look for a * in the <head>... */ for(i = 1; hscroll.help_source[0][i] && struncmp(hscroll.help_source[i], "</head>", 7); i++) if(!struncmp(hscroll.help_source[i], "<title>", 7)){ strncpy(tmp_20k_buf, &hscroll.help_source[i][7], SIZEOF_20KBUF); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; if((p = strchr(tmp_20k_buf, '<')) != NULL) *p = '\0'; snprintf(sargs.bar.title = tmp_title, sizeof(tmp_title), "%s -- %.*s", _("HELP"), ps_global->ttyo->screen_cols-10, strsquish(tmp_20k_buf + 500, SIZEOF_20KBUF, _(tmp_20k_buf), ps_global->ttyo->screen_cols / 3)); tmp_title[sizeof(tmp_title)-1] = '\0'; break; } } if(!sargs.bar.title) sargs.bar.title = _("HELP TEXT"); } sargs.bar.style = TextPercent; sargs.proc.tool = help_processor; sargs.proc.data.p = (void *) &hscroll; sargs.resize_exit = 1; sargs.help.text = h_special_help_nav; sargs.help.title = _("HELP FOR HELP TEXT"); sargs.keys.menu = &km; km = help_keymenu; km.keys = keys; memcpy(&keys[0], help_keymenu.keys, (help_keymenu.how_many * 12) * sizeof(struct key)); setbitmap(sargs.keys.bitmap); if(flags & HLPD_FROMHELP){ km.keys[HLP_EXIT_KEY].name = "P"; km.keys[HLP_EXIT_KEY].label = _("Prev Help"); km.keys[HLP_EXIT_KEY].bind.cmd = MC_FINISH; km.keys[HLP_EXIT_KEY].bind.ch[0] = 'p'; km.keys[HLP_SUBEXIT_KEY].name = "E"; km.keys[HLP_SUBEXIT_KEY].label = _("Exit Help"); km.keys[HLP_SUBEXIT_KEY].bind.cmd = MC_EXIT; km.keys[HLP_SUBEXIT_KEY].bind.ch[0] = 'e'; } else if(text == h_special_help_nav){ km.keys[HLP_EXIT_KEY].name = "P"; km.keys[HLP_EXIT_KEY].label = _("Prev Help"); km.keys[HLP_EXIT_KEY].bind.cmd = MC_FINISH; km.keys[HLP_EXIT_KEY].bind.ch[0] = 'p'; clrbitn(HLP_MAIN_KEY, sargs.keys.bitmap); clrbitn(HLP_SUBEXIT_KEY, sargs.keys.bitmap); } else{ km.keys[HLP_EXIT_KEY].name = "E"; km.keys[HLP_EXIT_KEY].label = _("Exit Help"); km.keys[HLP_EXIT_KEY].bind.cmd = MC_EXIT; km.keys[HLP_EXIT_KEY].bind.ch[0] = 'e'; km.keys[HLP_SUBEXIT_KEY].name = "?"; /* TRANSLATORS: this is the label of a command where the user is asking for Help about the Help command */ km.keys[HLP_SUBEXIT_KEY].label = _("Help Help"); km.keys[HLP_SUBEXIT_KEY].bind.cmd = MC_HELP; km.keys[HLP_SUBEXIT_KEY].bind.ch[0] = '?'; } if(flags & HLPD_SIMPLE){ clrbitn(HLP_MAIN_KEY, sargs.keys.bitmap); } else sargs.bogus_input = help_bogus_input; if(handles){ sargs.keys.each_cmd = help_keymenu_tweek; hscroll.keys_formatted = 0; } else{ clrbitn(HLP_VIEW_HANDLE, sargs.keys.bitmap); clrbitn(HLP_PREV_HANDLE, sargs.keys.bitmap); clrbitn(HLP_NEXT_HANDLE, sargs.keys.bitmap); } if(text != main_menu_tx && text != h_mainhelp_pinehelp) clrbitn(HLP_ALL_KEY, sargs.keys.bitmap); if(frag){ sargs.start.on = Fragment; sargs.start.loc.frag = frag; frag = NULL; /* ignore next time */ } else if(offset){ sargs.start.on = Offset; sargs.start.loc.offset = offset; } else sargs.start.on = FirstPage; #ifdef _WINDOWS sargs.mouse.popup = (flags & HLPD_FROMHELP) ? help_subsection_popup : help_popup; #endif cmd = scrolltool(&sargs); offset = sargs.start.loc.offset; if(F_ON(F_BLANK_KEYMENU,ps_global)) FOOTER_ROWS(ps_global) = 1; ClearScreen(); } so_give(&store); } free_handles(&handles); } while(cmd == MC_RESIZE); if(external_text != NULL){ for(rv = external_text; *rv != NULL; *rv++) fs_give((void **) &*rv); fs_give((void **) external_text); } if(help_text) fs_give((void **) &help_text); return(cmd); } /* * helper -- compatibility function around newer helper_internal */ int helper(HelpType text, char *title, int flags) { return(helper_internal(text, NULL, title, flags)); } void init_helper_getc(char **help_txt) { g_h_text.crlf = 0; g_h_text.line = help_txt; g_h_text.offset = *g_h_text.line; if(g_h_text.offset && g_h_text.offset[0]) g_h_text.offset = _(g_h_text.offset); } int helper_getc(char *c) { if(g_h_text.crlf){ *c = '\012'; g_h_text.crlf = 0; return(1); } else if(g_h_text.offset && *g_h_text.line){ if(!(*c = *g_h_text.offset++)){ g_h_text.offset = *++g_h_text.line; if(g_h_text.offset && g_h_text.offset[0]) g_h_text.offset = _(g_h_text.offset); *c = '\015'; g_h_text.crlf = 1; } return(1); } return(0); } int help_processor(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms) { int rv = 0; char message[64]; struct help_texts *t; switch(cmd){ /*----------- Print all the help ------------*/ case MC_PRINTALL : print_all_help(); break; case MC_PRINTMSG : snprintf(message, sizeof(message), "%s", STYLE_NAME(sparms)); message[sizeof(message)-1] = '\0'; if(open_printer(message) == 0){ print_help(((HELP_SCROLL_S *)sparms->proc.data.p)->help_source); close_printer(); } break; case MC_EXPORT: /* reuse old definition, so as not to patch pine.h */ {char help_name[40]; help_name[0] = '\0'; for(t = h_texts; t->help_text != NO_HELP; t++) if(t->help_text == ((HELP_SCROLL_S *)sparms->proc.data.p)->help_source){ strncpy(help_name, t->tag, sizeof(help_name)-1); help_name[sizeof(help_name)-1] = '\0'; break; } if(help_name[0]) q_status_message1(SM_ORDER, 0, 2, "Internal Name: x-alpine-help:%s", help_name); else q_status_message(SM_ORDER|SM_DING, 1, 2, "Can not find link for text help"); } break; case MC_FINISH : rv = 1; break; default : alpine_panic("Unhandled case"); } return(rv); } void help_keymenu_tweek(SCROLL_S *sparms, int handle_hidden) { if(handle_hidden){ sparms->keys.menu->keys[HLP_VIEW_HANDLE].name = ""; sparms->keys.menu->keys[HLP_VIEW_HANDLE].label = ""; } else{ if(!((HELP_SCROLL_S *)sparms->proc.data.p)->keys_formatted){ /* If label's always been blank, force reformatting */ mark_keymenu_dirty(); sparms->keys.menu->width = 0; ((HELP_SCROLL_S *)sparms->proc.data.p)->keys_formatted = 1; } sparms->keys.menu->keys[HLP_VIEW_HANDLE].name = "V"; sparms->keys.menu->keys[HLP_VIEW_HANDLE].label = "[" N_("View Link") "]"; } } /* * print_help - send the raw array of lines to printer */ void print_help(char **text) { char *error, buf[256]; HPRT_S help_data; init_helper_getc(text); memset(g_hprt = &help_data, 0, sizeof(HPRT_S)); help_data.page = 1; gf_filter_init(); if(!struncmp(text[0], "<html>", 6)){ int i; char *p; gf_link_filter(gf_html2plain, gf_html2plain_opt(NULL,80,non_messageview_margin(), NULL,NULL,GFHP_STRIPPED)); for(i = 1; i <= 5 && text[i]; i++) if(!struncmp(text[i], "<title>", 7) && (p = srchstr(text[i] + 7, "")) && p - text[i] > 7){ help_data.title = text[i] + 7; help_data.title_len = p - help_data.title; break; } } else gf_link_filter(gf_wrap, gf_wrap_filter_opt(80, 80, NULL, 0, GFW_NONE)); gf_link_filter(gf_line_test, gf_line_test_opt(print_help_page_break, NULL)); gf_link_filter(gf_nvtnl_local, NULL); print_help_page_title(buf, sizeof(buf), &help_data); print_text(buf); print_text(NEWLINE); /* terminate it */ print_text(NEWLINE); /* and write two blank links */ print_text(NEWLINE); if((error = gf_pipe(helper_getc, print_char)) != NULL) q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Printing Error: %s"), error); print_char(ctrl('L')); /* new page. */ } void print_all_help(void) { struct help_texts *t; char **h; int we_turned_on = 0; if(open_printer(_("all 150+ pages of help text")) == 0) { we_turned_on = intr_handling_on(); for(t = h_texts; (h = t->help_text) != NO_HELP; t++) { if(ps_global->intr_pending){ q_status_message(SM_ORDER, 3, 3, _("Print of all help cancelled")); break; } print_help(h); } if(we_turned_on) intr_handling_off(); close_printer(); } } /* * print_help_page_title -- */ void print_help_page_title(char *buf, size_t buflen, HPRT_S *hprt) { snprintf(buf, buflen, " Alpine Help%s%.*s%*s%d", hprt->title_len ? ": " : " Text", MIN(55, hprt->title_len), hprt->title_len ? hprt->title : "", 59 - (hprt->title_len ? MIN(55, hprt->title_len) : 5), "Page ", hprt->page); buf[buflen-1] = '\0'; } /* * print_help_page_break -- insert page breaks and such for printed * help text */ int print_help_page_break(long int linenum, char *line, LT_INS_S **ins, void *local) { char buf[256]; if(((linenum + (g_hprt->page * 3)) % 62) == 0){ g_hprt->page++; /* start on new page */ buf[0] = ctrl('L'); print_help_page_title(buf + 1, sizeof(buf)-1, g_hprt); strncat(buf, "\015\012\015\012\015\012", sizeof(buf)-strlen(buf)-1); buf[sizeof(buf)-1] = '\0'; ins = gf_line_test_new_ins(ins, line, buf, strlen(buf)); } return(0); } /* * help_bogus_input - used by scrolltool to complain about * invalid user input. */ int help_bogus_input(UCS ch) { bogus_command(ch, NULL); return(0); } int url_local_helper(char *url) { if((!struncmp(url, "x-alpine-help:", 14) && *(url += 14)) || (!struncmp(url, "x-pine-help:", 12) && *(url += 12))){ char *frag; HelpType newhelp; /* internal fragment reference? */ if((frag = strchr(url, '#')) != NULL){ size_t len; if((len = frag - url) != 0){ newhelp = help_name2section(url, len); } else{ url_local_fragment(url); return(1); } } else newhelp = help_name2section(url, strlen(url)); if(newhelp != NO_HELP){ int rv; rv = helper_internal(newhelp, frag, _("HELP SUB-SECTION"), HLPD_NEWWIN | HLPD_SIMPLE | HLPD_FROMHELP); ps_global->mangled_screen = 1; return((rv == MC_EXIT) ? 2 : 1); } } q_status_message1(SM_ORDER | SM_DING, 0, 3, _("Unrecognized Internal help: \"%s\""), url); return(0); } int url_local_config(char *url) { if(!struncmp(url, "x-alpine-config:", 16)){ char **config; int rv = MC_NONE; config = get_supported_options(); if(config){ /* TRANSLATORS: Help for configuration */ rv = helper_internal(config, NULL, _("HELP CONFIG"), HLPD_NEWWIN | HLPD_SIMPLE | HLPD_FROMHELP); free_list_array(&config); } ps_global->mangled_screen = 1; return((rv == MC_EXIT) ? 2 : 1); } q_status_message1(SM_ORDER | SM_DING, 0, 3, _("Unrecognized Internal help: \"%s\""), url); return(0); } /*---------------------------------------------------------------------- Review latest status messages -----*/ void review_messages(void) { SCROLL_S sargs; STORE_S *in_store = NULL, *out_store = NULL; gf_io_t gc, pc; int jo, lo, hi, donejo, donelo, donehi; RMCat rmcat; int cmd, timestamps=0, show_level=-1; char debugkeylabel[20]; /* TRANSLATORS: command label asking pine to include time stamps in output */ char timestampkeylabel[] = N_("Timestamps"); /* TRANSLATORS: do not include time stamps in output */ char *notimestampkeylabel = N_("NoTimestamps"); if((rmjofirst < 0 && rmlofirst < 0 && rmhifirst < 0) || rmjofirst >= RMJLEN || rmjolast >= RMJLEN || rmlofirst >= RMLLEN || rmlolast >= RMLLEN || rmhifirst >= RMHLEN || rmhilast >= RMHLEN || (rmjofirst >= 0 && rmjolast < 0) || (rmlofirst >= 0 && rmlolast < 0) || (rmhifirst >= 0 && rmhilast < 0)) return; do{ if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS)) || !(out_store = so_get(CharStar, NULL, EDIT_ACCESS))){ if(in_store) so_give(&in_store); q_status_message(SM_ORDER | SM_DING, 3, 4, _("Failed allocating memory")); return; } add_review_message(_("Turning off new messages while reviewing"), 0); rm_not_right_now = 1; donejo = donehi = donelo = 0; jo = rmjofirst; if(jo < 0) donejo = 1; lo = rmlofirst; if(lo < 0) donelo = 1; hi = rmhifirst; if(hi < 0) donehi = 1; while(!(donejo && donelo && donehi)){ REV_MSG_S *pjo, *plo, *phi, *p; if(!donejo) pjo = &rmjoarray[jo]; else pjo = NULL; if(!donelo) plo = &rmloarray[lo]; else plo = NULL; if(!donehi) phi = &rmhiarray[hi]; else phi = NULL; if(pjo && (!plo || pjo->seq <= plo->seq) && (!phi || pjo->seq <= phi->seq)) rmcat = Jo; else if(plo && (!phi || plo->seq <= phi->seq)) rmcat = Lo; else if(phi) rmcat = Hi; else rmcat = No; switch(rmcat){ case Jo: p = pjo; if(jo == rmjofirst && (((rmjolast + 1) % RMJLEN) == rmjofirst)) so_puts(in_store, _("**** Journal entries prior to this point have been trimmed. ****\n")); break; case Lo: p = plo; if(show_level >= 0 && lo == rmlofirst && (((rmlolast + 1) % RMLLEN) == rmlofirst)) so_puts(in_store, _("**** Debug 0-4 entries prior to this point have been trimmed. ****\n")); break; case Hi: p = phi; if(show_level >= 5 && hi == rmhifirst && (((rmhilast + 1) % RMHLEN) == rmhifirst)) so_puts(in_store, _("**** Debug 5-9 entries prior to this point have been trimmed. ****\n")); break; default: p = NULL; break; } if(p){ if(p->level <= show_level){ if(timestamps && p->timestamp && p->timestamp[0]){ so_puts(in_store, p->timestamp); so_puts(in_store, ": "); } if(p->message && p->message[0]){ if(p->continuation) so_puts(in_store, ">"); so_puts(in_store, p->message); so_puts(in_store, "\n"); } } } switch(rmcat){ case Jo: if(jo == rmjolast) donejo++; else jo = (jo + 1) % RMJLEN; break; case Lo: if(lo == rmlolast) donelo++; else lo = (lo + 1) % RMLLEN; break; case Hi: if(hi == rmhilast) donehi++; else hi = (hi + 1) % RMHLEN; break; default: donejo++; donelo++; donehi++; break; } } so_seek(in_store, 0L, 0); gf_filter_init(); gf_link_filter(gf_wrap, gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4, ps_global->ttyo->screen_cols, NULL, show_level < 0 ? 2 : 0, GFW_NONE)); gf_set_so_readc(&gc, in_store); gf_set_so_writec(&pc, out_store); gf_pipe(gc, pc); gf_clear_so_writec(out_store); gf_clear_so_readc(in_store); memset(&sargs, 0, sizeof(SCROLL_S)); sargs.text.text = so_text(out_store); sargs.text.src = CharStar; sargs.text.desc = _("journal"); sargs.keys.menu = &rev_msg_keymenu; sargs.proc.tool = journal_processor; sargs.start.on = LastPage; sargs.resize_exit = 1; sargs.proc.data.p = (void *)&show_level; setbitmap(sargs.keys.bitmap); #ifdef DEBUG #ifdef DEBUGJOURNAL sargs.jump_is_debug = 1; /* TRANSLATORS: these are some screen titles */ sargs.help.title = _("HELP FOR DEBUG JOURNAL"); sargs.help.text = h_debugjournal; sargs.bar.title = _("REVIEW DEBUGGING"); #else /* !DEBUGJOURNAL */ clrbitn(DEBUG_KEY, sargs.keys.bitmap); sargs.help.title = _("HELP FOR JOURNAL"); sargs.help.text = h_journal; sargs.bar.title = _("REVIEW RECENT MESSAGES"); #endif /* !DEBUGJOURNAL */ #else /* !DEBUG */ clrbitn(DEBUG_KEY, sargs.keys.bitmap); clrbitn(TIMESTAMP_KEY, sargs.keys.bitmap); sargs.help.title = _("HELP FOR JOURNAL"); sargs.help.text = h_journal; sargs.bar.title = _("REVIEW RECENT MESSAGES"); #endif /* !DEBUG */ if(timestamps) rev_msg_keys[TIMESTAMP_KEY].label = notimestampkeylabel; else rev_msg_keys[TIMESTAMP_KEY].label = timestampkeylabel; if(show_level >= 0) /* TRANSLATORS: shows what numeric level Debug output is displayed at */ snprintf(debugkeylabel, sizeof(debugkeylabel), _("Debug (%d)"), show_level); else /* TRANSLATORS: include debug output in journal */ strncpy(debugkeylabel, _("DebugView"), sizeof(debugkeylabel)); debugkeylabel[sizeof(debugkeylabel)-1] = '\0'; rev_msg_keys[DEBUG_KEY].label = debugkeylabel; KS_OSDATASET(&rev_msg_keys[DEBUG_KEY], KS_NONE); if((cmd = scrolltool(&sargs)) == MC_TOGGLE) timestamps = !timestamps; so_give(&in_store); so_give(&out_store); }while(cmd != MC_EXIT); rm_not_right_now = 0; add_review_message("Done reviewing", 0); } int journal_processor(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms) { switch(cmd){ case MC_TOGGLE: /* turn timestamps on or off */ break; default: alpine_panic("Unexpected command in journal_processor"); break; } return(1); } /* * standard type of storage object used for body parts... */ #ifdef DOS #define PART_SO_TYPE TmpFileStar #else #define PART_SO_TYPE CharStar #endif int gripe_gripe_to(url) char *url; { char *composer_title, *url_copy, *optstr, *p; int opts = 0; BODY *body = NULL; ENVELOPE *outgoing = NULL; REPLY_S fake_reply; PINEFIELD *pf = NULL; long msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap)); url_copy = cpystr(url + strlen("x-alpine-gripe:")); if((optstr = strchr(url_copy, '?')) != NULL) *optstr++ = '\0'; outgoing = mail_newenvelope(); outgoing->message_id = generate_message_id(NULL); if((outgoing->to = gripe_token_addr(url_copy)) != NULL){ composer_title = _("COMPOSE TO LOCAL SUPPORT"); dprint((1, "\n\n -- Send to local support(%s@%s) --\n", outgoing->to->mailbox ? outgoing->to->mailbox : "NULL", outgoing->to->host ? outgoing->to->host : "NULL")); } else{ /* must be global */ composer_title = _("REQUEST FOR ASSISTANCE"); rfc822_parse_adrlist(&outgoing->to, url_copy, ps_global->maildomain); } /* * Sniff thru options */ while(optstr){ if((p = strchr(optstr, '?')) != NULL) /* tie off list item */ *p++ = '\0'; if(!strucmp(optstr, "config")) opts |= GRIPE_OPT_CONF; else if(!strucmp(optstr, "curmsg")) opts |= GRIPE_OPT_MSG; else if(!strucmp(optstr, "local")) opts |= GRIPE_OPT_LOCAL; else if(!strucmp(optstr, "keys")) opts |= GRIPE_OPT_KEYS; optstr = p; } /* build body and hand off to composer... */ if(gripe_newbody(ps_global, &body, msgno, opts) == 0){ pf = (PINEFIELD *) fs_get(sizeof(PINEFIELD)); memset(pf, 0, sizeof(PINEFIELD)); pf->name = cpystr("X-Generated-Via"); pf->type = FreeText; pf->textbuf = gripe_id("Alpine Bug Report screen"); memset((void *)&fake_reply, 0, sizeof(fake_reply)); fake_reply.pseudo = 1; fake_reply.data.pico_flags = P_HEADEND; pine_send(outgoing, &body, composer_title, NULL, NULL, &fake_reply, NULL, NULL, pf, PS_STICKY_TO); } ps_global->mangled_screen = 1; mail_free_envelope(&outgoing); if(body) pine_free_body(&body); fs_give((void **) &url_copy); return(10); } int gripe_newbody(ps, body, msgno, flags) struct pine *ps; BODY **body; long msgno; int flags; { BODY *pb; PART **pp; STORE_S *store; gf_io_t pc; static char *err = "Problem creating space for message text."; int i; char tmp[MAILTMPLEN], *p; if((store = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){ *body = mail_newbody(); if((p = detoken(NULL, NULL, 2, 0, 1, NULL, NULL)) != NULL){ if(*p) so_puts(store, p); fs_give((void **) &p); } } else{ q_status_message(SM_ORDER | SM_DING, 3, 4, err); return(-1); } if(flags){ /*---- Might have multiple parts ----*/ (*body)->type = TYPEMULTIPART; /*---- The TEXT part/body ----*/ (*body)->nested.part = mail_newbody_part(); (*body)->nested.part->body.type = TYPETEXT; (*body)->nested.part->body.contents.text.data = (void *) store; /*---- create object, and write current config into it ----*/ pp = &((*body)->nested.part->next); if(flags & GRIPE_OPT_CONF){ *pp = mail_newbody_part(); pb = &((*pp)->body); pp = &((*pp)->next); pb->type = TYPETEXT; pb->id = generate_message_id(NULL); pb->description = cpystr("Alpine Configuration Data"); pb->parameter = mail_newbody_parameter(); pb->parameter->attribute = cpystr("name"); pb->parameter->value = cpystr("config.txt"); if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){ extern char datestamp[], hoststamp[]; pb->contents.text.data = (void *) store; gf_set_so_writec(&pc, store); gf_puts("Alpine built ", pc); gf_puts(datestamp, pc); gf_puts(" on host: ", pc); gf_puts(hoststamp, pc); gf_puts("\n", pc); #ifdef DEBUG dump_pine_struct(ps, pc); dump_config(ps, pc, 0); #endif /* DEBUG */ pb->size.bytes = strlen((char *) so_text(store)); gf_clear_so_writec(store); } else{ q_status_message(SM_ORDER | SM_DING, 3, 4, err); return(-1); } } if(flags & GRIPE_OPT_KEYS){ *pp = mail_newbody_part(); pb = &((*pp)->body); pp = &((*pp)->next); pb->type = TYPETEXT; pb->id = generate_message_id(NULL); pb->description = cpystr("Recent User Input"); pb->parameter = mail_newbody_parameter(); pb->parameter->attribute = cpystr("name"); pb->parameter->value = cpystr("uinput.txt"); if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){ pb->contents.text.data = (void *) store; so_puts(store, "User's most recent input:\n"); /* dump last n keystrokes */ so_puts(store, "========== Latest keystrokes ==========\n"); while((i = key_playback(0)) != -1){ snprintf(tmp, sizeof(tmp), "\t%s\t(0x%x)\n", pretty_command(i), i); tmp[sizeof(tmp)-1] = '\0'; so_puts(store, tmp); } pb->size.bytes = strlen((char *) so_text(store)); } else{ q_status_message(SM_ORDER | SM_DING, 3, 4, err); return(-1); } } /* check for local debugging info? */ if((flags & GRIPE_OPT_LOCAL) && ps_global->VAR_BUGS_EXTRAS && can_access(ps_global->VAR_BUGS_EXTRAS, EXECUTE_ACCESS) == 0){ char *error = NULL; *pp = mail_newbody_part(); pb = &((*pp)->body); pp = &((*pp)->next); pb->type = TYPETEXT; pb->id = generate_message_id(NULL); pb->description = cpystr("Local Configuration Data"); pb->parameter = mail_newbody_parameter(); pb->parameter->attribute = cpystr("name"); pb->parameter->value = cpystr("lconfig.txt"); if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){ PIPE_S *syspipe; gf_io_t gc; pb->contents.text.data = (void *) store; gf_set_so_writec(&pc, store); if((syspipe = open_system_pipe(ps_global->VAR_BUGS_EXTRAS, NULL, NULL, PIPE_READ | PIPE_STDERR | PIPE_USER, 0, pipe_callback, pipe_report_error)) != NULL){ gf_set_readc(&gc, (void *)syspipe, 0, PipeStar, 0); gf_filter_init(); error = gf_pipe(gc, pc); (void) close_system_pipe(&syspipe, NULL, pipe_callback); } else error = "executing config collector"; gf_clear_so_writec(store); } if(error){ q_status_message1(SM_ORDER | SM_DING, 3, 4, "Problem %s", error); return(-1); } else /* fixup attachment's size */ pb->size.bytes = strlen((char *) so_text(store)); } if((flags & GRIPE_OPT_MSG) && mn_get_total(ps->msgmap) > 0L){ int ch = 0; ps->redrawer = att_cur_drawer; att_cur_drawer(); if((ch = one_try_want_to("Attach current message to report", 'y','x',NO_HELP, WT_FLUSH_IN|WT_SEQ_SENSITIVE)) == 'y'){ *pp = mail_newbody_part(); pb = &((*pp)->body); pp = &((*pp)->next); pb->type = TYPEMESSAGE; pb->id = generate_message_id(NULL); snprintf(tmp, sizeof(tmp), "Problem Message (%ld of %ld)", mn_get_cur(ps->msgmap), mn_get_total(ps->msgmap)); tmp[sizeof(tmp)-1] = '\0'; pb->description = cpystr(tmp); /*---- Package each message in a storage object ----*/ if((store = so_get(PART_SO_TYPE, NULL, EDIT_ACCESS)) != NULL){ pb->contents.text.data = (void *) store; } else{ q_status_message(SM_ORDER | SM_DING, 3, 4, err); return(-1); } /* write the header */ if((p = mail_fetch_header(ps->mail_stream, msgno, NIL, NIL, NIL, FT_PEEK)) && *p) so_puts(store, p); else return(-1); pb->size.bytes = strlen(p); so_puts(store, "\015\012"); if((p = pine_mail_fetch_text(ps->mail_stream, msgno, NULL, NULL, NIL)) && *p) so_puts(store, p); else return(-1); pb->size.bytes += strlen(p); } else if(ch == 'x'){ q_status_message(SM_ORDER, 0, 3, "Bug report cancelled."); return(-1); } } } else{ /*---- Only one part! ----*/ (*body)->type = TYPETEXT; (*body)->contents.text.data = (void *) store; } return(0); } ADDRESS * gripe_token_addr(token) char *token; { char *p; ADDRESS *a = NULL; if(token && *token++ == '_'){ if(!strcmp(token, "LOCAL_ADDRESS_")){ p = (ps_global->VAR_LOCAL_ADDRESS && ps_global->VAR_LOCAL_ADDRESS[0]) ? ps_global->VAR_LOCAL_ADDRESS : "postmaster"; a = rfc822_parse_mailbox(&p, ps_global->maildomain); a->personal = cpystr((ps_global->VAR_LOCAL_FULLNAME && ps_global->VAR_LOCAL_FULLNAME[0]) ? ps_global->VAR_LOCAL_FULLNAME : "Place to report Alpine Bugs"); } else if(!strcmp(token, "BUGS_ADDRESS_")){ p = (ps_global->VAR_BUGS_ADDRESS && ps_global->VAR_BUGS_ADDRESS[0]) ? ps_global->VAR_BUGS_ADDRESS : "postmaster"; a = rfc822_parse_mailbox(&p, ps_global->maildomain); a->personal = cpystr((ps_global->VAR_BUGS_FULLNAME && ps_global->VAR_BUGS_FULLNAME[0]) ? ps_global->VAR_BUGS_FULLNAME : "Place to report Alpine Bugs"); } } return(a); } char * gripe_id(key) char *key; { int i,j,k,l; /* * Build our contribution to the subject; part constant string * and random 4 character alpha numeric string. */ i = (int)(random() % 36L); j = (int)(random() % 36L); k = (int)(random() % 36L); l = (int)(random() % 36L); tmp_20k_buf[0] = '\0'; snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s (ID %c%c%d%c%c)", key, (i < 10) ? '0' + i : 'A' + (i - 10), (j < 10) ? '0' + j : 'A' + (j - 10), (int)(random() % 10L), (k < 10) ? '0' + k : 'A' + (k - 10), (l < 10) ? '0' + l : 'A' + (l - 10)); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; return(cpystr(tmp_20k_buf)); } /* * Used by gripe_tool. */ void att_cur_drawer(void) { int i, dline, j; char buf[256+1]; /* blat helpful message to screen */ ClearBody(); j = 0; for(dline = 2; dline < ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global); dline++){ for(i = 0; i < 256 && att_cur_msg[j] && att_cur_msg[j] != '\n'; i++) buf[i] = att_cur_msg[j++]; buf[i] = '\0'; if(att_cur_msg[j]) j++; else if(!i) break; PutLine0(dline, 1, buf); } } #ifdef _WINDOWS /* * */ char * pcpine_help(HelpType section) { char **help_lines, *help_text = NULL; STORE_S *store; gf_io_t pc; /* assumption here is that HelpType is char ** */ help_lines = section; if (help_lines != NULL){ init_helper_getc(help_lines); if(store = so_get(CharStar, NULL, EDIT_ACCESS)){ gf_set_so_writec(&pc, store); gf_filter_init(); gf_link_filter(gf_local_nvtnl, NULL); gf_link_filter(gf_html2plain, gf_html2plain_opt(NULL, ps_global->ttyo->screen_cols, non_messageview_margin(), NULL, NULL, GFHP_STRIPPED)); if(!gf_pipe(helper_getc, pc)){ help_text = (char *) store->txt; store->txt = (void *) NULL; } gf_clear_so_writec(store); so_give(&store); } } return(help_text); } /* * */ int help_popup(SCROLL_S *sparms, int in_handle) { MPopup hp_menu[10]; int i = -1; if(in_handle){ hp_menu[++i].type = tQueue; hp_menu[i].label.style = lNormal; hp_menu[i].label.string = "View Help Section"; hp_menu[i].data.val = 'V'; } hp_menu[++i].type = tQueue; hp_menu[i].label.style = lNormal; hp_menu[i].label.string = "Exit Help"; hp_menu[i].data.val = 'E'; hp_menu[++i].type = tTail; mswin_popup(hp_menu); return(0); } /* * */ int help_subsection_popup(SCROLL_S *sparms, int in_handle) { MPopup hp_menu[10]; int i = -1; if(in_handle){ hp_menu[++i].type = tQueue; hp_menu[i].label.style = lNormal; hp_menu[i].label.string = "View Help Section"; hp_menu[i].data.val = 'V'; } hp_menu[++i].type = tQueue; hp_menu[i].label.style = lNormal; hp_menu[i].label.string = "Previous Help Section"; hp_menu[i].data.val = 'P'; hp_menu[++i].type = tQueue; hp_menu[i].label.style = lNormal; hp_menu[i].label.string = "Exit Help"; hp_menu[i].data.val = 'E'; hp_menu[++i].type = tTail; if(mswin_popup(hp_menu) == (in_handle ? 1 : 0)) /*(void) helper_internal()*/; return(0); } #endif /* _WINDOWS */