* Set up the IMAP ID at the moment of loging in to the server, rather than
[alpine.git] / alpine / alpine.c
blob7c63e77ac50a720406cc1c2eb5fb9daf0794d16b
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: alpine.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2020 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "headers.h"
21 #include "../pith/newmail.h"
22 #include "../pith/init.h"
23 #include "../pith/sort.h"
24 #include "../pith/options.h"
25 #include "../pith/list.h"
26 #include "../pith/conf.h"
27 #include "../pith/body.h"
29 #include "osdep/debuging.h"
30 #include "osdep/termout.gen.h"
31 #include "osdep/chnge_pw.h"
33 #include "alpine.h"
34 #include "mailindx.h"
35 #include "mailcmd.h"
36 #include "addrbook.h"
37 #include "reply.h"
38 #include "arg.h"
39 #include "keymenu.h"
40 #include "status.h"
41 #include "context.h"
42 #include "mailview.h"
43 #include "imap.h"
44 #include "xoauth2conf.h"
45 #include "radio.h"
46 #include "folder.h"
47 #include "send.h"
48 #include "help.h"
49 #include "titlebar.h"
50 #include "takeaddr.h"
51 #include "dispfilt.h"
52 #include "init.h"
53 #include "remote.h"
54 #include "pattern.h"
55 #include "setup.h"
56 #include "newuser.h"
57 #include "adrbkcmd.h"
58 #include "signal.h"
59 #include "kblock.h"
60 #include "ldapconf.h"
61 #include "roleconf.h"
62 #include "colorconf.h"
63 #include "print.h"
64 #include "after.h"
65 #include "smime.h"
66 #include "newmail.h"
67 #include "xoauth2conf.h"
68 #ifndef _WINDOWS
69 #include "../pico/osdep/raw.h" /* for STD*_FD */
70 #endif
73 #define PIPED_FD 5 /* Some innocuous desc */
76 /* look for my_timer_period in pico directory for an explanation */
77 int my_timer_period = ((IDLE_TIMEOUT + 1)*1000);
79 /* byte count used by our gets routine to keep track */
80 static unsigned long gets_bytes;
84 * Internal prototypes
86 void convert_args_to_utf8(struct pine *, ARGDATA_S *);
87 void preopen_stayopen_folders(void);
88 int read_stdin_char(char *);
89 void main_redrawer(void);
90 void show_main_screen(struct pine *, int, OtherMenu, struct key_menu *, int, Pos *);
91 void do_menu(int, Pos *, struct key_menu *);
92 int choose_setup_cmd(int, MSGNO_S *, SCROLL_S *);
93 int setup_menu(struct pine *);
94 void do_setup_task(int);
95 void queue_init_errors(struct pine *);
96 void process_init_cmds(struct pine *, char **);
97 void goodnight_gracey(struct pine *, int);
98 void pine_read_progress(GETS_DATA *, unsigned long);
99 int remote_pinerc_failure(void);
100 void dump_supported_options(void);
101 int prune_folders_ok(void);
102 void free_alpine_module_globals(void);
103 #ifdef WIN32
104 char *pine_user_callback(void);
105 #endif
106 #ifdef _WINDOWS
107 int fkey_mode_callback(int, long);
108 void imap_telemetry_on(void);
109 void imap_telemetry_off(void);
110 char *pcpine_help_main(char *);
111 int pcpine_main_cursor(int, long);
112 #define main app_main
113 #endif
116 typedef struct setup_return_val {
117 int cmd;
118 int exc;
119 }SRV_S;
123 * strlen of longest label from keymenu, of labels corresponding to
124 * commands in the middle of the screen. 9 is length of ListFldrs
126 #define LONGEST_LABEL 9 /* length of longest label from keymenu */
128 #define EDIT_EXCEPTION (0x100)
131 static int in_panic = 0;
134 /*----------------------------------------------------------------------
135 main routine -- entry point
137 Args: argv, argc -- The command line arguments
140 Initialize pine, parse arguments and so on
142 If there is a user address on the command line go into send mode and exit,
143 otherwise loop executing the various screens in Alpine.
145 NOTE: The Windows port def's this to "app_main"
146 ----*/
149 main(int argc, char **argv)
151 ARGDATA_S args;
152 int rv;
153 long rvl;
154 struct pine *pine_state;
155 gf_io_t stdin_getc = NULL;
156 char *args_for_debug = NULL, *init_pinerc_debugging = NULL;
158 /*----------------------------------------------------------------------
159 Set up buffering and some data structures
160 ----------------------------------------------------------------------*/
162 pine_state = new_pine_struct();
163 ps_global = pine_state;
166 * fill in optional pith-offered behavior hooks
168 pith_opt_read_msg_prompt = read_msg_prompt;
169 pith_opt_paint_index_hline = paint_index_hline;
170 pith_opt_rfc2369_editorial = rfc2369_editorial;
171 pith_opt_condense_thread_cue = condensed_thread_cue;
172 pith_opt_truncate_sfstr = truncate_subj_and_from_strings;
173 pith_opt_save_and_restore = save_and_restore;
174 pith_opt_newmail_announce = newmail_status_message;
175 pith_opt_newmail_check_cue = newmail_check_cue;
176 pith_opt_checkpoint_cue = newmail_check_point_cue;
177 pith_opt_icon_text = icon_text;
178 pith_opt_rd_metadata_name = rd_metadata_name;
179 pith_opt_remote_pinerc_failure = remote_pinerc_failure;
180 pith_opt_reopen_folder = ask_mailbox_reopen;
181 pith_opt_expunge_prompt = expunge_prompt;
182 pith_opt_begin_closing = expunge_and_close_begins;
183 pith_opt_replyto_prompt = reply_using_replyto_query;
184 pith_opt_reply_to_all_prompt = reply_to_all_query;
185 pith_opt_save_create_prompt = create_for_save_prompt;
186 pith_opt_daemon_confirm = confirm_daemon_send;
187 pith_opt_save_size_changed_prompt = save_size_changed_prompt;
188 pith_opt_save_index_state = setup_index_state;
189 pith_opt_filter_pattern_cmd = pattern_filter_command;
190 pith_opt_get_signature_file = get_signature_file;
191 pith_opt_pretty_var_name = pretty_var_name;
192 pith_opt_pretty_feature_name = pretty_feature_name;
193 pith_opt_closing_stream = titlebar_stream_closing;
194 pith_opt_current_expunged = mm_expunged_current;
195 #ifdef SMIME
196 pith_opt_smime_get_passphrase = smime_get_passphrase;
197 pith_smime_import_certificate = smime_import_certificate;
198 pith_smime_enter_password = alpine_get_password;
199 pith_smime_confirm_save = alpine_smime_confirm_save;
200 #endif
201 #ifdef ENABLE_LDAP
202 pith_opt_save_ldap_entry = save_ldap_entry;
203 #endif
205 status_message_lock_init();
206 inverse_itokens();
208 #if HAVE_SRANDOM
210 * Seed the random number generator with the date & pid. Random
211 * numbers are used for new mail notification and bug report id's
213 srandom(getpid() + time(0));
214 #endif
216 /* need home directory early */
217 get_user_info(&ps_global->ui);
219 if(!(pine_state->home_dir = our_getenv("HOME")))
220 pine_state->home_dir = cpystr(ps_global->ui.homedir);
222 #ifdef _WINDOWS
224 char *p;
226 /* normalize path delimiters */
227 for(p = pine_state->home_dir; p = strchr(p, '/'); p++)
228 *p='\\';
230 #endif /* _WINDOWS */
232 #ifdef DEBUG
233 { size_t len = 0;
234 int i;
235 char *p;
236 char *no_args = " <no args>";
238 for(i = 0; i < argc; i++)
239 len += (strlen(argv[i] ? argv[i] : "")+3);
241 if(argc == 1)
242 len += strlen(no_args);
244 p = args_for_debug = (char *)fs_get((len+2) * sizeof(char));
245 *p++ = '\n';
246 *p = '\0';
248 for(i = 0; i < argc; i++){
249 snprintf(p, len+2-(p-args_for_debug), "%s\"%s\"", i ? " " : "", argv[i] ? argv[i] : "");
250 args_for_debug[len+2-1] = '\0';
251 p += strlen(p);
254 if(argc == 1){
255 strncat(args_for_debug, no_args, len+2-strlen(args_for_debug)-1);
256 args_for_debug[len+2-1] = '\0';
259 #endif
261 /*----------------------------------------------------------------------
262 Parse arguments and initialize debugging
263 ----------------------------------------------------------------------*/
264 pine_args(pine_state, argc, argv, &args);
266 #ifndef _WINDOWS
267 if(!isatty(0)){
269 * monkey with descriptors so our normal tty i/o routines don't
270 * choke...
272 dup2(STDIN_FD, PIPED_FD); /* redirected stdin to new desc */
273 dup2(STDERR_FD, STDIN_FD); /* rebind stdin to the tty */
274 stdin_getc = read_stdin_char;
275 if(stdin_getc){
276 if(args.action == aaURL){
277 display_args_err(
278 "Cannot read stdin when using -url\nFor mailto URLs, use \'body=\' instead",
279 NULL, 1);
280 args_help();
281 exit(-1);
282 } else if (args.action == aaFolder){
283 display_args_err("Cannot take input from pipe when opening a folder", NULL, 1);
284 args_help();
285 exit(-1);
290 #else /* _WINDOWS */
292 * We now have enough information to do some of the basic registry settings.
294 if(ps_global->update_registry != UREG_NEVER_SET){
295 mswin_reg(MSWR_OP_SET
296 | ((ps_global->update_registry == UREG_ALWAYS_SET)
297 ? MSWR_OP_FORCE : 0),
298 MSWR_PINE_DIR, ps_global->pine_dir, (size_t)NULL);
299 mswin_reg(MSWR_OP_SET
300 | ((ps_global->update_registry == UREG_ALWAYS_SET)
301 ? MSWR_OP_FORCE : 0),
302 MSWR_PINE_EXE, ps_global->pine_name, (size_t)NULL);
305 #endif /* _WINDOWS */
307 if(ps_global->convert_sigs &&
308 (!ps_global->pinerc || !ps_global->pinerc[0])){
309 fprintf(stderr, "Use -p <pinerc> with -convert_sigs\n");
310 exit(-1);
313 /* Windows has its own functions to determine width of a character
314 * in the screen, so this is not necessary to do in Window, and
315 * using pith_ucs4width does not produce the correct result
317 #if !defined(_WINDOWS) && defined(LC_CTYPE)
318 { char *s;
319 if((s = setlocale(LC_CTYPE, "")) != NULL
320 && strlen(s) >= 5
321 && !strucmp(s+strlen(s)-5, "UTF-8"))
322 mail_parameters(NULL, SET_UCS4WIDTH, (void *) pith_ucs4width);
324 #endif /* !_WINDOWS && LC_CTYPE */
325 mail_parameters(NULL, SET_QUOTA, (void *) pine_parse_quota);
326 /* set some default timeouts in case pinerc is remote */
327 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)30);
328 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)15);
329 mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
330 /* could be TO_BAIL_THRESHOLD, 15 seems more appropriate for now */
331 pine_state->tcp_query_timeout = 15;
333 mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
334 mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
335 mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
336 mail_parameters(NULL, SET_FREEBODYSPAREP, (void *) free_body_sparep);
337 mail_parameters(NULL, SET_OA2CLIENTGETACCESSCODE, (void *) oauth2_get_access_code);
338 mail_parameters(NULL, SET_OA2CLIENTINFO, (void *) oauth2_get_client_info);
339 mail_parameters(NULL, SET_OA2DEVICEINFO, (void *) oauth2_set_device_info);
341 init_pinerc(pine_state, &init_pinerc_debugging);
343 #ifdef DEBUG
344 /* Since this is specific debugging we don't mind if the
345 ifdef is the type of system.
347 #if defined(HAVE_SMALLOC) || defined(NXT)
348 if(ps_global->debug_malloc)
349 malloc_debug(ps_global->debug_malloc);
350 #endif
351 #ifdef CSRIMALLOC
352 if(ps_global->debug_malloc)
353 mal_debug(ps_global->debug_malloc);
354 #endif
356 if(!ps_global->convert_sigs
357 #ifdef _WINDOWS
358 && !ps_global->install_flag
359 #endif /* _WINDOWS */
361 init_debug();
363 if(args_for_debug){
364 dprint((0, " %s (PID=%ld)\n\n", args_for_debug,
365 (long) getpid()));
366 fs_give((void **)&args_for_debug);
370 char *env_to_free;
371 if((env_to_free = our_getenv("HOME")) != NULL){
372 dprint((2, "Setting home dir from $HOME: \"%s\"\n",
373 env_to_free));
374 fs_give((void **)&env_to_free);
376 else{
377 dprint((2, "Setting home dir: \"%s\"\n",
378 pine_state->home_dir ? pine_state->home_dir : "<?>"));
382 /* Watch out. Sensitive information in debug file. */
383 if(ps_global->debug_imap > 4)
384 mail_parameters(NULL, SET_DEBUGSENSITIVE, (void *) TRUE);
386 #ifndef DEBUGJOURNAL
387 if(ps_global->debug_tcp)
388 #endif
389 mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
391 #ifdef _WINDOWS
392 mswin_setdebug(debug, debugfile);
393 mswin_setdebugoncallback (imap_telemetry_on);
394 mswin_setdebugoffcallback (imap_telemetry_off);
395 mswin_enableimaptelemetry(ps_global->debug_imap != 0);
396 #endif
397 #endif /* DEBUG */
399 #ifdef _WINDOWS
400 mswin_setsortcallback(index_sort_callback);
401 mswin_setflagcallback(flag_callback);
402 mswin_sethdrmodecallback(header_mode_callback);
403 mswin_setselectedcallback(any_selected_callback);
404 mswin_setzoomodecallback(zoom_mode_callback);
405 mswin_setfkeymodecallback(fkey_mode_callback);
406 #endif
408 /*------- Set up c-client drivers -------*/
409 #include "../c-client/linkage.c"
411 /*------- ... then tune the drivers just installed -------*/
412 #ifdef _WINDOWS
413 if(_tgetenv(TEXT("HOME")))
414 mail_parameters(NULL, SET_HOMEDIR, (void *) pine_state->home_dir);
416 mail_parameters(NULL, SET_USERPROMPT, (void *) pine_user_callback);
419 * Sniff the environment for timezone offset. We need to do this
420 * here since Windows needs help figuring out UTC, and will adjust
421 * what time() returns based on TZ. THIS WILL SCREW US because
422 * we use time() differences to manage status messages. So, if
423 * rfc822_date, which calls localtime() and thus needs tzset(),
424 * is called while a status message is displayed, it's possible
425 * for time() to return a time *before* what we remember as the
426 * time we put the status message on the display. Sheesh.
428 tzset();
429 #else /* !_WINDOWS */
431 * We used to let c-client do this for us automatically, but it declines
432 * to do so for root. This forces c-client to establish an environment,
433 * even if the uid is 0.
435 env_init(ps_global->ui.login, ps_global->ui.homedir);
438 * Install callback to let us know the progress of network reads...
440 (void) mail_parameters(NULL, SET_READPROGRESS, (void *)pine_read_progress);
441 #endif /* !_WINDOWS */
444 * Install callback to handle certificate validation failures,
445 * allowing the user to continue if they wish.
447 mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) pine_sslcertquery);
448 mail_parameters(NULL, SET_SSLFAILURE, (void *) pine_sslfailure);
450 if(init_pinerc_debugging){
451 dprint((2, "%s", init_pinerc_debugging));
452 fs_give((void **)&init_pinerc_debugging);
456 * Initial allocation of array of stream pool pointers.
457 * We do this before init_vars so that we can re-use streams used for
458 * remote config files. These sizes may get changed later.
460 ps_global->s_pool.max_remstream = 2;
461 dprint((9,
462 "Setting initial max_remstream to %d for remote config re-use\n",
463 ps_global->s_pool.max_remstream));
465 init_vars(pine_state, process_init_cmds);
467 #if !defined(_WINDOWS) || defined(WINDOWS_UNIXSSL_CERTS)
468 set_system_certs_path(pine_state);
469 set_system_certs_container(pine_state);
470 set_user_certs_path(pine_state);
471 set_user_certs_container(pine_state);
472 #endif
474 #ifdef SMIME
475 if(F_ON(F_DONT_DO_SMIME, ps_global))
476 smime_deinit();
477 #endif /* SMIME */
479 #ifdef ENABLE_NLS
481 * LC_CTYPE is already set from the set_collation call above.
483 * We can't use gettext calls before we do this stuff so it doesn't
484 * help to translate strings that come before this in the program.
485 * Maybe we could rearrange things to accommodate that.
487 setlocale(LC_MESSAGES, "");
488 bindtextdomain(PACKAGE, LOCALEDIR);
489 bind_textdomain_codeset(PACKAGE, "UTF-8");
490 textdomain(PACKAGE);
491 #endif /* ENABLE_NLS */
493 convert_args_to_utf8(pine_state, &args);
495 if(args.action == aaFolder){
496 pine_state->beginning_of_month = first_run_of_month();
497 pine_state->beginning_of_year = first_run_of_year();
500 /* Set up optional for user-defined display filtering */
501 pine_state->tools.display_filter = dfilter;
502 pine_state->tools.display_filter_trigger = dfilter_trigger;
504 #ifdef _WINDOWS
505 if(ps_global->install_flag){
506 init_install_get_vars();
508 if(ps_global->prc)
509 free_pinerc_s(&ps_global->prc);
511 exit(0);
513 #endif
515 if(ps_global->convert_sigs){
516 if(convert_sigs_to_literal(ps_global, 0) == -1){
517 /* TRANSLATORS: sigs refers to signatures, which the user was trying to convert */
518 fprintf(stderr, _("trouble converting sigs\n"));
519 exit(-1);
522 if(ps_global->prc){
523 if(ps_global->prc->outstanding_pinerc_changes)
524 write_pinerc(ps_global, Main, WRP_NONE);
526 free_pinerc_s(&pine_state->prc);
529 exit(0);
533 * Set up a c-client read timeout and timeout handler. In general,
534 * it shouldn't happen, but a server crash or dead link can cause
535 * pine to appear wedged if we don't set this up...
537 rv = 30;
538 if(pine_state->VAR_TCPOPENTIMEO)
539 (void)SVAR_TCP_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
540 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)rv);
542 rv = 15;
543 if(pine_state->VAR_TCPREADWARNTIMEO)
544 (void)SVAR_TCP_READWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
545 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)rv);
547 rv = 0;
548 if(pine_state->VAR_TCPWRITEWARNTIMEO){
549 if(!SVAR_TCP_WRITEWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
550 if(rv == 0 || rv > 4) /* making sure */
551 mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long)rv);
554 mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
556 rv = 15;
557 if(pine_state->VAR_RSHOPENTIMEO){
558 if(!SVAR_RSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
559 if(rv == 0 || rv > 4) /* making sure */
560 mail_parameters(NULL, SET_RSHTIMEOUT, (void *)(long)rv);
563 rv = 15;
564 if(pine_state->VAR_SSHOPENTIMEO){
565 if(!SVAR_SSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
566 if(rv == 0 || rv > 4) /* making sure */
567 mail_parameters(NULL, SET_SSHTIMEOUT, (void *)(long)rv);
570 rvl = 60L;
571 if(pine_state->VAR_MAILDROPCHECK){
572 if(!SVAR_MAILDCHK(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF)){
573 if(rvl == 0L)
574 rvl = (60L * 60L * 24L * 100L); /* 100 days */
576 if(rvl >= 60L) /* making sure */
577 mail_parameters(NULL, SET_SNARFINTERVAL, (void *) rvl);
582 * Lookups of long login names which don't exist are very slow in aix.
583 * This would normally get set in system-wide config if not needed.
585 if(F_ON(F_DISABLE_SHARED_NAMESPACES, ps_global))
586 mail_parameters(NULL, SET_DISABLEAUTOSHAREDNS, (void *) TRUE);
588 if(F_ON(F_HIDE_NNTP_PATH, ps_global))
589 mail_parameters(NULL, SET_NNTPHIDEPATH, (void *) TRUE);
591 if(F_ON(F_MAILDROPS_PRESERVE_STATE, ps_global))
592 mail_parameters(NULL, SET_SNARFPRESERVE, (void *) TRUE);
594 rvl = 0L;
595 if(pine_state->VAR_NNTPRANGE){
596 if(!SVAR_NNTPRANGE(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF))
597 if(rvl > 0L)
598 mail_parameters(NULL, SET_NNTPRANGE, (void *) rvl);
602 * Tell c-client not to be so aggressive about uid mappings
604 mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20);
607 * Setup referral handling
609 mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral);
610 mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy);
613 * Setup multiple newsrc transition
615 mail_parameters(NULL, SET_NEWSRCQUERY, (void *) pine_newsrcquery);
618 * Disable some drivers if requested.
620 if(ps_global->VAR_DISABLE_DRIVERS &&
621 ps_global->VAR_DISABLE_DRIVERS[0] &&
622 ps_global->VAR_DISABLE_DRIVERS[0][0]){
623 char **t;
625 for(t = ps_global->VAR_DISABLE_DRIVERS; t[0] && t[0][0]; t++)
626 if(mail_parameters(NULL, DISABLE_DRIVER, (void *)(*t))){
627 dprint((2, "Disabled mail driver \"%s\"\n", *t));
629 else{
630 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
631 _("Failed to disable mail driver \"%s\": name not found"),
632 *t);
633 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
634 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
639 * Disable some authenticators if requested.
641 if(ps_global->VAR_DISABLE_AUTHS &&
642 ps_global->VAR_DISABLE_AUTHS[0] &&
643 ps_global->VAR_DISABLE_AUTHS[0][0]){
644 char **t;
646 for(t = ps_global->VAR_DISABLE_AUTHS; t[0] && t[0][0]; t++)
647 if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *)(*t))){
648 dprint((2,"Disabled SASL authenticator \"%s\"\n", *t));
650 else{
651 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
652 _("Failed to disable SASL authenticator \"%s\": name not found"),
653 *t);
654 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
655 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
659 if(ps_global->VAR_ENCRYPTION_RANGE
660 && ps_global->VAR_ENCRYPTION_RANGE[0]){
661 char *min_s, *max_s, *s;
662 int min_v, max_v;
664 if((s = strchr(ps_global->VAR_ENCRYPTION_RANGE, ',')) == NULL){
665 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
666 _("Bad encryption range: \"%s\": resetting to default"),
667 ps_global->VAR_ENCRYPTION_RANGE);
668 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
669 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
670 fs_give((void **) &ps_global->VAR_ENCRYPTION_RANGE);
671 ps_global->VAR_ENCRYPTION_RANGE = cpystr(DF_ENCRYPTION_RANGE);
672 s = strchr(ps_global->VAR_ENCRYPTION_RANGE, ','); /* try again */
675 if(s == NULL){
676 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
677 _("Bad default encryption range: \"%s\""),
678 ps_global->VAR_ENCRYPTION_RANGE);
679 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
680 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
682 else {
683 *s = ' ';
684 get_pair(ps_global->VAR_ENCRYPTION_RANGE, &min_s, &max_s, 1, 0);
685 *s = ',';
687 min_v = pith_ssl_encryption_version(min_s);
688 max_v = pith_ssl_encryption_version(max_s);
690 if(min_v < 0 || max_v < 0){
691 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
692 _("Bad encryption range: \"%s\": resetting to default"),
693 ps_global->VAR_ENCRYPTION_RANGE);
694 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
695 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
696 min_v = max_v = 0;
699 if(min_v > max_v){
700 int bubble;
701 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
702 _("Minimum encryption protocol (%s) bigger than maximum value (%s). Reversing..."),
703 min_s, max_s);
704 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
705 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
706 bubble = min_v;
707 min_v = max_v;
708 max_v = bubble;
711 if(max_v > 0 && max_v < (long) pith_ssl_encryption_version("tls1")){
712 strncpy(tmp_20k_buf, _("Security alert: SSL maximum encryption version was set to SSLv3."), SIZEOF_20KBUF);
713 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
714 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
717 mail_parameters(NULL, SET_ENCRYPTION_RANGE_MIN, (void *) &min_v);
718 mail_parameters(NULL, SET_ENCRYPTION_RANGE_MAX, (void *) &max_v);
724 * setup alternative authentication driver preference for IMAP opens
726 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
727 mail_parameters(NULL, SET_IMAPTRYALT, (void *) TRUE);
730 * Install handler to let us know about potential delays
732 (void) mail_parameters(NULL, SET_BLOCKNOTIFY, (void *) pine_block_notify);
734 if(ps_global->dump_supported_options){
735 dump_supported_options();
736 exit(0);
740 * Install extra headers to fetch along with all the other stuff
741 * mail_fetch_structure and mail_fetch_overview requests.
743 calc_extra_hdrs();
744 if(get_extra_hdrs())
745 (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
746 (void *) get_extra_hdrs());
748 if(init_username(pine_state) < 0){
749 fprintf(stderr, _("Who are you? (Unable to look up login name)\n"));
750 exit(-1);
753 if(init_userdir(pine_state) < 0)
754 exit(-1);
756 if(init_hostname(pine_state) < 0)
757 exit(-1);
760 * Verify mail dir if we're not in send only mode...
762 if(args.action == aaFolder && init_mail_dir(pine_state) < 0)
763 exit(-1);
765 init_signals();
767 /*--- input side ---*/
768 if(init_tty_driver(pine_state)){
769 #ifndef _WINDOWS /* always succeeds under _WINDOWS */
770 fprintf(stderr, _("Can't access terminal or input is not a terminal. Redirection of\nstandard input is not allowed. For example \"pine < file\" doesn't work.\n%c"), BELL);
771 exit(-1);
772 #endif /* !_WINDOWS */
776 /*--- output side ---*/
777 rv = config_screen(&(pine_state->ttyo));
778 #ifndef _WINDOWS /* always succeeds under _WINDOWS */
779 if(rv){
780 switch(rv){
781 case -1:
782 printf(_("Terminal type (environment variable TERM) not set.\n"));
783 break;
784 case -2:
785 printf(_("Terminal type \"%s\" is unknown.\n"), getenv("TERM"));
786 break;
787 case -3:
788 printf(_("Can't open terminal capabilities database.\n"));
789 break;
790 case -4:
791 printf(_("Your terminal, of type \"%s\", is lacking functions needed to run alpine.\n"), getenv("TERM"));
792 break;
795 printf("\r");
796 end_tty_driver(pine_state);
797 exit(-1);
799 #endif /* !_WINDOWS */
801 if(F_ON(F_BLANK_KEYMENU,ps_global))
802 FOOTER_ROWS(ps_global) = 1;
804 init_screen();
805 init_keyboard(pine_state->orig_use_fkeys);
806 strncpy(pine_state->inbox_name, INBOX_NAME,
807 sizeof(pine_state->inbox_name)-1);
808 init_folders(pine_state); /* digest folder spec's */
810 pine_state->in_init_seq = 0; /* so output (& ClearScreen) show up */
811 pine_state->dont_use_init_cmds = 1; /* don't use up initial_commands yet */
812 ClearScreen();
814 /* initialize titlebar in case we use it */
815 set_titlebar("", NULL, NULL, NULL, NULL, 0, FolderName, 0, 0, NULL);
818 * Prep storage object driver for PicoText
820 so_register_external_driver(pine_pico_get, pine_pico_give, pine_pico_writec, pine_pico_readc,
821 pine_pico_puts, pine_pico_seek, NULL, NULL);
823 #ifdef DEBUG
824 if(ps_global->debug_imap > 4 || debug > 9){
825 q_status_message(SM_ORDER | SM_DING, 5, 9,
826 _("Warning: sensitive authentication data included in debug file"));
827 flush_status_messages(0);
829 #endif
831 if(args.action == aaPrcCopy || args.action == aaAbookCopy){
832 int exit_val = -1;
833 char *err_msg = NULL;
836 * Don't translate these into UTF-8 because we'll be using them
837 * before we translate next time. User should use ascii.
839 if(args.data.copy.local && args.data.copy.remote){
840 switch(args.action){
841 case aaPrcCopy:
842 exit_val = copy_pinerc(args.data.copy.local,
843 args.data.copy.remote, &err_msg);
844 break;
846 case aaAbookCopy:
847 exit_val = copy_abook(args.data.copy.local,
848 args.data.copy.remote, &err_msg);
849 break;
851 default:
852 break;
855 if(err_msg){
856 q_status_message(SM_ORDER | SM_DING, 3, 4, err_msg);
857 fs_give((void **)&err_msg);
859 goodnight_gracey(pine_state, exit_val);
862 if(args.action == aaFolder
863 && (pine_state->first_time_user || pine_state->show_new_version)){
864 pine_state->mangled_header = 1;
865 show_main_screen(pine_state, 0, FirstMenu, &main_keymenu, 0,
866 (Pos *) NULL);
867 new_user_or_version(pine_state);
868 ClearScreen();
871 /* put back in case we need to suppress output */
872 pine_state->in_init_seq = pine_state->save_in_init_seq;
874 /* queue any init errors so they get displayed in a screen below */
875 queue_init_errors(ps_global);
877 /* "Page" the given file? */
878 if(args.action == aaMore){
879 int dice = 1, redir = 0;
881 if(pine_state->in_init_seq){
882 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
883 clear_cursor_pos();
884 if(pine_state->free_initial_cmds)
885 fs_give((void **)&(pine_state->free_initial_cmds));
887 pine_state->initial_cmds = NULL;
890 /*======= Requested that we simply page the given file =======*/
891 if(args.data.file){ /* Open the requested file... */
892 SourceType src;
893 STORE_S *store = NULL;
894 char *decode_error = NULL;
895 char filename[MAILTMPLEN];
897 if(args.data.file[0] == '\0'){
898 HelpType help = NO_HELP;
900 pine_state->mangled_footer = 1;
901 filename[0] = '\0';
902 while(1){
903 int flags = OE_APPEND_CURRENT;
905 rv = optionally_enter(filename, -FOOTER_ROWS(pine_state),
906 0, sizeof(filename),
907 /* TRANSLATORS: file is computer data */
908 _("File to open : "),
909 NULL, help, &flags);
910 if(rv == 3){
911 help = (help == NO_HELP) ? h_no_F_arg : NO_HELP;
912 continue;
915 if(rv != 4)
916 break;
919 if(rv == 1){
920 q_status_message(SM_ORDER, 0, 2, _("Cancelled"));
921 goodnight_gracey(pine_state, -1);
924 if(*filename){
925 removing_trailing_white_space(filename);
926 removing_leading_white_space(filename);
927 if(is_absolute_path(filename))
928 fnexpand(filename, sizeof(filename));
930 args.data.file = filename;
933 if(!*filename){
934 /* TRANSLATORS: file is computer data */
935 q_status_message(SM_ORDER, 0, 2 ,_("No file to open"));
936 goodnight_gracey(pine_state, -1);
940 if(stdin_getc){
941 redir++;
942 src = CharStar;
943 if(isatty(0) && (store = so_get(src, NULL, EDIT_ACCESS))){
944 gf_io_t pc;
946 gf_set_so_writec(&pc, store);
947 gf_filter_init();
948 if((decode_error = gf_pipe(stdin_getc, pc)) != NULL){
949 dice = 0;
950 q_status_message1(SM_ORDER, 3, 4,
951 _("Problem reading standard input: %s"),
952 decode_error);
955 gf_clear_so_writec(store);
957 else
958 dice = 0;
960 else{
961 src = FileStar;
962 strncpy(ps_global->cur_folder, args.data.file,
963 sizeof(ps_global->cur_folder)-1);
964 ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
965 if(!(store = so_get(src, args.data.file, READ_ACCESS|READ_FROM_LOCALE)))
966 dice = 0;
969 if(dice){
970 SCROLL_S sargs;
972 memset(&sargs, 0, sizeof(SCROLL_S));
973 sargs.text.text = so_text(store);
974 sargs.text.src = src;
975 /* TRANSLATORS: file is computer file being read by user */
976 sargs.text.desc = _("file");
977 /* TRANSLATORS: this is in the title bar at top of screen */
978 sargs.bar.title = _("FILE VIEW");
979 sargs.bar.style = FileTextPercent;
980 sargs.keys.menu = &simple_file_keymenu;
981 setbitmap(sargs.keys.bitmap);
982 scrolltool(&sargs);
984 printf("\n\n");
985 so_give(&store);
989 if(!dice){
990 q_status_message2(SM_ORDER, 3, 4,
991 _("Can't display \"%s\": %s"),
992 (redir) ? _("Standard Input")
993 : args.data.file ? args.data.file : "NULL",
994 error_description(errno));
997 goodnight_gracey(pine_state, 0);
999 else if(args.action == aaMail || (stdin_getc && (args.action != aaURL))){
1000 /*======= address on command line/send one message mode ============*/
1001 char *to = NULL, *error = NULL, *addr = NULL;
1002 int len, good_addr = 1;
1003 int exit_val = 0;
1004 BUILDER_ARG fcc;
1006 if(pine_state->in_init_seq){
1007 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
1008 clear_cursor_pos();
1009 if(pine_state->free_initial_cmds)
1010 fs_give((void **) &(pine_state->free_initial_cmds));
1012 pine_state->initial_cmds = NULL;
1015 /*----- Format the To: line with commas for the composer ---*/
1016 if(args.data.mail.addrlist){
1017 STRLIST_S *p;
1019 for(p = args.data.mail.addrlist, len = 0; p; p = p->next)
1020 len += strlen(p->name) + 2;
1022 to = (char *) fs_get((len + 5) * sizeof(char));
1023 for(p = args.data.mail.addrlist, *to = '\0'; p; p = p->next){
1024 if(*to){
1025 strncat(to, ", ", len+5-strlen(to)-1);
1026 to[len+5-1] = '\0';
1029 strncat(to, p->name, len+5-strlen(to)-1);
1030 to[len+5-1] = '\0';
1033 memset((void *)&fcc, 0, sizeof(BUILDER_ARG));
1034 dprint((2, "building addr: -->%s<--\n", to ? to : "?"));
1035 good_addr = (build_address(to, &addr, &error, &fcc, NULL) >= 0);
1036 dprint((2, "mailing to: -->%s<--\n", addr ? addr : "?"));
1037 free_strlist(&args.data.mail.addrlist);
1039 else
1040 memset(&fcc, 0, sizeof(fcc));
1042 if(good_addr){
1043 compose_mail(addr, fcc.tptr, NULL,
1044 args.data.mail.attachlist, stdin_getc);
1046 else{
1047 /* TRANSLATORS: refers to bad email address */
1048 q_status_message1(SM_ORDER, 3, 4, _("Bad address: %s"), error);
1049 exit_val = -1;
1052 if(addr)
1053 fs_give((void **) &addr);
1055 if(fcc.tptr)
1056 fs_give((void **) &fcc.tptr);
1058 if(args.data.mail.attachlist)
1059 free_attachment_list(&args.data.mail.attachlist);
1061 if(to)
1062 fs_give((void **) &to);
1064 if(error)
1065 fs_give((void **) &error);
1067 goodnight_gracey(pine_state, exit_val);
1069 else{
1070 char int_mail[MAXPATH+1];
1071 struct key_menu *km = &main_keymenu;
1073 /*========== Normal pine mail reading mode ==========*/
1075 pine_state->mail_stream = NULL;
1076 pine_state->mangled_screen = 1;
1078 if(args.action == aaURL){
1079 url_tool_t f;
1081 if(pine_state->in_init_seq){
1082 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
1083 clear_cursor_pos();
1084 if(pine_state->free_initial_cmds)
1085 fs_give((void **) &(pine_state->free_initial_cmds));
1086 pine_state->initial_cmds = NULL;
1088 if((f = url_local_handler(args.url)) != NULL){
1089 if(args.data.mail.attachlist){
1090 if(f == url_local_mailto){
1091 if(!(url_local_mailto_and_atts(args.url,
1092 args.data.mail.attachlist)
1093 && pine_state->next_screen))
1094 free_attachment_list(&args.data.mail.attachlist);
1095 goodnight_gracey(pine_state, 0);
1097 else {
1098 q_status_message(SM_ORDER | SM_DING, 3, 4,
1099 _("Only mailto URLs are allowed with file attachments"));
1100 goodnight_gracey(pine_state, -1); /* no return */
1103 else if(!((*f)(args.url) && pine_state->next_screen))
1104 goodnight_gracey(pine_state, 0); /* no return */
1106 else{
1107 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1108 _("Unrecognized URL \"%s\""), args.url);
1109 goodnight_gracey(pine_state, -1); /* no return */
1112 else if(!pine_state->start_in_index){
1113 /* flash message about executing initial commands */
1114 if(pine_state->in_init_seq){
1115 pine_state->in_init_seq = 0;
1116 clear_cursor_pos();
1117 pine_state->mangled_header = 1;
1118 pine_state->mangled_footer = 1;
1119 pine_state->mangled_screen = 0;
1120 /* show that this is Alpine */
1121 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
1122 pine_state->mangled_screen = 1;
1123 pine_state->painted_footer_on_startup = 1;
1124 if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
1125 char buf1[6*MAX_SCREEN_COLS+1];
1126 char buf2[6*MAX_SCREEN_COLS+1];
1127 int wid;
1129 /* TRANSLATORS: Initial Keystroke List is the literal name of an option */
1130 strncpy(buf1, _("Executing Initial Keystroke List......"), sizeof(buf1));
1131 buf1[sizeof(buf1)-1] = '\0';
1132 wid = utf8_width(buf1);
1133 if(wid > ps_global->ttyo->screen_cols){
1134 utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
1135 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
1137 else{
1138 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
1139 MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
1143 pine_state->in_init_seq = 1;
1145 else{
1146 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
1147 pine_state->painted_body_on_startup = 1;
1148 pine_state->painted_footer_on_startup = 1;
1151 else{
1152 /* cancel any initial commands, overridden by cmd line */
1153 if(pine_state->in_init_seq){
1154 pine_state->in_init_seq = 0;
1155 pine_state->save_in_init_seq = 0;
1156 clear_cursor_pos();
1157 if(pine_state->initial_cmds){
1158 if(pine_state->free_initial_cmds)
1159 fs_give((void **)&(pine_state->free_initial_cmds));
1161 pine_state->initial_cmds = NULL;
1164 F_SET(F_USE_FK,pine_state, pine_state->orig_use_fkeys);
1167 (void) do_index_border(pine_state->context_current,
1168 pine_state->cur_folder,
1169 pine_state->mail_stream,
1170 pine_state->msgmap, MsgIndex, NULL,
1171 INDX_CLEAR|INDX_HEADER|INDX_FOOTER);
1172 pine_state->painted_footer_on_startup = 1;
1173 if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
1174 char buf1[6*MAX_SCREEN_COLS+1];
1175 char buf2[6*MAX_SCREEN_COLS+1];
1176 int wid;
1178 strncpy(buf1, _("Please wait, opening mail folder......"), sizeof(buf1));
1179 buf1[sizeof(buf1)-1] = '\0';
1180 wid = utf8_width(buf1);
1181 if(wid > ps_global->ttyo->screen_cols){
1182 utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
1183 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
1185 else{
1186 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
1187 MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
1192 fflush(stdout);
1194 #if !defined(_WINDOWS) && !defined(LEAVEOUTFIFO)
1195 if(ps_global->VAR_FIFOPATH && ps_global->VAR_FIFOPATH[0])
1196 init_newmailfifo(ps_global->VAR_FIFOPATH);
1197 #endif
1199 if(pine_state->in_init_seq){
1200 pine_state->in_init_seq = 0;
1201 clear_cursor_pos();
1204 if(args.action == aaFolder && args.data.folder){
1205 CONTEXT_S *cntxt = NULL, *tc = NULL;
1206 char foldername[MAILTMPLEN];
1207 int notrealinbox = 0;
1209 if(args.data.folder[0] == '\0'){
1210 char *fldr;
1211 unsigned save_def_goto_rule;
1213 foldername[0] = '\0';
1214 save_def_goto_rule = pine_state->goto_default_rule;
1215 pine_state->goto_default_rule = GOTO_FIRST_CLCTN;
1216 tc = default_save_context(pine_state->context_list);
1217 fldr = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
1218 pine_state->goto_default_rule = save_def_goto_rule;
1219 if(fldr){
1220 strncpy(foldername, fldr, sizeof(foldername)-1);
1221 foldername[sizeof(foldername)-1] = '\0';
1224 if(*foldername){
1225 removing_trailing_white_space(foldername);
1226 removing_leading_white_space(foldername);
1227 args.data.folder = cpystr(foldername);
1230 if(!*foldername){
1231 q_status_message(SM_ORDER, 0, 2 ,_("No folder to open"));
1232 goodnight_gracey(pine_state, -1);
1236 if(tc)
1237 cntxt = tc;
1238 else if((rv = pine_state->init_context) < 0)
1240 * As with almost all the folder vars in the pinerc,
1241 * we subvert the collection "breakout" here if the
1242 * folder name given looks like an absolute path on
1243 * this system...
1245 cntxt = (is_absolute_path(args.data.folder))
1246 ? NULL : pine_state->context_current;
1247 else if(rv == 0)
1248 cntxt = NULL;
1249 else
1250 for(cntxt = pine_state->context_list;
1251 rv > 1 && cntxt->next;
1252 rv--, cntxt = cntxt->next)
1255 if(pine_state && pine_state->ttyo){
1256 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1257 pine_state->painted_footer_on_startup = 0;
1258 pine_state->mangled_footer = 1;
1261 if(args.data.folder && *args.data.folder
1262 && !strucmp(args.data.folder, ps_global->inbox_name)
1263 && cntxt != ps_global->context_list)
1264 notrealinbox = 1;
1266 if(do_broach_folder(args.data.folder, cntxt, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) <= 0){
1267 q_status_message1(SM_ORDER, 3, 4,
1268 _("Unable to open folder \"%s\""), args.data.folder);
1270 fs_give((void **) &args.data.folder);
1272 goodnight_gracey(pine_state, -1);
1275 else if(args.action == aaFolder){
1276 #ifdef _WINDOWS
1278 * need to ask for the inbox name if no default under DOS
1279 * since there is no "inbox"
1282 if(!pine_state->VAR_INBOX_PATH || !pine_state->VAR_INBOX_PATH[0]
1283 || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0){
1284 HelpType help = NO_HELP;
1285 static ESCKEY_S ekey[] = {{ctrl(T), 2, "^T", "To Fldrs"},
1286 {-1, 0, NULL, NULL}};
1288 pine_state->mangled_footer = 1;
1289 int_mail[0] = '\0';
1290 while(1){
1291 int flags = OE_APPEND_CURRENT;
1293 rv = optionally_enter(int_mail, -FOOTER_ROWS(pine_state),
1294 0, sizeof(int_mail),
1295 _("No inbox! Folder to open as inbox : "),
1296 /* ekey */ NULL, help, &flags);
1297 if(rv == 3){
1298 help = (help == NO_HELP) ? h_sticky_inbox : NO_HELP;
1299 continue;
1302 if(rv != 4)
1303 break;
1306 if(rv == 1){
1307 q_status_message(SM_ORDER, 0, 2 ,_("Folder open cancelled"));
1308 rv = 0; /* reset rv */
1310 else if(rv == 2){
1311 show_main_screen(pine_state,0,FirstMenu,km,0,(Pos *)NULL);
1314 if(*int_mail){
1315 removing_trailing_white_space(int_mail);
1316 removing_leading_white_space(int_mail);
1317 if((!pine_state->VAR_INBOX_PATH
1318 || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0)
1319 /* TRANSLATORS: Inbox-Path and PINERC are literal, not to be translated */
1320 && want_to(_("Preserve folder as \"Inbox-Path\" in PINERC"),
1321 'y', 'n', NO_HELP, WT_NORM) == 'y'){
1322 set_variable(V_INBOX_PATH, int_mail, 1, 1, Main);
1324 else{
1325 if(pine_state->VAR_INBOX_PATH)
1326 fs_give((void **)&pine_state->VAR_INBOX_PATH);
1328 pine_state->VAR_INBOX_PATH = cpystr(int_mail);
1331 if(pine_state && pine_state->ttyo){
1332 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1333 pine_state->painted_footer_on_startup = 0;
1334 pine_state->mangled_footer = 1;
1337 do_broach_folder(pine_state->inbox_name,
1338 pine_state->context_list, NULL, DB_INBOXWOCNTXT);
1340 else
1341 q_status_message(SM_ORDER, 0, 2 ,_("No folder opened"));
1344 else
1346 #endif /* _WINDOWS */
1347 if(F_ON(F_PREOPEN_STAYOPENS, ps_global))
1348 preopen_stayopen_folders();
1350 if(pine_state && pine_state->ttyo){
1351 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1352 pine_state->painted_footer_on_startup = 0;
1353 pine_state->mangled_footer = 1;
1356 /* open inbox */
1357 do_broach_folder(pine_state->inbox_name,
1358 pine_state->context_list, NULL, DB_INBOXWOCNTXT);
1361 if(pine_state->mangled_footer)
1362 pine_state->painted_footer_on_startup = 0;
1364 if(args.action == aaFolder
1365 && pine_state->mail_stream
1366 && expire_sent_mail())
1367 pine_state->painted_footer_on_startup = 0;
1370 * Initialize the defaults. Initializing here means that
1371 * if they're remote, the user isn't prompted for an imap login
1372 * before the display's drawn, AND there's the chance that
1373 * we can climb onto the already opened folder's stream...
1375 if(ps_global->first_time_user)
1376 init_save_defaults(); /* initialize default save folders */
1378 build_path(int_mail,
1379 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
1380 : pine_state->home_dir,
1381 INTERRUPTED_MAIL, sizeof(int_mail));
1382 if(args.action == aaFolder
1383 && (folder_exists(NULL, int_mail) & FEX_ISFILE))
1384 q_status_message(SM_ORDER | SM_DING, 4, 5,
1385 _("Use Compose command to continue interrupted message."));
1387 if(args.action == aaFolder && args.data.folder)
1388 fs_give((void **) &args.data.folder);
1390 #if defined(USE_QUOTAS)
1392 long q;
1393 int over;
1394 q = disk_quota(pine_state->home_dir, &over);
1395 if(q > 0 && over){
1396 q_status_message2(SM_ASYNC | SM_DING, 4, 5,
1397 _("WARNING! Over your disk quota by %s bytes (%s)"),
1398 comatose(q),byte_string(q));
1401 #endif
1403 pine_state->in_init_seq = pine_state->save_in_init_seq;
1404 pine_state->dont_use_init_cmds = 0;
1405 clear_cursor_pos();
1407 if(pine_state->give_fixed_warning)
1408 q_status_message(SM_ASYNC, 0, 10,
1409 /* TRANSLATORS: config is an abbreviation for configuration */
1410 _("Note: some of your config options conflict with site policy and are ignored"));
1412 if(!prune_folders_ok())
1413 q_status_message(SM_ASYNC, 0, 10,
1414 /* TRANSLATORS: Pruned-Folders is literal */
1415 _("Note: ignoring Pruned-Folders outside of default collection for saves"));
1417 if(get_input_timeout() == 0 &&
1418 ps_global->VAR_INBOX_PATH &&
1419 ps_global->VAR_INBOX_PATH[0] == '{')
1420 q_status_message(SM_ASYNC, 0, 10,
1421 _("Note: Mail-Check-Interval=0 may cause IMAP server connection to time out"));
1423 #ifdef _WINDOWS
1424 mswin_setnewmailwidth(ps_global->nmw_width);
1425 #endif
1428 /*-------------------------------------------------------------------
1429 Loop executing the commands
1431 This is done like this so that one command screen can cause
1432 another one to execute it with out going through the main menu.
1433 ------------------------------------------------------------------*/
1434 if(!pine_state->next_screen)
1435 pine_state->next_screen = pine_state->start_in_index
1436 ? mail_index_screen : main_menu_screen;
1437 while(1){
1438 if(pine_state->next_screen == SCREEN_FUN_NULL)
1439 pine_state->next_screen = main_menu_screen;
1441 (*(pine_state->next_screen))(pine_state);
1445 exit(0);
1450 * The arguments need to be converted to UTF-8 for our internal use.
1451 * Not all arguments are converted because some are used before we
1452 * are able to do the conversion, like the pinerc name.
1454 void
1455 convert_args_to_utf8(struct pine *ps, ARGDATA_S *args)
1457 char *fromcharset = NULL;
1458 char *conv;
1460 if(args){
1461 if(ps->keyboard_charmap && strucmp(ps->keyboard_charmap, "UTF-8")
1462 && strucmp(ps->keyboard_charmap, "US-ASCII"))
1463 fromcharset = ps->keyboard_charmap;
1464 else if(ps->display_charmap && strucmp(ps->display_charmap, "UTF-8")
1465 && strucmp(ps->display_charmap, "US-ASCII"))
1466 fromcharset = ps->display_charmap;
1467 #ifndef _WINDOWS
1468 else if(ps->VAR_OLD_CHAR_SET && strucmp(ps->VAR_OLD_CHAR_SET, "UTF-8")
1469 && strucmp(ps->VAR_OLD_CHAR_SET, "US-ASCII"))
1470 fromcharset = ps->VAR_OLD_CHAR_SET;
1471 #endif /* ! _WINDOWS */
1473 if(args->action == aaURL && args->url){
1474 conv = convert_to_utf8(args->url, fromcharset, 0);
1475 if(conv){
1476 fs_give((void **) &args->url);
1477 args->url = conv;
1481 if(args->action == aaFolder && args->data.folder){
1482 conv = convert_to_utf8(args->data.folder, fromcharset, 0);
1483 if(conv){
1484 fs_give((void **) &args->data.folder);
1485 args->data.folder = conv;
1489 if(args->action == aaMore && args->data.file){
1490 conv = convert_to_utf8(args->data.file, fromcharset, 0);
1491 if(conv){
1492 fs_give((void **) &args->data.file);
1493 args->data.file = conv;
1497 if(args->action == aaURL || args->action == aaMail){
1498 if(args->data.mail.addrlist){
1499 STRLIST_S *p;
1501 for(p = args->data.mail.addrlist; p; p=p->next){
1502 if(p->name){
1503 conv = convert_to_utf8(p->name, fromcharset, 0);
1504 if(conv){
1505 fs_give((void **) &p->name);
1506 p->name = conv;
1512 if(args->data.mail.attachlist){
1513 PATMT *p;
1515 for(p = args->data.mail.attachlist; p; p=p->next){
1516 if(p->filename){
1517 conv = convert_to_utf8(p->filename, fromcharset, 0);
1518 if(conv){
1519 fs_give((void **) &p->filename);
1520 p->filename = conv;
1530 void
1531 preopen_stayopen_folders(void)
1533 char **open_these;
1535 for(open_these = ps_global->VAR_PERMLOCKED;
1536 open_these && *open_these; open_these++)
1537 (void) do_broach_folder(*open_these, ps_global->context_list,
1538 NULL, DB_NOVISIT);
1543 * read_stdin_char - simple function to return a character from
1544 * redirected stdin
1547 read_stdin_char(char *c)
1549 int rv;
1551 /* it'd probably be a good idea to fix this to pre-read blocks */
1552 while(1){
1553 rv = read(PIPED_FD, c, 1);
1554 if(rv < 0){
1555 if(errno == EINTR){
1556 dprint((2, "read_stdin_char: read interrupted, restarting\n"));
1557 continue;
1559 else
1560 dprint((1, "read_stdin_char: read FAILED: %s\n",
1561 error_description(errno)));
1563 break;
1565 return(rv);
1569 /* this default is from the array of structs below */
1570 #define DEFAULT_MENU_ITEM ((unsigned) 3) /* LIST FOLDERS */
1571 #define ABOOK_MENU_ITEM ((unsigned) 4) /* ADDRESS BOOK */
1572 #define MAX_MENU_ITEM ((unsigned) 6)
1574 * Skip this many spaces between rows of main menu screen.
1575 * We have MAX_MENU_ITEM+1 = # of commands in menu
1576 * 1 = copyright line
1577 * MAX_MENU_ITEM = rows between commands
1578 * 1 = extra row above commands
1579 * 1 = row between commands and copyright
1581 * To make it simple, if there is enough room for all of that include all the
1582 * extra space, if not, cut it all out.
1584 #define MNSKIP(X) (((HEADER_ROWS(X)+FOOTER_ROWS(X)+(MAX_MENU_ITEM+1)+1+MAX_MENU_ITEM+1+1) <= (X)->ttyo->screen_rows) ? 1 : 0)
1586 static unsigned menu_index = DEFAULT_MENU_ITEM;
1589 * One of these for each line that gets printed in the middle of the
1590 * screen in the main menu.
1592 static struct menu_key {
1593 char *key_and_name,
1594 *news_addition;
1595 int key_index; /* index into keymenu array for this cmd */
1596 } mkeys[] = {
1598 * TRANSLATORS: These next few are headings on the Main alpine menu.
1599 * It's nice if the dashes can be made to line up vertically.
1601 {N_(" %s HELP - Get help using Alpine"),
1602 NULL, MAIN_HELP_KEY},
1603 {N_(" %s COMPOSE MESSAGE - Compose and send%s a message"),
1604 /* TRANSLATORS: We think of sending an email message or posting a news message.
1605 The message is shown as Compose and send/post a message */
1606 N_("/post"), MAIN_COMPOSE_KEY},
1607 {N_(" %s MESSAGE INDEX - View messages in current folder"),
1608 NULL, MAIN_INDEX_KEY},
1609 {N_(" %s FOLDER LIST - Select a folder%s to view"),
1610 /* TRANSLATORS: When news is supported the message above becomes
1611 Select a folder OR news group to view */
1612 N_(" OR news group"), MAIN_FOLDER_KEY},
1613 {N_(" %s ADDRESS BOOK - Update address book"),
1614 NULL, MAIN_ADDRESS_KEY},
1615 {N_(" %s SETUP - Configure Alpine Options"),
1616 NULL, MAIN_SETUP_KEY},
1617 /* TRANSLATORS: final Main menu line */
1618 {N_(" %s QUIT - Leave the Alpine program"),
1619 NULL, MAIN_QUIT_KEY}
1624 /*----------------------------------------------------------------------
1625 display main menu and execute main menu commands
1627 Args: The usual pine structure
1629 Result: main menu commands are executed
1632 M A I N M E N U S C R E E N
1634 Paint the main menu on the screen, get the commands and either execute
1635 the function or pass back the name of the function to execute for the menu
1636 selection. Only simple functions that always return here can be executed
1637 here.
1639 This functions handling of new mail, redrawing, errors and such can
1640 serve as a template for the other screen that do much the same thing.
1642 There is a loop that fetches and executes commands until a command to leave
1643 this screen is given. Then the name of the next screen to display is
1644 stored in next_screen member of the structure and this function is exited
1645 with a return.
1647 First a check for new mail is performed. This might involve reading the new
1648 mail into the inbox which might then cause the screen to be repainted.
1650 Then the general screen painting is done. This is usually controlled
1651 by a few flags and some other position variables. If they change they
1652 tell this part of the code what to repaint. This will include cursor
1653 motion and so on.
1654 ----*/
1655 void
1656 main_menu_screen(struct pine *pine_state)
1658 UCS ch;
1659 int cmd, just_a_navigate_cmd, setup_command, km_popped;
1660 int notrealinbox;
1661 char *new_folder, *utf8str;
1662 CONTEXT_S *tc;
1663 struct key_menu *km;
1664 OtherMenu what;
1665 Pos curs_pos;
1667 ps_global = pine_state;
1668 just_a_navigate_cmd = 0;
1669 km_popped = 0;
1670 menu_index = DEFAULT_MENU_ITEM;
1671 what = FirstMenu; /* which keymenu to display */
1672 ch = 'x'; /* For display_message 1st time through */
1673 pine_state->next_screen = SCREEN_FUN_NULL;
1674 pine_state->prev_screen = main_menu_screen;
1675 curs_pos.row = pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
1676 curs_pos.col = 0;
1677 km = &main_keymenu;
1679 mailcap_free(); /* free resources we won't be using for a while */
1681 if(!pine_state->painted_body_on_startup
1682 && !pine_state->painted_footer_on_startup){
1683 pine_state->mangled_screen = 1;
1686 dprint((1, "\n\n ---- MAIN_MENU_SCREEN ----\n"));
1688 while(1){
1689 if(km_popped){
1690 km_popped--;
1691 if(km_popped == 0){
1692 clearfooter(pine_state);
1693 pine_state->mangled_body = 1;
1698 * fix up redrawer just in case some submenu caused it to get
1699 * reassigned...
1701 pine_state->redrawer = main_redrawer;
1703 /*----------- Check for new mail -----------*/
1704 if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
1705 pine_state->mangled_header = 1;
1707 if(streams_died())
1708 pine_state->mangled_header = 1;
1710 show_main_screen(pine_state, just_a_navigate_cmd, what, km,
1711 km_popped, &curs_pos);
1712 just_a_navigate_cmd = 0;
1713 what = SameMenu;
1715 /*---- This displays new mail notification, or errors ---*/
1716 if(km_popped){
1717 FOOTER_ROWS(pine_state) = 3;
1718 mark_status_dirty();
1721 display_message(ch);
1722 if(km_popped){
1723 FOOTER_ROWS(pine_state) = 1;
1724 mark_status_dirty();
1727 if(F_OFF(F_SHOW_CURSOR, ps_global)){
1728 curs_pos.row =pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
1729 curs_pos.col =0;
1732 MoveCursor(curs_pos.row, curs_pos.col);
1734 /*------ Read the command from the keyboard ----*/
1735 #ifdef MOUSE
1736 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
1737 register_mfunc(mouse_in_content, HEADER_ROWS(pine_state), 0,
1738 pine_state->ttyo->screen_rows-(FOOTER_ROWS(pine_state)+1),
1739 pine_state->ttyo->screen_cols);
1740 #endif
1741 #if defined(DOS) || defined(OS2)
1743 * AND pre-build header lines. This works just fine under
1744 * DOS since we wait for characters in a loop. Something will
1745 * will have to change under UNIX if we want to do the same.
1747 /* while_waiting = build_header_cache; */
1748 #ifdef _WINDOWS
1749 mswin_sethelptextcallback(pcpine_help_main);
1750 mswin_mousetrackcallback(pcpine_main_cursor);
1751 #endif
1752 #endif
1753 ch = READ_COMMAND(&utf8str);
1754 #ifdef MOUSE
1755 clear_mfunc(mouse_in_content);
1756 #endif
1757 #if defined(DOS) || defined(OS2)
1758 /* while_waiting = NULL; */
1759 #ifdef _WINDOWS
1760 mswin_sethelptextcallback(NULL);
1761 mswin_mousetrackcallback(NULL);
1762 #endif
1763 #endif
1765 /* No matter what, Quit here always works */
1766 if(ch == 'q' || ch == 'Q'){
1767 cmd = MC_QUIT;
1769 #ifdef DEBUG
1770 else if(debug && ch && ch < 0x80 && strchr("123456789", ch)){
1771 int olddebug;
1773 olddebug = debug;
1774 debug = ch - '0';
1775 if(debug > 7)
1776 ps_global->debug_timestamp = 1;
1777 else
1778 ps_global->debug_timestamp = 0;
1780 if(debug > 7)
1781 ps_global->debug_imap = 4;
1782 else if(debug > 6)
1783 ps_global->debug_imap = 3;
1784 else if(debug > 4)
1785 ps_global->debug_imap = 2;
1786 else if(debug > 2)
1787 ps_global->debug_imap = 1;
1788 else
1789 ps_global->debug_imap = 0;
1791 if(ps_global->mail_stream){
1792 if(ps_global->debug_imap > 0){
1793 mail_debug(ps_global->mail_stream);
1794 #ifdef _WINDOWS
1795 mswin_enableimaptelemetry(TRUE);
1796 #endif
1798 else{
1799 mail_nodebug(ps_global->mail_stream);
1800 #ifdef _WINDOWS
1801 mswin_enableimaptelemetry(FALSE);
1802 #endif
1806 if(debug > 7 && olddebug <= 7)
1807 mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
1808 else if(debug <= 7 && olddebug > 7 && !ps_global->debugmem)
1809 mail_parameters(NULL, SET_TCPDEBUG, (void *) FALSE);
1811 dprint((1, "*** Debug level set to %d ***\n", debug));
1812 if(debugfile)
1813 fflush(debugfile);
1815 q_status_message1(SM_ORDER, 0, 1, _("Debug level set to %s"),
1816 int2string(debug));
1817 continue;
1819 #endif /* DEBUG */
1820 else{
1821 cmd = menu_command(ch, km);
1823 if(km_popped)
1824 switch(cmd){
1825 case MC_NONE :
1826 case MC_OTHER :
1827 case MC_RESIZE :
1828 case MC_REPAINT :
1829 km_popped++;
1830 break;
1832 default:
1833 clearfooter(pine_state);
1834 break;
1838 /*------ Execute the command ------*/
1839 switch (cmd){
1840 help_case :
1841 /*------ HELP ------*/
1842 case MC_HELP :
1844 if(FOOTER_ROWS(pine_state) == 1 && km_popped == 0){
1845 km_popped = 2;
1846 pine_state->mangled_footer = 1;
1848 else{
1849 /* TRANSLATORS: This is a screen title */
1850 helper(main_menu_tx, _("HELP FOR MAIN MENU"), 0);
1851 pine_state->mangled_screen = 1;
1854 break;
1857 /*---------- display other key bindings ------*/
1858 case MC_OTHER :
1859 if(ch == 'o')
1860 warn_other_cmds();
1862 what = NextMenu;
1863 pine_state->mangled_footer = 1;
1864 break;
1867 /*---------- Previous item in menu ----------*/
1868 case MC_PREVITEM :
1869 if(menu_index > 0) {
1870 menu_index--;
1871 pine_state->mangled_body = 1;
1872 if(km->which == 0)
1873 pine_state->mangled_footer = 1;
1875 just_a_navigate_cmd++;
1877 else
1878 /* TRANSLATORS: list refers to list of commands in main menu */
1879 q_status_message(SM_ORDER, 0, 2, _("Already at top of list"));
1881 break;
1884 /*---------- Next item in menu ----------*/
1885 case MC_NEXTITEM :
1886 if(menu_index < MAX_MENU_ITEM){
1887 menu_index++;
1888 pine_state->mangled_body = 1;
1889 if(km->which == 0)
1890 pine_state->mangled_footer = 1;
1892 just_a_navigate_cmd++;
1894 else
1895 q_status_message(SM_ORDER, 0, 2, _("Already at bottom of list"));
1897 break;
1900 /*---------- Release Notes ----------*/
1901 case MC_RELNOTES :
1902 /* TRANSLATORS: This is a screen title */
1903 helper(h_news, _("ALPINE RELEASE NOTES"), 0);
1904 pine_state->mangled_screen = 1;
1905 break;
1908 #ifdef KEYBOARD_LOCK
1909 /*---------- Keyboard lock ----------*/
1910 case MC_KBLOCK :
1911 (void) lock_keyboard();
1912 pine_state->mangled_screen = 1;
1913 break;
1914 #endif /* KEYBOARD_LOCK */
1917 /*---------- Quit pine ----------*/
1918 case MC_QUIT :
1919 pine_state->next_screen = quit_screen;
1920 return;
1923 /*---------- Go to composer ----------*/
1924 case MC_COMPOSE :
1925 pine_state->next_screen = compose_screen;
1926 return;
1929 /*---- Go to alternate composer ------*/
1930 case MC_ROLE :
1931 pine_state->next_screen = alt_compose_screen;
1932 return;
1935 /*---------- Top of Folder list ----------*/
1936 case MC_COLLECTIONS :
1937 pine_state->next_screen = folder_screen;
1938 return;
1941 /*---------- Goto new folder ----------*/
1942 case MC_GOTO :
1943 tc = ps_global->context_current;
1944 new_folder = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
1945 if(new_folder)
1946 visit_folder(ps_global, new_folder, tc, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT);
1948 return;
1951 /*---------- Go to index ----------*/
1952 case MC_INDEX :
1953 if(THREADING()
1954 && sp_viewing_a_thread(pine_state->mail_stream)
1955 && unview_thread(pine_state, pine_state->mail_stream,
1956 pine_state->msgmap)){
1957 pine_state->view_skipped_index = 0;
1958 pine_state->mangled_screen = 1;
1961 pine_state->next_screen = mail_index_screen;
1962 return;
1965 /*---------- Review Status Messages ----------*/
1966 case MC_JOURNAL :
1967 review_messages();
1968 pine_state->mangled_screen = 1;
1969 break;
1972 /*---------- Setup mini menu ----------*/
1973 case MC_SETUP :
1974 setup_case :
1975 setup_command = setup_menu(pine_state);
1976 pine_state->mangled_footer = 1;
1977 do_setup_task(setup_command);
1978 if(ps_global->next_screen != main_menu_screen)
1979 return;
1981 break;
1984 /*---------- Go to address book ----------*/
1985 case MC_ADDRBOOK :
1986 pine_state->next_screen = addr_book_screen;
1987 return;
1990 /*------ Repaint the works -------*/
1991 case MC_RESIZE :
1992 case MC_REPAINT :
1993 ClearScreen();
1994 pine_state->mangled_screen = 1;
1995 break;
1998 #ifdef MOUSE
1999 /*------- Mouse event ------*/
2000 case MC_MOUSE :
2002 MOUSEPRESS mp;
2003 unsigned ndmi;
2004 struct pine *ps = pine_state;
2006 mouse_get_last (NULL, &mp);
2008 #ifdef _WINDOWS
2009 if(mp.button == M_BUTTON_RIGHT){
2010 if(!mp.doubleclick){
2011 static MPopup main_popup[] = {
2012 {tQueue, {"Folder List", lNormal}, {'L'}},
2013 {tQueue, {"Message Index", lNormal}, {'I'}},
2014 {tSeparator},
2015 {tQueue, {"Address Book", lNormal}, {'A'}},
2016 {tQueue, {"Setup Options", lNormal}, {'S'}},
2017 {tTail}
2020 mswin_popup(main_popup);
2023 else {
2024 #endif
2025 if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps)))
2026 ndmi = (mp.row+1 - HEADER_ROWS(ps) - (MNSKIP(ps)+1))/(MNSKIP(ps)+1);
2028 if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps))
2029 && !(MNSKIP(ps) && (mp.row+1) & 0x01)
2030 && ndmi <= MAX_MENU_ITEM
2031 && FOOTER_ROWS(ps) + (ndmi+1)*(MNSKIP(ps)+1)
2032 + MNSKIP(ps) + FOOTER_ROWS(ps) <= ps->ttyo->screen_rows){
2033 if(mp.doubleclick){
2034 switch(ndmi){ /* fake main_screen request */
2035 case 0 :
2036 goto help_case;
2038 case 1 :
2039 pine_state->next_screen = compose_screen;
2040 return;
2042 case 2 :
2043 pine_state->next_screen = mail_index_screen;
2044 return;
2046 case 3 :
2047 pine_state->next_screen = folder_screen;
2048 return;
2050 case 4 :
2051 pine_state->next_screen = addr_book_screen;
2052 return;
2054 case 5 :
2055 goto setup_case;
2057 case 6 :
2058 pine_state->next_screen = quit_screen;
2059 return;
2061 default: /* no op */
2062 break;
2065 else{
2066 menu_index = ndmi;
2067 pine_state->mangled_body = 1;
2068 if(km->which == 0)
2069 pine_state->mangled_footer = 1;
2071 just_a_navigate_cmd++;
2074 #ifdef _WINDOWS
2076 #endif
2079 break;
2080 #endif
2083 /*------ Input timeout ------*/
2084 case MC_NONE :
2085 break; /* noop for timeout loop mail check */
2088 /*------ Bogus Input ------*/
2089 case MC_UNKNOWN :
2090 if(ch == 'm' || ch == 'M'){
2091 q_status_message(SM_ORDER, 0, 1, "Already in Main Menu");
2092 break;
2095 default:
2096 bogus_command(ch, F_ON(F_USE_FK,pine_state) ? "F1" : "?");
2097 break;
2099 case MC_UTF8:
2100 bogus_utf8_command(utf8str, F_ON(F_USE_FK, pine_state) ? "F1" : "?");
2101 break;
2102 } /* the switch */
2103 } /* the BIG while loop! */
2107 /*----------------------------------------------------------------------
2108 Re-Draw the main menu
2110 Args: none.
2112 Result: main menu is re-displayed
2113 ----*/
2114 void
2115 main_redrawer(void)
2117 struct key_menu *km = &main_keymenu;
2119 ps_global->mangled_screen = 1;
2120 show_main_screen(ps_global, 0, FirstMenu, km, 0, (Pos *)NULL);
2124 /*----------------------------------------------------------------------
2125 Draw the main menu
2127 Args: pine_state - the usual struct
2128 quick_draw - tells do_menu() it can skip some drawing
2129 what - tells which section of keymenu to draw
2130 km - the keymenu
2131 cursor_pos - returns a good position for the cursor to be located
2133 Result: main menu is displayed
2134 ----*/
2135 void
2136 show_main_screen(struct pine *ps, int quick_draw, OtherMenu what,
2137 struct key_menu *km, int km_popped, Pos *cursor_pos)
2139 if(ps->painted_body_on_startup || ps->painted_footer_on_startup){
2140 ps->mangled_screen = 0; /* only worry about it here */
2141 ps->mangled_header = 1; /* we have to redo header */
2142 if(!ps->painted_body_on_startup)
2143 ps->mangled_body = 1; /* make sure to paint body*/
2145 if(!ps->painted_footer_on_startup)
2146 ps->mangled_footer = 1; /* make sure to paint footer*/
2148 ps->painted_body_on_startup = 0;
2149 ps->painted_footer_on_startup = 0;
2152 if(ps->mangled_screen){
2153 ps->mangled_header = 1;
2154 ps->mangled_body = 1;
2155 ps->mangled_footer = 1;
2156 ps->mangled_screen = 0;
2159 #ifdef _WINDOWS
2160 /* Reset the scroll range. Main screen never scrolls. */
2161 scroll_setrange (0L, 0L);
2162 mswin_beginupdate();
2163 #endif
2165 /* paint the titlebar if needed */
2166 if(ps->mangled_header){
2167 /* TRANSLATORS: screen title */
2168 set_titlebar(_("MAIN MENU"), ps->mail_stream, ps->context_current,
2169 ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0, NULL);
2170 ps->mangled_header = 0;
2173 /* paint the body if needed */
2174 if(ps->mangled_body){
2175 if(!quick_draw)
2176 ClearBody();
2178 do_menu(quick_draw, cursor_pos, km);
2179 ps->mangled_body = 0;
2182 /* paint the keymenu if needed */
2183 if(km && ps->mangled_footer){
2184 static char label[LONGEST_LABEL + 2 + 1], /* label + brackets + \0 */
2185 name[8];
2186 bitmap_t bitmap;
2188 setbitmap(bitmap);
2190 #ifdef KEYBOARD_LOCK
2191 if(ps_global->restricted || F_ON(F_DISABLE_KBLOCK_CMD,ps_global))
2192 #endif
2193 clrbitn(MAIN_KBLOCK_KEY, bitmap);
2195 menu_clear_binding(km, '>');
2196 menu_clear_binding(km, '.');
2197 menu_clear_binding(km, KEY_RIGHT);
2198 menu_clear_binding(km, ctrl('M'));
2199 menu_clear_binding(km, ctrl('J'));
2200 km->keys[MAIN_DEFAULT_KEY].bind
2201 = km->keys[mkeys[menu_index].key_index].bind;
2202 km->keys[MAIN_DEFAULT_KEY].label
2203 = km->keys[mkeys[menu_index].key_index].label;
2205 /* put brackets around the default action */
2206 snprintf(label, sizeof(label), "[%s]", km->keys[mkeys[menu_index].key_index].label);
2207 label[sizeof(label)-1] = '\0';
2208 strncpy(name, ">", sizeof(name));
2209 name[sizeof(name)-1] = '\0';
2210 km->keys[MAIN_DEFAULT_KEY].label = label;
2211 km->keys[MAIN_DEFAULT_KEY].name = name;
2212 menu_add_binding(km, '>', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2213 menu_add_binding(km, '.', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2214 menu_add_binding(km, ctrl('M'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2215 menu_add_binding(km, ctrl('J'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2217 if(F_ON(F_ARROW_NAV,ps_global))
2218 menu_add_binding(km, KEY_RIGHT, km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2220 if(km_popped){
2221 FOOTER_ROWS(ps) = 3;
2222 clearfooter(ps);
2225 draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
2226 1-FOOTER_ROWS(ps_global), 0, what);
2227 ps->mangled_footer = 0;
2228 if(km_popped){
2229 FOOTER_ROWS(ps) = 1;
2230 mark_keymenu_dirty();
2234 #ifdef _WINDOWS
2235 mswin_endupdate();
2236 #endif
2240 /*----------------------------------------------------------------------
2241 Actually display the main menu
2243 Args: quick_draw - just a next or prev command was typed so we only have
2244 to redraw the highlighting
2245 cursor_pos - a place to return a good value for cursor location
2247 Result: Main menu is displayed
2248 ---*/
2249 void
2250 do_menu(int quick_draw, Pos *cursor_pos, struct key_menu *km)
2252 struct pine *ps = ps_global;
2253 int dline, indent, longest = 0, cmd;
2254 char buf[4*MAX_SCREEN_COLS+1];
2255 char buf2[4*MAX_SCREEN_COLS+1];
2256 static int last_inverse = -1;
2257 Pos pos;
2259 /* find the longest command */
2260 for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
2261 memset((void *) buf, ' ', sizeof(buf));
2262 snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
2263 (F_OFF(F_USE_FK,ps)
2264 && km->keys[mkeys[cmd].key_index].name)
2265 ? km->keys[mkeys[cmd].key_index].name : "",
2266 (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
2267 ? _(mkeys[cmd].news_addition) : "");
2268 buf[sizeof(buf)-1] = '\0';
2270 if(longest < (indent = utf8_width(buf)))
2271 longest = indent;
2274 indent = MAX(((ps->ttyo->screen_cols - longest)/2) - 1, 0);
2276 dline = HEADER_ROWS(ps) + MNSKIP(ps);
2277 for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
2278 /* leave room for copyright and footer */
2279 if(dline + MNSKIP(ps) + 1 + FOOTER_ROWS(ps) >= ps->ttyo->screen_rows)
2280 break;
2282 if(quick_draw && !(cmd == last_inverse || cmd == menu_index)){
2283 dline += (1 + MNSKIP(ps));
2284 continue;
2287 if(cmd == menu_index)
2288 StartInverse();
2290 memset((void *) buf, ' ', sizeof(buf));
2291 snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
2292 (F_OFF(F_USE_FK,ps)
2293 && km->keys[mkeys[cmd].key_index].name)
2294 ? km->keys[mkeys[cmd].key_index].name : "",
2295 (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
2296 ? _(mkeys[cmd].news_addition) : "");
2297 buf[sizeof(buf)-1] = '\0';
2299 utf8_pad_to_width(buf2, buf, sizeof(buf2),
2300 MIN(ps->ttyo->screen_cols-indent,longest+1), 1);
2301 pos.row = dline++;
2302 pos.col = indent;
2303 PutLine0(pos.row, pos.col, buf2);
2305 if(MNSKIP(ps))
2306 dline++;
2308 if(cmd == menu_index){
2309 if(cursor_pos){
2310 cursor_pos->row = pos.row;
2311 /* 6 is 1 for the letter plus 5 spaces */
2312 cursor_pos->col = pos.col + 6;
2313 if(F_OFF(F_USE_FK,ps))
2314 cursor_pos->col++;
2316 cursor_pos->col = MIN(cursor_pos->col, ps->ttyo->screen_cols);
2319 EndInverse();
2324 last_inverse = menu_index;
2326 if(!quick_draw && FOOTER_ROWS(ps)+1 < ps->ttyo->screen_rows){
2327 utf8_to_width(buf2, LEGAL_NOTICE, sizeof(buf2),
2328 ps->ttyo->screen_cols-3, NULL);
2329 PutLine0(ps->ttyo->screen_rows - (FOOTER_ROWS(ps)+1),
2330 MAX(0, ((ps->ttyo->screen_cols-utf8_width(buf2))/2)),
2331 buf2);
2334 fflush(stdout);
2339 choose_setup_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2341 int rv = 1;
2342 SRV_S *srv;
2344 if(!(srv = (SRV_S *)sparms->proc.data.p)){
2345 sparms->proc.data.p = (SRV_S *)fs_get(sizeof(*srv));
2346 srv = (SRV_S *)sparms->proc.data.p;
2347 memset(srv, 0, sizeof(*srv));
2350 ps_global->next_screen = SCREEN_FUN_NULL;
2352 switch(cmd){
2353 case MC_PRINTER :
2354 srv->cmd = 'p';
2355 break;
2357 case MC_PASSWD :
2358 srv->cmd = 'n';
2359 break;
2361 case MC_CONFIG :
2362 srv->cmd = 'c';
2363 break;
2365 case MC_XOAUTH2 :
2366 srv->cmd = 'u';
2367 break;
2369 case MC_SIG :
2370 srv->cmd = 's';
2371 break;
2373 case MC_ABOOKS :
2374 srv->cmd = 'a';
2375 break;
2377 case MC_CLISTS :
2378 srv->cmd = 'l';
2379 break;
2381 case MC_RULES :
2382 srv->cmd = 'r';
2383 break;
2385 case MC_DIRECTORY :
2386 srv->cmd = 'd';
2387 break;
2389 case MC_KOLOR :
2390 srv->cmd = 'k';
2391 break;
2393 case MC_REMOTE :
2394 srv->cmd = 'z';
2395 break;
2397 case MC_SECURITY : /* S/MIME setup screen */
2398 srv->cmd = 'm';
2399 break;
2401 case MC_EXCEPT :
2402 srv->exc = !srv->exc;
2403 menu_clear_binding(sparms->keys.menu, 'x');
2404 if(srv->exc){
2405 if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
2406 /* TRANSLATORS: screen title */
2407 sparms->bar.title = cpystr(_("SETUP EXCEPTIONS"));
2408 ps_global->mangled_header = 1;
2409 /* TRANSLATORS: The reason the X is upper case in eXceptions
2410 is because the command key is X. It isn't necessary, just
2411 nice if it works. */
2412 menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
2413 N_("not eXceptions"), SETUP_EXCEPT);
2415 else{
2416 if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
2417 /* TRANSLATORS: screen title */
2418 sparms->bar.title = cpystr(_("SETUP"));
2419 ps_global->mangled_header = 1;
2420 menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
2421 N_("eXceptions"), SETUP_EXCEPT);
2424 if(sparms->keys.menu->which == 1)
2425 ps_global->mangled_footer = 1;
2427 rv = 0;
2428 break;
2430 case MC_NO_EXCEPT :
2431 #if defined(DOS) || defined(OS2)
2432 q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \"PINERCEX\" file to use eXceptions"));
2433 #else
2434 q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \".pinercex\" file to use eXceptions"));
2435 #endif
2436 rv = 0;
2437 break;
2439 default:
2440 alpine_panic("Unexpected command in choose_setup_cmd");
2441 break;
2444 return(rv);
2449 setup_menu(struct pine *ps)
2451 int ret = 0, exceptions = 0;
2452 int printer = 0, passwd = 0, config = 0, sig = 0, dir = 0, smime = 0, exc = 0;
2453 SCROLL_S sargs;
2454 SRV_S *srv;
2455 STORE_S *store;
2457 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2458 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
2459 return(ret);
2462 #if !defined(DOS)
2463 if(!ps_global->vars[V_PRINTER].is_fixed) /* printer can be changed */
2464 printer++;
2465 #endif
2467 #ifdef PASSWD_PROG
2468 if(F_OFF(F_DISABLE_PASSWORD_CMD,ps_global)) /* password is allowed */
2469 passwd++;
2470 #endif
2472 if(F_OFF(F_DISABLE_CONFIG_SCREEN,ps_global)) /* config allowed */
2473 config++;
2475 if(F_OFF(F_DISABLE_SIGEDIT_CMD,ps_global)) /* .sig editing is allowed */
2476 sig++;
2478 #ifdef ENABLE_LDAP
2479 dir++;
2480 #endif
2482 #ifdef SMIME
2483 smime++;
2484 #endif
2486 if(ps_global->post_prc)
2487 exc++;
2489 /* TRANSLATORS: starting here we have a whole screen of help text */
2490 so_puts(store, _("This is the Setup screen for Alpine. Choose from the following commands:\n"));
2492 so_puts(store, "\n");
2493 so_puts(store, _("(E) Exit Setup:\n"));
2494 so_puts(store, _(" This puts you back at the Main Menu.\n"));
2496 if(exc){
2497 so_puts(store, "\n");
2498 so_puts(store, _("(X) eXceptions:\n"));
2499 so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
2500 so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
2501 so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
2502 so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
2503 so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
2504 so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
2505 so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
2506 so_puts(store, _(" on a particular platform.\n"));
2507 so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
2508 so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
2509 so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
2512 if(printer){
2513 so_puts(store, "\n");
2514 so_puts(store, _("(P) Printer:\n"));
2515 so_puts(store, _(" Allows you to set a default printer and to define custom\n"));
2516 so_puts(store, _(" print commands.\n"));
2519 if(passwd){
2520 so_puts(store, "\n");
2521 so_puts(store, _("(N) Newpassword:\n"));
2522 so_puts(store, _(" Change your password.\n"));
2525 if(config){
2526 so_puts(store, "\n");
2527 so_puts(store, _("(C) Config:\n"));
2528 so_puts(store, _(" Allows you to set or unset many features of Alpine.\n"));
2529 so_puts(store, _(" You may also set the values of many options with this command.\n"));
2532 if(sig){
2533 so_puts(store, "\n");
2534 so_puts(store, _("(S) Signature:\n"));
2535 so_puts(store, _(" Enter or edit a custom signature which will\n"));
2536 so_puts(store, _(" be included with each new message you send.\n"));
2539 so_puts(store, "\n");
2540 so_puts(store, _("(A) AddressBooks:\n"));
2541 so_puts(store, _(" Define a non-default address book.\n"));
2543 so_puts(store, "\n");
2544 so_puts(store, _("(L) collectionLists:\n"));
2545 so_puts(store, _(" You may define groups of folders to help you better organize your mail.\n"));
2547 so_puts(store, "\n");
2548 so_puts(store, _("(R) Rules:\n"));
2549 so_puts(store, _(" This has up to six sub-categories: Roles, Index Colors, Filters,\n"));
2550 so_puts(store, _(" SetScores, Search, and Other. If the Index Colors option is\n"));
2551 so_puts(store, _(" missing you may turn it on (if possible) with Setup/Kolor.\n"));
2552 so_puts(store, _(" If Roles is missing it has probably been administratively disabled.\n"));
2554 if(dir){
2555 so_puts(store, "\n");
2556 so_puts(store, _("(D) Directory:\n"));
2557 so_puts(store, _(" Define an LDAP Directory server for Alpine's use. A directory server is\n"));
2558 so_puts(store, _(" similar to an address book, but it is usually maintained by an\n"));
2559 so_puts(store, _(" organization. It is similar to a telephone directory.\n"));
2562 so_puts(store, "\n");
2563 so_puts(store, _("(K) Kolor:\n"));
2564 so_puts(store, _(" Set custom colors for various parts of the Alpine screens. For example, the\n"));
2565 so_puts(store, _(" command key labels, the titlebar at the top of each page, and quoted\n"));
2566 so_puts(store, _(" sections of messages you are viewing.\n"));
2568 if(smime){
2569 so_puts(store, "\n");
2570 so_puts(store, _("(M) S/MIME:\n"));
2571 so_puts(store, _(" Setup for using S/MIME to verify signed messages, decrypt\n"));
2572 so_puts(store, _(" encrypted messages, and to sign or encrypt outgoing messages.\n"));
2575 so_puts(store, "\n");
2576 so_puts(store, _("(U) xoaUth2:\n"));
2577 so_puts(store, _(" Set client-id and client-secret to use the XOAUTH2\n"));
2578 so_puts(store, _(" authenticator.\n"));
2580 so_puts(store, "\n");
2581 so_puts(store, _("(Z) RemoteConfigSetup:\n"));
2582 so_puts(store, _(" This is a command you will probably only want to use once, if at all.\n"));
2583 so_puts(store, _(" It helps you transfer your Alpine configuration data to an IMAP server,\n"));
2584 so_puts(store, _(" where it will be accessible from any of the computers you read mail\n"));
2585 so_puts(store, _(" from (using Alpine). The idea behind a remote configuration is that you\n"));
2586 so_puts(store, _(" can change your configuration in one place and have that change show\n"));
2587 so_puts(store, _(" up on all of the computers you use.\n"));
2588 so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
2589 so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
2590 so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
2592 /* put this down here for people who don't have exceptions */
2593 if(!exc){
2594 so_puts(store, "\n");
2595 so_puts(store, _("(X) eXceptions:\n"));
2596 so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
2597 so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
2598 so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
2599 so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
2600 so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
2601 so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
2602 so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
2603 so_puts(store, _(" on a particular platform.\n"));
2604 so_puts(store, _(" (Note: this command does not do anything unless you have a configuration\n"));
2605 so_puts(store, _(" with exceptions enabled (you don't have that). Common ways to enable an\n"));
2606 so_puts(store, _(" exceptions config are the command line argument \"-x <exception_config>\";\n"));
2607 so_puts(store, _(" or the existence of the file \".pinercex\" for Unix Alpine, or \"PINERCEX\")\n"));
2608 so_puts(store, _(" for PC-Alpine.)\n"));
2609 so_puts(store, _(" (Another note: this command does not show up on the keymenu at the bottom\n"));
2610 so_puts(store, _(" of the screen unless you press \"O\" for \"Other Commands\" --but you\n"));
2611 so_puts(store, _(" don't need to press the \"O\" in order to invoke the command.)\n"));
2614 memset(&sargs, 0, sizeof(SCROLL_S));
2615 sargs.text.text = so_text(store);
2616 sargs.text.src = CharStar;
2617 sargs.text.desc = _("Information About Setup Command");
2618 sargs.bar.title = cpystr(_("SETUP"));
2619 sargs.proc.tool = choose_setup_cmd;
2620 sargs.help.text = NO_HELP;
2621 sargs.help.title = NULL;
2622 sargs.keys.menu = &choose_setup_keymenu;
2623 sargs.keys.menu->how_many = 2;
2625 setbitmap(sargs.keys.bitmap);
2626 if(!printer)
2627 clrbitn(SETUP_PRINTER, sargs.keys.bitmap);
2629 if(!passwd)
2630 clrbitn(SETUP_PASSWD, sargs.keys.bitmap);
2632 if(!config)
2633 clrbitn(SETUP_CONFIG, sargs.keys.bitmap);
2635 if(!sig)
2636 clrbitn(SETUP_SIG, sargs.keys.bitmap);
2638 if(!dir)
2639 clrbitn(SETUP_DIRECTORY, sargs.keys.bitmap);
2641 if(!smime)
2642 clrbitn(SETUP_SMIME, sargs.keys.bitmap);
2644 if(exc)
2645 menu_init_binding(sargs.keys.menu, 'x', MC_EXCEPT, "X",
2646 N_("eXceptions"), SETUP_EXCEPT);
2647 else
2648 menu_init_binding(sargs.keys.menu, 'x', MC_NO_EXCEPT, "X",
2649 N_("eXceptions"), SETUP_EXCEPT);
2652 scrolltool(&sargs);
2654 ps->mangled_screen = 1;
2656 srv = (SRV_S *)sargs.proc.data.p;
2658 exceptions = srv ? srv->exc : 0;
2660 so_give(&store);
2662 if(sargs.bar.title) fs_give((void**)&sargs.bar.title);
2663 if(srv){
2664 ret = srv->cmd;
2665 fs_give((void **)&sargs.proc.data.p);
2667 else
2668 ret = 'e';
2670 return(ret | (exceptions ? EDIT_EXCEPTION : 0));
2674 /*----------------------------------------------------------------------
2676 Args: command -- command char to perform
2678 ----*/
2679 void
2680 do_setup_task(int command)
2682 char *err = NULL;
2683 int rtype;
2684 int edit_exceptions = 0;
2685 int do_lit_sig = 0;
2687 if(command & EDIT_EXCEPTION){
2688 edit_exceptions = 1;
2689 command &= ~EDIT_EXCEPTION;
2692 switch(command) {
2693 /*----- EDIT SIGNATURE -----*/
2694 case 's':
2695 if(ps_global->VAR_LITERAL_SIG)
2696 do_lit_sig = 1;
2697 else {
2698 char sig_path[MAXPATH+1];
2700 if(!signature_path(ps_global->VAR_SIGNATURE_FILE, sig_path, MAXPATH))
2701 do_lit_sig = 1;
2702 else if((!IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
2703 && can_access(sig_path, READ_ACCESS) == 0)
2704 ||(IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
2705 && (folder_exists(NULL, sig_path) & FEX_ISFILE)))
2706 do_lit_sig = 0;
2707 else if(!ps_global->vars[V_SIGNATURE_FILE].main_user_val.p
2708 && !ps_global->vars[V_SIGNATURE_FILE].cmdline_val.p
2709 && !ps_global->vars[V_SIGNATURE_FILE].fixed_val.p)
2710 do_lit_sig = 1;
2711 else
2712 do_lit_sig = 0;
2715 if(do_lit_sig){
2716 char *result = NULL;
2717 char **apval;
2718 EditWhich ew;
2719 int readonly = 0;
2721 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
2723 if(ps_global->restricted)
2724 readonly = 1;
2725 else switch(ew){
2726 case Main:
2727 readonly = ps_global->prc->readonly;
2728 break;
2729 case Post:
2730 readonly = ps_global->post_prc->readonly;
2731 break;
2732 default:
2733 break;
2736 if(readonly)
2737 err = cpystr(ps_global->restricted
2738 ? "Alpine demo can't change config file"
2739 : _("Config file not changeable"));
2741 if(!err){
2742 apval = APVAL(&ps_global->vars[V_LITERAL_SIG], ew);
2743 if(!apval)
2744 err = cpystr(_("Problem accessing configuration"));
2745 else{
2746 char *input;
2748 input = (char *)fs_get((strlen(*apval ? *apval : "")+1) *
2749 sizeof(char));
2750 input[0] = '\0';
2751 cstring_to_string(*apval, input);
2752 err = signature_edit_lit(input, &result,
2753 _("SIGNATURE EDITOR"),
2754 h_composer_sigedit);
2755 fs_give((void **)&input);
2759 if(!err){
2760 char *cstring_version;
2762 cstring_version = string_to_cstring(result);
2764 set_variable(V_LITERAL_SIG, cstring_version, 0, 0, ew);
2766 if(cstring_version)
2767 fs_give((void **)&cstring_version);
2770 if(result)
2771 fs_give((void **)&result);
2773 else
2774 err = signature_edit(ps_global->VAR_SIGNATURE_FILE,
2775 _("SIGNATURE EDITOR"));
2777 if(err){
2778 q_status_message(SM_ORDER, 3, 4, err);
2779 fs_give((void **)&err);
2782 ps_global->mangled_screen = 1;
2783 break;
2785 /*----- ADD ADDRESSBOOK ----*/
2786 case 'a':
2787 addr_book_config(ps_global, edit_exceptions);
2788 menu_index = ABOOK_MENU_ITEM;
2789 ps_global->mangled_screen = 1;
2790 break;
2792 #ifdef ENABLE_LDAP
2793 /*--- ADD DIRECTORY SERVER --*/
2794 case 'd':
2795 directory_config(ps_global, edit_exceptions);
2796 ps_global->mangled_screen = 1;
2797 break;
2798 #endif
2800 #ifdef SMIME
2801 /*--- S/MIME --*/
2802 case 'm':
2803 smime_config_screen(ps_global, edit_exceptions);
2804 ps_global->mangled_screen = 1;
2805 break;
2806 #endif
2808 /*----- CONFIGURE OPTIONS -----*/
2809 case 'c':
2810 option_screen(ps_global, edit_exceptions);
2811 ps_global->mangled_screen = 1;
2812 break;
2814 /*----- XOAUTH2 CLIENT CONFIGURATION -----*/
2815 case 'u':
2816 alpine_xoauth2_configuration(ps_global, edit_exceptions);
2817 ps_global->mangled_screen = 1;
2818 break;
2820 /*----- COLLECTION LIST -----*/
2821 case 'l':
2822 folder_config_screen(ps_global, edit_exceptions);
2823 ps_global->mangled_screen = 1;
2824 break;
2826 /*----- RULES -----*/
2827 case 'r':
2828 rtype = rule_setup_type(ps_global, RS_RULES | RS_INCFILTNOW,
2829 _("Type of rule setup : "));
2830 switch(rtype){
2831 case 'r':
2832 case 's':
2833 case 'i':
2834 case 'f':
2835 case 'o':
2836 case 'c':
2837 role_config_screen(ps_global, (rtype == 'r') ? ROLE_DO_ROLES :
2838 (rtype == 's') ? ROLE_DO_SCORES :
2839 (rtype == 'o') ? ROLE_DO_OTHER :
2840 (rtype == 'f') ? ROLE_DO_FILTER :
2841 (rtype == 'c') ? ROLE_DO_SRCH :
2842 ROLE_DO_INCOLS,
2843 edit_exceptions);
2844 break;
2846 case 'Z':
2847 q_status_message(SM_ORDER | SM_DING, 3, 5,
2848 _("Try turning on color with the Setup/Kolor command."));
2849 break;
2851 case 'n':
2852 role_process_filters();
2853 break;
2855 default:
2856 cmd_cancelled(NULL);
2857 break;
2860 ps_global->mangled_screen = 1;
2861 break;
2863 /*----- COLOR -----*/
2864 case 'k':
2865 color_config_screen(ps_global, edit_exceptions);
2866 ps_global->mangled_screen = 1;
2867 break;
2869 case 'z':
2870 convert_to_remote_config(ps_global, edit_exceptions);
2871 ps_global->mangled_screen = 1;
2872 break;
2874 /*----- EXIT -----*/
2875 case 'e':
2876 break;
2878 /*----- NEW PASSWORD -----*/
2879 case 'n':
2880 #ifdef PASSWD_PROG
2881 if(ps_global->restricted){
2882 q_status_message(SM_ORDER, 3, 5,
2883 "Password change unavailable in restricted demo version of Alpine.");
2884 }else {
2885 change_passwd();
2886 ClearScreen();
2887 ps_global->mangled_screen = 1;
2889 #else
2890 q_status_message(SM_ORDER, 0, 5,
2891 _("Password changing not configured for this version of Alpine."));
2892 display_message('x');
2893 #endif /* DOS */
2894 break;
2896 #if !defined(DOS)
2897 /*----- CHOOSE PRINTER ------*/
2898 case 'p':
2899 select_printer(ps_global, edit_exceptions);
2900 ps_global->mangled_screen = 1;
2901 break;
2902 #endif
2908 rule_setup_type(struct pine *ps, int flags, char *prompt)
2910 ESCKEY_S opts[9];
2911 int ekey_num = 0, deefault = 0;
2913 if(flags & RS_INCADDR){
2914 deefault = 'a';
2915 opts[ekey_num].ch = 'a';
2916 opts[ekey_num].rval = 'a';
2917 opts[ekey_num].name = "A";
2918 opts[ekey_num++].label = "Addrbook";
2921 if(flags & RS_RULES){
2923 if(F_OFF(F_DISABLE_ROLES_SETUP,ps)){ /* roles are allowed */
2924 if(deefault != 'a')
2925 deefault = 'r';
2927 opts[ekey_num].ch = 'r';
2928 opts[ekey_num].rval = 'r';
2929 opts[ekey_num].name = "R";
2930 opts[ekey_num++].label = "Roles";
2932 else if(deefault != 'a')
2933 deefault = 's';
2935 opts[ekey_num].ch = 's';
2936 opts[ekey_num].rval = 's';
2937 opts[ekey_num].name = "S";
2938 opts[ekey_num++].label = "SetScores";
2940 #ifndef _WINDOWS
2941 if(ps->color_style != COL_NONE && pico_hascolor()){
2942 #endif
2943 if(deefault != 'a')
2944 deefault = 'i';
2946 opts[ekey_num].ch = 'i';
2947 opts[ekey_num].rval = 'i';
2948 opts[ekey_num].name = "I";
2949 opts[ekey_num++].label = "Indexcolor";
2950 #ifndef _WINDOWS
2952 else{
2953 opts[ekey_num].ch = 'i';
2954 opts[ekey_num].rval = 'Z'; /* notice this rval! */
2955 opts[ekey_num].name = "I";
2956 opts[ekey_num++].label = "Indexcolor";
2958 #endif
2960 opts[ekey_num].ch = 'f';
2961 opts[ekey_num].rval = 'f';
2962 opts[ekey_num].name = "F";
2963 opts[ekey_num++].label = "Filters";
2965 opts[ekey_num].ch = 'o';
2966 opts[ekey_num].rval = 'o';
2967 opts[ekey_num].name = "O";
2968 opts[ekey_num++].label = "Other";
2970 opts[ekey_num].ch = 'c';
2971 opts[ekey_num].rval = 'c';
2972 opts[ekey_num].name = "C";
2973 opts[ekey_num++].label = "searCh";
2977 if(flags & RS_INCEXP){
2978 opts[ekey_num].ch = 'e';
2979 opts[ekey_num].rval = 'e';
2980 opts[ekey_num].name = "E";
2981 opts[ekey_num++].label = "Export";
2984 if(flags & RS_INCFILTNOW){
2985 opts[ekey_num].ch = 'n';
2986 opts[ekey_num].rval = 'n';
2987 opts[ekey_num].name = "N";
2988 opts[ekey_num++].label = "filterNow";
2991 opts[ekey_num].ch = -1;
2993 return(radio_buttons(prompt, -FOOTER_ROWS(ps), opts,
2994 deefault, 'x', NO_HELP, RB_NORM));
3000 * Process the command list, changing function key notation into
3001 * lexical equivalents.
3003 void
3004 process_init_cmds(struct pine *ps, char **list)
3006 char **p;
3007 int i = 0;
3008 int j;
3009 int lpm1;
3010 #define MAX_INIT_CMDS 500
3011 /* this is just a temporary stack array, the real one is allocated below */
3012 int i_cmds[MAX_INIT_CMDS];
3013 int fkeys = 0;
3014 int not_fkeys = 0;
3016 if(list){
3017 for(p = list; *p; p++){
3018 if(i >= MAX_INIT_CMDS){
3019 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3020 "Initial keystroke list too long at \"%s\"", *p);
3021 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
3022 break;
3026 /* regular character commands */
3027 if(strlen(*p) == 1){
3028 i_cmds[i++] = **p;
3029 not_fkeys++;
3032 /* special commands */
3033 else if(strucmp(*p, "SPACE") == 0)
3034 i_cmds[i++] = ' ';
3035 else if(strucmp(*p, "CR") == 0)
3036 i_cmds[i++] = '\n';
3037 else if(strucmp(*p, "TAB") == 0)
3038 i_cmds[i++] = '\t';
3039 else if(strucmp(*p, "UP") == 0)
3040 i_cmds[i++] = KEY_UP;
3041 else if(strucmp(*p, "DOWN") == 0)
3042 i_cmds[i++] = KEY_DOWN;
3043 else if(strucmp(*p, "LEFT") == 0)
3044 i_cmds[i++] = KEY_LEFT;
3045 else if(strucmp(*p, "RIGHT") == 0)
3046 i_cmds[i++] = KEY_RIGHT;
3048 /* control chars */
3049 else if(strlen(*p) == 2 && **p == '^')
3050 i_cmds[i++] = ctrl(*((*p)+1));
3052 /* function keys */
3053 else if(**p == 'F' || **p == 'f'){
3054 int v;
3056 fkeys++;
3057 v = atoi((*p)+1);
3058 if(v >= 1 && v <= 12)
3059 i_cmds[i++] = PF1 + v - 1;
3060 else
3061 i_cmds[i++] = KEY_JUNK;
3064 /* literal string */
3065 else if(**p == '"' && (*p)[lpm1 = strlen(*p) - 1] == '"'){
3066 if(lpm1 + i - 1 > MAX_INIT_CMDS){
3067 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3068 "Initial keystroke list too long, truncated at %s\n", *p);
3069 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
3070 break; /* Bail out of this loop! */
3071 } else
3072 for(j = 1; j < lpm1; j++)
3073 i_cmds[i++] = (*p)[j];
3075 else {
3076 snprintf(tmp_20k_buf,SIZEOF_20KBUF,
3077 "Bad initial keystroke \"%.500s\" (missing comma?)", *p);
3078 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
3079 break;
3085 * We don't handle the case where function keys are used to specify the
3086 * commands but some non-function key input is also required. For example,
3087 * you might want to jump to a specific message number and view it
3088 * on start up. To do that, you need to use character commands instead
3089 * of function key commands in the initial-keystroke-list.
3091 if(fkeys && not_fkeys){
3092 init_error(ps, SM_ORDER | SM_DING, 3, 5,
3093 "Mixed characters and function keys in \"initial-keystroke-list\", skipping.");
3094 i = 0;
3097 if(fkeys && !not_fkeys)
3098 F_TURN_ON(F_USE_FK,ps);
3099 if(!fkeys && not_fkeys)
3100 F_TURN_OFF(F_USE_FK,ps);
3102 if(i > 0){
3103 ps->initial_cmds = (int *)fs_get((i+1) * sizeof(int));
3104 ps->free_initial_cmds = ps->initial_cmds;
3105 for(j = 0; j < i; j++)
3106 ps->initial_cmds[j] = i_cmds[j];
3108 ps->initial_cmds[i] = 0;
3109 ps->in_init_seq = ps->save_in_init_seq = 1;
3114 UCS *
3115 user_wordseps(char **list)
3117 char **p;
3118 int i = 0;
3119 int j;
3120 #define MAX_SEPARATORS 500
3122 * This is just a temporary stack array, the real one is allocated below.
3123 * This is supposed to be way large enough.
3125 UCS seps[MAX_SEPARATORS+1];
3126 UCS *u;
3127 UCS *return_array = NULL;
3128 size_t l;
3130 seps[0] = '\0';
3132 if(list){
3133 for(p = list; *p; p++){
3134 if(i >= MAX_SEPARATORS){
3135 q_status_message(SM_ORDER | SM_DING, 3, 3,
3136 "Warning: composer-word-separators list is too long");
3137 break;
3140 u = utf8_to_ucs4_cpystr(*p);
3142 if(u){
3143 if(ucs4_strlen(u) == 1)
3144 seps[i++] = *u;
3145 else if(*u == '"' && u[l = ucs4_strlen(u) - 1] == '"'){
3146 if(l + i - 1 > MAX_SEPARATORS){
3147 q_status_message(SM_ORDER | SM_DING, 3, 3,
3148 "Warning: composer-word-separators list is too long");
3149 break; /* Bail out of this loop! */
3151 else{
3152 for(j = 1; j < l; j++)
3153 seps[i++] = u[j];
3156 else{
3157 l = ucs4_strlen(u);
3158 if(l + i > MAX_SEPARATORS){
3159 q_status_message(SM_ORDER | SM_DING, 3, 3,
3160 "Warning: composer-word-separators list is too long");
3161 break; /* Bail out of this loop! */
3163 else{
3164 for(j = 0; j < l; j++)
3165 seps[i++] = u[j];
3169 fs_give((void **) &u);
3174 seps[i] = '\0';
3176 if(i > 0)
3177 return_array = ucs4_cpystr(seps);
3179 return(return_array);
3184 * Make sure any errors during initialization get queued for display
3186 void
3187 queue_init_errors(struct pine *ps)
3189 int i;
3191 if(ps->init_errs){
3192 for(i = 0; (ps->init_errs)[i].message; i++){
3193 q_status_message((ps->init_errs)[i].flags,
3194 (ps->init_errs)[i].min_time,
3195 (ps->init_errs)[i].max_time,
3196 (ps->init_errs)[i].message);
3197 fs_give((void **)&(ps->init_errs)[i].message);
3200 fs_give((void **)&ps->init_errs);
3205 /*----------------------------------------------------------------------
3206 Quit pine if the user wants to
3208 Args: The usual pine structure
3210 Result: User is asked if she wants to quit, if yes then execute quit.
3212 Q U I T S C R E E N
3214 Not really a full screen. Just count up deletions and ask if we really
3215 want to quit.
3216 ----*/
3217 void
3218 quit_screen(struct pine *pine_state)
3220 int quit = 0;
3222 dprint((1, "\n\n ---- QUIT SCREEN ----\n"));
3224 if(F_ON(F_CHECK_MAIL_ONQUIT,ps_global)
3225 && pine_state->mail_stream != NULL
3226 && new_mail(1, VeryBadTime, NM_STATUS_MSG | NM_DEFER_SORT) > 0
3227 && (quit = want_to(_("Quit even though new mail just arrived"), 'y', 0,
3228 NO_HELP, WT_NORM | WT_DING)) != 'y'){
3229 refresh_sort(pine_state->mail_stream, pine_state->msgmap, SRT_VRB);
3230 pine_state->next_screen = pine_state->prev_screen;
3231 return;
3234 if(quit != 'y'
3235 && F_OFF(F_QUIT_WO_CONFIRM,pine_state)
3236 && want_to(_("Really quit Alpine"), 'y', 0, NO_HELP, WT_NORM) != 'y'){
3237 pine_state->next_screen = pine_state->prev_screen;
3238 return;
3241 goodnight_gracey(pine_state, 0);
3245 /*----------------------------------------------------------------------
3246 The nuts and bolts of actually cleaning up and exitting pine
3248 Args: ps -- the usual pine structure,
3249 exit_val -- what to tell our parent
3251 Result: This never returns
3253 ----*/
3254 void
3255 goodnight_gracey(struct pine *pine_state, int exit_val)
3257 int i, cnt_user_streams = 0;
3258 char *final_msg = NULL;
3259 char msg[MAX_SCREEN_COLS+1];
3260 char *pf = _("Alpine finished");
3261 MAILSTREAM *m;
3262 extern KBESC_T *kbesc;
3264 dprint((2, "goodnight_gracey:\n"));
3266 /* We want to do this here before we close up the streams */
3267 trim_remote_adrbks();
3269 for(i = 0; i < ps_global->s_pool.nstream; i++){
3270 m = ps_global->s_pool.streams[i];
3271 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR))
3272 cnt_user_streams++;
3275 /* clean up open streams */
3277 if(pine_state->mail_stream
3278 && sp_flagged(pine_state->mail_stream, SP_LOCKED)
3279 && sp_flagged(pine_state->mail_stream, SP_USERFLDR)){
3280 dprint((5, "goodnight_gracey: close current stream\n"));
3281 expunge_and_close(pine_state->mail_stream,
3282 (cnt_user_streams <= 1) ? &final_msg : NULL, EC_NONE);
3283 cnt_user_streams--;
3286 pine_state->mail_stream = NULL;
3287 pine_state->redrawer = (void (*)(void))NULL;
3289 dprint((5,
3290 "goodnight_gracey: close other stream pool streams\n"));
3291 for(i = 0; i < ps_global->s_pool.nstream; i++){
3292 m = ps_global->s_pool.streams[i];
3294 * fix global for functions that depend(ed) on it sort_folder.
3295 * Hopefully those will get phased out.
3297 ps_global->mail_stream = m;
3298 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
3299 && !sp_flagged(m, SP_INBOX)){
3300 sp_set_expunge_count(m, 0L);
3301 expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
3302 EC_NONE);
3303 cnt_user_streams--;
3307 for(i = 0; i < ps_global->s_pool.nstream; i++){
3308 m = ps_global->s_pool.streams[i];
3310 * fix global for functions that depend(ed) on it (sort_folder).
3311 * Hopefully those will get phased out.
3313 ps_global->mail_stream = m;
3314 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
3315 && sp_flagged(m, SP_INBOX)){
3316 dprint((5,
3317 "goodnight_gracey: close inbox stream stream\n"));
3318 sp_set_expunge_count(m, 0L);
3319 expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
3320 EC_NONE);
3321 cnt_user_streams--;
3325 #ifdef _WINDOWS
3326 if(ps_global->ttyo)
3327 (void)get_windsize(ps_global->ttyo);
3328 #endif
3330 dprint((7, "goodnight_gracey: close config files\n"));
3332 free_pinerc_strings(&pine_state);
3334 strncpy(msg, pf, sizeof(msg));
3335 msg[sizeof(msg)-1] = '\0';
3336 if(final_msg){
3337 strncat(msg, " -- ", sizeof(msg)-strlen(msg)-1);
3338 msg[sizeof(msg)-1] = '\0';
3339 strncat(msg, final_msg, sizeof(msg)-strlen(msg)-1);
3340 msg[sizeof(msg)-1] = '\0';
3341 fs_give((void **)&final_msg);
3344 dprint((7, "goodnight_gracey: sp_end\n"));
3345 ps_global->noshow_error = 1;
3346 sp_end();
3348 #ifdef SMIME
3349 smime_deinit();
3350 #endif
3352 /* after sp_end, which might call a filter */
3353 completely_done_with_adrbks();
3355 dprint((7, "goodnight_gracey: end_screen\n"));
3356 end_screen(msg, exit_val);
3357 dprint((7, "goodnight_gracey: end_titlebar\n"));
3358 end_titlebar();
3359 dprint((7, "goodnight_gracey: end_keymenu\n"));
3360 end_keymenu();
3362 dprint((7, "goodnight_gracey: end_keyboard\n"));
3363 end_keyboard(F_ON(F_USE_FK,pine_state));
3364 dprint((7, "goodnight_gracey: end_ttydriver\n"));
3365 end_tty_driver(pine_state);
3366 #if !defined(DOS) && !defined(OS2)
3367 kbdestroy(kbesc);
3368 #if !defined(LEAVEOUTFIFO)
3369 close_newmailfifo();
3370 #endif
3371 #endif
3372 end_signals(0);
3373 if(filter_data_file(0))
3374 our_unlink(filter_data_file(0));
3376 imap_flush_passwd_cache(TRUE);
3377 free_newsgrp_cache();
3378 mailcap_free();
3379 close_every_pattern();
3380 free_extra_hdrs();
3381 free_contexts(&ps_global->context_list);
3382 free_charsetchecker();
3383 dprint((7, "goodnight_gracey: free more memory\n"));
3384 #ifdef ENABLE_LDAP
3385 free_saved_query_parameters();
3386 #endif
3388 html_dir_clean(1); /* force remove of remaining files */
3389 free_pine_struct(&pine_state);
3391 free_histlist();
3393 free_alpine_module_globals(); /* should we have module globals? */
3394 free_pith_module_globals();
3395 free_pico_module_globals();
3396 free_c_client_module_globals();
3398 #ifdef DEBUG
3399 if(debugfile){
3400 if(debug >= 2)
3401 fputs("goodnight_gracey finished\n", debugfile);
3403 fclose(debugfile);
3405 #endif
3407 exit(exit_val);
3411 /*----------------------------------------------------------------------
3412 Call back for c-client to feed us back the progress of network reads
3414 Input:
3416 Result:
3417 ----*/
3418 void
3419 pine_read_progress(GETS_DATA *md, long unsigned int count)
3421 gets_bytes += count; /* update counter */
3425 /*----------------------------------------------------------------------
3426 Function to fish the current byte count from a c-client fetch.
3428 Input: reset -- flag telling us to reset the count
3430 Result: Returns the number of bytes read by the c-client so far
3431 ----*/
3432 unsigned long
3433 pine_gets_bytes(int reset)
3435 if(reset)
3436 gets_bytes = 0L;
3438 return(gets_bytes);
3442 /*----------------------------------------------------------------------
3443 Panic pine - call on detected programmatic errors to exit pine
3445 Args: message -- message to record in debug file and to be printed for user
3447 Result: The various tty modes are restored
3448 If debugging is active a core dump will be generated
3449 Exits Alpine
3451 This is also called from imap routines and fs_get and fs_resize.
3452 ----*/
3453 void
3454 alpine_panic(char *message)
3456 char buf[256];
3458 /* global variable in .../pico/edef.h */
3459 in_panic = 1;
3461 if(ps_global->ttyo){
3462 end_screen(NULL, -1);
3463 end_keyboard(ps_global != NULL ? F_ON(F_USE_FK,ps_global) : 0);
3464 end_tty_driver(ps_global);
3465 end_signals(1);
3467 if(filter_data_file(0))
3468 our_unlink(filter_data_file(0));
3470 dprint((1, "\n===========================================\n\n"));
3471 dprint((1, " Alpine Panic: %s\n\n", message ? message : "?"));
3472 dprint((1, "===========================================\n\n"));
3474 /* intercept c-client "free storage" errors */
3475 if(strstr(message, "free storage"))
3476 snprintf(buf, sizeof(buf), _("No more available memory.\nAlpine Exiting"));
3477 else
3478 snprintf(buf, sizeof(buf), _("Problem detected: \"%s\".\nAlpine Exiting."), message);
3480 buf[sizeof(buf)-1] = '\0';
3482 #ifdef _WINDOWS
3483 /* Put up a message box. */
3484 mswin_messagebox (buf, 1);
3485 #else
3486 fprintf(stderr, "\n\n%s\n", buf);
3487 #endif
3489 #ifdef DEBUG
3490 if(debugfile){
3491 save_debug_on_crash(debugfile, recent_keystroke);
3494 coredump(); /*--- If we're debugging get a core dump --*/
3495 #endif
3497 exit(-1);
3498 fatal("ffo"); /* BUG -- hack to get fatal out of library in right order*/
3503 * panicking - function to test whether or not we're exiting under stress.
3507 panicking(void)
3509 return(in_panic);
3513 /*----------------------------------------------------------------------
3514 exceptional_exit - called to exit under unusual conditions (with no core)
3516 Args: message -- message to record in debug file and to be printed for user
3517 ev -- exit value
3519 ----*/
3520 void
3521 exceptional_exit(char *message, int ev)
3523 fprintf(stderr, "%s\n", message);
3524 exit(ev);
3529 * PicoText Storage Object Support Routines
3532 STORE_S *
3533 pine_pico_get(void)
3535 return((STORE_S *)pico_get());
3539 pine_pico_give(STORE_S **sop)
3541 pico_give((void *)sop);
3542 return(1);
3546 pine_pico_writec(int c, STORE_S *so)
3548 unsigned char ch = (unsigned char) c;
3550 return(pico_writec(so->txt, ch, PICOREADC_NONE));
3554 pine_pico_writec_noucs(int c, STORE_S *so)
3556 unsigned char ch = (unsigned char) c;
3558 return(pico_writec(so->txt, ch, PICOREADC_NOUCS));
3562 pine_pico_readc(unsigned char *c, STORE_S *so)
3564 return(pico_readc(so->txt, c, PICOREADC_NONE));
3568 pine_pico_readc_noucs(unsigned char *c, STORE_S *so)
3570 return(pico_readc(so->txt, c, PICOREADC_NOUCS));
3574 pine_pico_puts(STORE_S *so, char *s)
3576 return(pico_puts(so->txt, s, PICOREADC_NONE));
3580 pine_pico_puts_noucs(STORE_S *so, char *s)
3582 return(pico_puts(so->txt, s, PICOREADC_NOUCS));
3586 pine_pico_seek(STORE_S *so, long pos, int orig)
3588 return(pico_seek((void *)so, pos, orig));
3593 remote_pinerc_failure(void)
3595 #ifdef _WINDOWS
3596 if(ps_global->install_flag) /* just exit silently */
3597 exit(0);
3598 #endif /* _WINDOWS */
3600 if(ps_global->exit_if_no_pinerc){
3601 exceptional_exit("Exiting because -bail option is set and config file not readable.", -1);
3604 if(want_to("Trouble reading remote configuration! Continue anyway ",
3605 'n', 'n', NO_HELP, WT_FLUSH_IN) != 'y'){
3606 return(0);
3609 return(1);
3613 void
3614 dump_supported_options(void)
3616 char **config;
3618 config = get_supported_options();
3619 if(config){
3620 display_args_err(NULL, config, 0);
3621 free_list_array(&config);
3626 /*----------------------------------------------------------------------
3627 Check pruned-folders for validity, making sure they are in the
3628 same context as sent-mail.
3630 ----*/
3632 prune_folders_ok(void)
3634 char **p;
3636 for(p = ps_global->VAR_PRUNED_FOLDERS; p && *p && **p; p++)
3637 if(!context_isambig(*p))
3638 return(0);
3640 return(1);
3643 void
3644 free_alpine_module_globals(void)
3646 #ifdef LOCAL_PASSWD_CACHE
3647 free_passfile_cache();
3648 #endif
3649 free_message_queue();
3650 free_titlebar_globals();
3653 #ifdef WIN32
3654 char *
3655 pine_user_callback()
3657 if(ps_global->VAR_USER_ID && ps_global->VAR_USER_ID[0]){
3658 return(ps_global->VAR_USER_ID);
3660 else{
3661 /* SHOULD PROMPT HERE! */
3662 return(NULL);
3665 #endif
3667 #ifdef _WINDOWS
3669 * windows callback to get/set function keys mode state
3672 fkey_mode_callback(set, args)
3673 int set;
3674 long args;
3676 return(F_ON(F_USE_FK, ps_global) != 0);
3680 void
3681 imap_telemetry_on()
3683 if(ps_global->mail_stream)
3684 mail_debug(ps_global->mail_stream);
3688 void
3689 imap_telemetry_off()
3691 if(ps_global->mail_stream)
3692 mail_nodebug(ps_global->mail_stream);
3696 char *
3697 pcpine_help_main(title)
3698 char *title;
3700 if(title)
3701 strncpy(title, _("PC-Alpine MAIN MENU Help"), 256);
3703 return(pcpine_help(main_menu_tx));
3708 pcpine_main_cursor(col, row)
3709 int col;
3710 long row;
3712 unsigned ndmi;
3714 if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global)))
3715 ndmi = (row+1 - HEADER_ROWS(ps_global) - (MNSKIP(ps_global)+1))/(MNSKIP(ps_global)+1);
3717 if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global))
3718 && !(MNSKIP(ps_global) && (row+1) & 0x01)
3719 && ndmi <= MAX_MENU_ITEM
3720 && FOOTER_ROWS(ps_global) + (ndmi+1)*(MNSKIP(ps_global)+1)
3721 + MNSKIP(ps_global) + FOOTER_ROWS(ps_global) <= ps_global->ttyo->screen_rows)
3722 return(MSWIN_CURSOR_HAND);
3723 else
3724 return(MSWIN_CURSOR_ARROW);
3726 #endif /* _WINDOWS */