* Fix some compiler warnings for bad casting in some functions in the file
[alpine.git] / alpine / alpine.c
blob1b96d7b3a93b0992119d46ac5fc19e01347990e9
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "headers.h"
17 #include "../pith/newmail.h"
18 #include "../pith/init.h"
19 #include "../pith/sort.h"
20 #include "../pith/options.h"
21 #include "../pith/list.h"
22 #include "../pith/conf.h"
23 #include "../pith/body.h"
25 #include "osdep/debuging.h"
26 #include "osdep/termout.gen.h"
27 #include "osdep/chnge_pw.h"
29 #include "alpine.h"
30 #include "mailindx.h"
31 #include "mailcmd.h"
32 #include "addrbook.h"
33 #include "reply.h"
34 #include "arg.h"
35 #include "keymenu.h"
36 #include "status.h"
37 #include "context.h"
38 #include "mailview.h"
39 #include "imap.h"
40 #include "xoauth2conf.h"
41 #include "radio.h"
42 #include "folder.h"
43 #include "send.h"
44 #include "help.h"
45 #include "titlebar.h"
46 #include "takeaddr.h"
47 #include "dispfilt.h"
48 #include "init.h"
49 #include "remote.h"
50 #include "pattern.h"
51 #include "setup.h"
52 #include "newuser.h"
53 #include "adrbkcmd.h"
54 #include "signal.h"
55 #include "kblock.h"
56 #include "ldapconf.h"
57 #include "roleconf.h"
58 #include "colorconf.h"
59 #include "print.h"
60 #include "after.h"
61 #include "smime.h"
62 #include "newmail.h"
63 #ifndef _WINDOWS
64 #include "../pico/osdep/raw.h" /* for STD*_FD */
65 #endif
68 #define PIPED_FD 5 /* Some innocuous desc */
71 /* look for my_timer_period in pico directory for an explanation */
72 int my_timer_period = ((IDLE_TIMEOUT + 1)*1000);
74 /* byte count used by our gets routine to keep track */
75 static unsigned long gets_bytes;
79 * Internal prototypes
81 void convert_args_to_utf8(struct pine *, ARGDATA_S *);
82 void preopen_stayopen_folders(void);
83 int read_stdin_char(unsigned char *);
84 void main_redrawer(void);
85 void show_main_screen(struct pine *, int, OtherMenu, struct key_menu *, int, Pos *);
86 void do_menu(int, Pos *, struct key_menu *);
87 int choose_setup_cmd(int, MSGNO_S *, SCROLL_S *);
88 int setup_menu(struct pine *);
89 void do_setup_task(int);
90 void queue_init_errors(struct pine *);
91 void process_init_cmds(struct pine *, char **);
92 void goodnight_gracey(struct pine *, int);
93 void pine_read_progress(GETS_DATA *, unsigned long);
94 int remote_pinerc_failure(void);
95 void dump_supported_options(void);
96 int prune_folders_ok(void);
97 void free_alpine_module_globals(void);
98 #ifdef WIN32
99 char *pine_user_callback(void);
100 #endif
101 #ifdef _WINDOWS
102 int fkey_mode_callback(int, long);
103 void imap_telemetry_on(void);
104 void imap_telemetry_off(void);
105 char *pcpine_help_main(char *);
106 int pcpine_main_cursor(int, long);
107 #define main app_main
108 #endif
111 typedef struct setup_return_val {
112 int cmd;
113 int exc;
114 }SRV_S;
118 * strlen of longest label from keymenu, of labels corresponding to
119 * commands in the middle of the screen. 9 is length of ListFldrs
121 #define LONGEST_LABEL 9 /* length of longest label from keymenu */
123 #define EDIT_EXCEPTION (0x100)
126 static int in_panic = 0;
129 /*----------------------------------------------------------------------
130 main routine -- entry point
132 Args: argv, argc -- The command line arguments
135 Initialize pine, parse arguments and so on
137 If there is a user address on the command line go into send mode and exit,
138 otherwise loop executing the various screens in Alpine.
140 NOTE: The Windows port def's this to "app_main"
141 ----*/
144 main(int argc, char **argv)
146 ARGDATA_S args;
147 int rv;
148 long rvl;
149 struct pine *pine_state;
150 gf_i_t stdin_getc = NULL;
151 char *args_for_debug = NULL, *init_pinerc_debugging = NULL;
153 /*----------------------------------------------------------------------
154 Set up buffering and some data structures
155 ----------------------------------------------------------------------*/
157 pine_state = new_pine_struct();
158 ps_global = pine_state;
161 * fill in optional pith-offered behavior hooks
163 pith_opt_read_msg_prompt = read_msg_prompt;
164 pith_opt_paint_index_hline = paint_index_hline;
165 pith_opt_rfc2369_editorial = rfc2369_editorial;
166 pith_opt_condense_thread_cue = condensed_thread_cue;
167 pith_opt_truncate_sfstr = truncate_subj_and_from_strings;
168 pith_opt_save_and_restore = save_and_restore;
169 pith_opt_newmail_announce = newmail_status_message;
170 pith_opt_newmail_check_cue = newmail_check_cue;
171 pith_opt_checkpoint_cue = newmail_check_point_cue;
172 pith_opt_icon_text = icon_text;
173 pith_opt_rd_metadata_name = rd_metadata_name;
174 pith_opt_remote_pinerc_failure = remote_pinerc_failure;
175 pith_opt_reopen_folder = ask_mailbox_reopen;
176 pith_opt_expunge_prompt = expunge_prompt;
177 pith_opt_begin_closing = expunge_and_close_begins;
178 pith_opt_replyto_prompt = reply_using_replyto_query;
179 pith_opt_reply_to_all_prompt = reply_to_all_query;
180 pith_opt_save_create_prompt = create_for_save_prompt;
181 pith_opt_daemon_confirm = confirm_daemon_send;
182 pith_opt_save_size_changed_prompt = save_size_changed_prompt;
183 pith_opt_save_index_state = setup_index_state;
184 pith_opt_filter_pattern_cmd = pattern_filter_command;
185 pith_opt_get_signature_file = get_signature_file;
186 pith_opt_pretty_var_name = pretty_var_name;
187 pith_opt_pretty_feature_name = pretty_feature_name;
188 pith_opt_closing_stream = titlebar_stream_closing;
189 pith_opt_current_expunged = mm_expunged_current;
190 #ifdef SMIME
191 pith_opt_smime_get_passphrase = smime_get_passphrase;
192 pith_smime_import_certificate = smime_import_certificate;
193 pith_smime_enter_password = alpine_get_password;
194 pith_smime_confirm_save = alpine_smime_confirm_save;
195 #endif
196 #ifdef ENABLE_LDAP
197 pith_opt_save_ldap_entry = save_ldap_entry;
198 #endif
200 status_message_lock_init();
201 inverse_itokens();
203 #if HAVE_SRANDOM
205 * Seed the random number generator with the date & pid. Random
206 * numbers are used for new mail notification and bug report id's
208 srandom(getpid() + time(0));
209 #endif
211 /* need home directory early */
212 get_user_info(&ps_global->ui);
214 if(!(pine_state->home_dir = our_getenv("HOME")))
215 pine_state->home_dir = cpystr(ps_global->ui.homedir);
217 #ifdef _WINDOWS
219 char *p;
221 /* normalize path delimiters */
222 for(p = pine_state->home_dir; p = strchr(p, '/'); p++)
223 *p='\\';
225 #endif /* _WINDOWS */
227 #ifdef DEBUG
228 { size_t len = 0;
229 int i;
230 char *p;
231 char *no_args = " <no args>";
233 for(i = 0; i < argc; i++)
234 len += (strlen(argv[i] ? argv[i] : "")+3);
236 if(argc == 1)
237 len += strlen(no_args);
239 p = args_for_debug = (char *)fs_get((len+2) * sizeof(char));
240 *p++ = '\n';
241 *p = '\0';
243 for(i = 0; i < argc; i++){
244 snprintf(p, len+2-(p-args_for_debug), "%s\"%s\"", i ? " " : "", argv[i] ? argv[i] : "");
245 args_for_debug[len+2-1] = '\0';
246 p += strlen(p);
249 if(argc == 1){
250 strncat(args_for_debug, no_args, len+2-strlen(args_for_debug)-1);
251 args_for_debug[len+2-1] = '\0';
254 #endif
256 /*----------------------------------------------------------------------
257 Parse arguments and initialize debugging
258 ----------------------------------------------------------------------*/
259 pine_args(pine_state, argc, argv, &args);
261 #ifndef _WINDOWS
262 if(!isatty(0)){
264 * monkey with descriptors so our normal tty i/o routines don't
265 * choke...
267 dup2(STDIN_FD, PIPED_FD); /* redirected stdin to new desc */
268 dup2(STDERR_FD, STDIN_FD); /* rebind stdin to the tty */
269 stdin_getc = read_stdin_char;
270 if(stdin_getc){
271 if(args.action == aaURL){
272 display_args_err(
273 "Cannot read stdin when using -url\nFor mailto URLs, use \'body=\' instead",
274 NULL, 1);
275 args_help();
276 exit(-1);
277 } else if (args.action == aaFolder){
278 display_args_err("Cannot take input from pipe when opening a folder", NULL, 1);
279 args_help();
280 exit(-1);
285 #else /* _WINDOWS */
287 * We now have enough information to do some of the basic registry settings.
289 if(ps_global->update_registry != UREG_NEVER_SET){
290 mswin_reg(MSWR_OP_SET
291 | ((ps_global->update_registry == UREG_ALWAYS_SET)
292 ? MSWR_OP_FORCE : 0),
293 MSWR_PINE_DIR, ps_global->pine_dir, (size_t)NULL);
294 mswin_reg(MSWR_OP_SET
295 | ((ps_global->update_registry == UREG_ALWAYS_SET)
296 ? MSWR_OP_FORCE : 0),
297 MSWR_PINE_EXE, ps_global->pine_name, (size_t)NULL);
300 #endif /* _WINDOWS */
302 if(ps_global->convert_sigs &&
303 (!ps_global->pinerc || !ps_global->pinerc[0])){
304 fprintf(stderr, "Use -p <pinerc> with -convert_sigs\n");
305 exit(-1);
308 /* Windows has its own functions to determine width of a character
309 * in the screen, so this is not necessary to do in Window, and
310 * using pith_ucs4width does not produce the correct result
312 #if !defined(_WINDOWS) && defined(LC_CTYPE)
313 { char *s;
314 if((s = setlocale(LC_CTYPE, "")) != NULL
315 && strlen(s) >= 5
316 && !strucmp(s+strlen(s)-5, "UTF-8"))
317 mail_parameters(NULL, SET_UCS4WIDTH, (void *) pith_ucs4width);
319 #endif /* !_WINDOWS && LC_CTYPE */
320 mail_parameters(NULL, SET_QUOTA, (void *) pine_parse_quota);
321 /* set some default timeouts in case pinerc is remote */
322 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)30);
323 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)15);
324 mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
325 /* could be TO_BAIL_THRESHOLD, 15 seems more appropriate for now */
326 pine_state->tcp_query_timeout = 15;
328 mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
329 mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
330 mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
331 mail_parameters(NULL, SET_FREEBODYSPAREP, (void *) free_body_sparep);
332 mail_parameters(NULL, SET_ERASEPASSWORD, (void *) pine_delete_pwd);
333 mail_parameters(NULL, SET_OA2CLIENTGETACCESSCODE, (void *) oauth2_get_access_code);
334 mail_parameters(NULL, SET_OA2CLIENTINFO, (void *) oauth2_get_client_info);
335 mail_parameters(NULL, SET_OA2DEVICEINFO, (void *) oauth2_set_device_info);
337 init_pinerc(pine_state, &init_pinerc_debugging);
339 #ifdef DEBUG
340 /* Since this is specific debugging we don't mind if the
341 ifdef is the type of system.
343 #if defined(HAVE_SMALLOC) || defined(NXT)
344 if(ps_global->debug_malloc)
345 malloc_debug(ps_global->debug_malloc);
346 #endif
347 #ifdef CSRIMALLOC
348 if(ps_global->debug_malloc)
349 mal_debug(ps_global->debug_malloc);
350 #endif
352 if(!ps_global->convert_sigs
353 #ifdef _WINDOWS
354 && !ps_global->install_flag
355 #endif /* _WINDOWS */
357 init_debug();
359 if(args_for_debug){
360 dprint((0, " %s (PID=%ld)\n\n", args_for_debug,
361 (long) getpid()));
362 fs_give((void **)&args_for_debug);
366 char *env_to_free;
367 if((env_to_free = our_getenv("HOME")) != NULL){
368 dprint((2, "Setting home dir from $HOME: \"%s\"\n",
369 env_to_free));
370 fs_give((void **)&env_to_free);
372 else{
373 dprint((2, "Setting home dir: \"%s\"\n",
374 pine_state->home_dir ? pine_state->home_dir : "<?>"));
378 /* Watch out. Sensitive information in debug file. */
379 if(ps_global->debug_imap > 4)
380 mail_parameters(NULL, SET_DEBUGSENSITIVE, (void *) TRUE);
382 #ifndef DEBUGJOURNAL
383 if(ps_global->debug_tcp)
384 #endif
385 mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
387 #ifndef DEBUGJOURNAL
388 if(ps_global->debug_http)
389 #endif
390 mail_parameters(NULL, SET_HTTPDEBUG, (void *) TRUE);
392 #ifdef _WINDOWS
393 mswin_setdebug(debug, debugfile);
394 mswin_setdebugoncallback (imap_telemetry_on);
395 mswin_setdebugoffcallback (imap_telemetry_off);
396 mswin_enableimaptelemetry(ps_global->debug_imap != 0);
397 #endif
398 #endif /* DEBUG */
400 #ifdef _WINDOWS
401 mswin_setsortcallback(index_sort_callback);
402 mswin_setflagcallback(flag_callback);
403 mswin_sethdrmodecallback(header_mode_callback);
404 mswin_setselectedcallback(any_selected_callback);
405 mswin_setzoomodecallback(zoom_mode_callback);
406 mswin_setfkeymodecallback(fkey_mode_callback);
407 #endif
409 /*------- Set up c-client drivers -------*/
410 #include "../c-client/linkage.c"
412 /*------- ... then tune the drivers just installed -------*/
413 #ifdef _WINDOWS
414 if(_tgetenv(TEXT("HOME")))
415 mail_parameters(NULL, SET_HOMEDIR, (void *) pine_state->home_dir);
417 mail_parameters(NULL, SET_USERPROMPT, (void *) pine_user_callback);
420 * Sniff the environment for timezone offset. We need to do this
421 * here since Windows needs help figuring out UTC, and will adjust
422 * what time() returns based on TZ. THIS WILL SCREW US because
423 * we use time() differences to manage status messages. So, if
424 * rfc822_date, which calls localtime() and thus needs tzset(),
425 * is called while a status message is displayed, it's possible
426 * for time() to return a time *before* what we remember as the
427 * time we put the status message on the display. Sheesh.
429 tzset();
430 #else /* !_WINDOWS */
432 * We used to let c-client do this for us automatically, but it declines
433 * to do so for root. This forces c-client to establish an environment,
434 * even if the uid is 0.
436 env_init(ps_global->ui.login, ps_global->ui.homedir);
439 * Install callback to let us know the progress of network reads...
441 (void) mail_parameters(NULL, SET_READPROGRESS, (void *)pine_read_progress);
442 #endif /* !_WINDOWS */
445 * Install callback to handle certificate validation failures,
446 * allowing the user to continue if they wish.
448 mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) pine_sslcertquery);
449 mail_parameters(NULL, SET_SSLFAILURE, (void *) pine_sslfailure);
451 if(init_pinerc_debugging){
452 dprint((2, "%s", init_pinerc_debugging));
453 fs_give((void **)&init_pinerc_debugging);
457 * Initial allocation of array of stream pool pointers.
458 * We do this before init_vars so that we can re-use streams used for
459 * remote config files. These sizes may get changed later.
461 ps_global->s_pool.max_remstream = 2;
462 dprint((9,
463 "Setting initial max_remstream to %d for remote config re-use\n",
464 ps_global->s_pool.max_remstream));
466 init_vars(pine_state, process_init_cmds);
468 #if !defined(_WINDOWS) || defined(WINDOWS_UNIXSSL_CERTS)
469 set_system_certs_path(pine_state);
470 set_system_certs_container(pine_state);
471 set_user_certs_path(pine_state);
472 set_user_certs_container(pine_state);
473 mail_parameters(NULL, SET_SSLCIPHERS, (void *) pine_state->VAR_SSLCIPHERS);
474 #endif
476 #ifdef SMIME
477 if(F_ON(F_DONT_DO_SMIME, ps_global))
478 smime_deinit();
479 #endif /* SMIME */
481 #ifdef ENABLE_NLS
483 * LC_CTYPE is already set from the set_collation call above.
485 * We can't use gettext calls before we do this stuff so it doesn't
486 * help to translate strings that come before this in the program.
487 * Maybe we could rearrange things to accommodate that.
489 setlocale(LC_MESSAGES, "");
490 bindtextdomain(PACKAGE, LOCALEDIR);
491 bind_textdomain_codeset(PACKAGE, "UTF-8");
492 textdomain(PACKAGE);
493 #endif /* ENABLE_NLS */
495 convert_args_to_utf8(pine_state, &args);
497 if(args.action == aaFolder){
498 pine_state->beginning_of_month = first_run_of_month();
499 pine_state->beginning_of_year = first_run_of_year();
502 /* Set up optional for user-defined display filtering */
503 pine_state->tools.display_filter = dfilter;
504 pine_state->tools.display_filter_trigger = dfilter_trigger;
506 #ifdef _WINDOWS
507 if(ps_global->install_flag){
508 init_install_get_vars();
510 if(ps_global->prc)
511 free_pinerc_s(&ps_global->prc);
513 exit(0);
515 #endif
517 if(ps_global->convert_sigs){
518 if(convert_sigs_to_literal(ps_global, 0) == -1){
519 /* TRANSLATORS: sigs refers to signatures, which the user was trying to convert */
520 fprintf(stderr, _("trouble converting sigs\n"));
521 exit(-1);
524 if(ps_global->prc){
525 if(ps_global->prc->outstanding_pinerc_changes)
526 write_pinerc(ps_global, Main, WRP_NONE);
528 free_pinerc_s(&pine_state->prc);
531 exit(0);
535 * Set up a c-client read timeout and timeout handler. In general,
536 * it shouldn't happen, but a server crash or dead link can cause
537 * pine to appear wedged if we don't set this up...
539 rv = 30;
540 if(pine_state->VAR_TCPOPENTIMEO)
541 (void)SVAR_TCP_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
542 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)rv);
544 rv = 15;
545 if(pine_state->VAR_TCPREADWARNTIMEO)
546 (void)SVAR_TCP_READWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
547 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)rv);
549 rv = 0;
550 if(pine_state->VAR_TCPWRITEWARNTIMEO){
551 if(!SVAR_TCP_WRITEWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
552 if(rv == 0 || rv > 4) /* making sure */
553 mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long)rv);
556 mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
558 rv = 15;
559 if(pine_state->VAR_RSHOPENTIMEO){
560 if(!SVAR_RSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
561 if(rv == 0 || rv > 4) /* making sure */
562 mail_parameters(NULL, SET_RSHTIMEOUT, (void *)(long)rv);
565 rv = 15;
566 if(pine_state->VAR_SSHOPENTIMEO){
567 if(!SVAR_SSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
568 if(rv == 0 || rv > 4) /* making sure */
569 mail_parameters(NULL, SET_SSHTIMEOUT, (void *)(long)rv);
572 rvl = 60L;
573 if(pine_state->VAR_MAILDROPCHECK){
574 if(!SVAR_MAILDCHK(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF)){
575 if(rvl == 0L)
576 rvl = (60L * 60L * 24L * 100L); /* 100 days */
578 if(rvl >= 60L) /* making sure */
579 mail_parameters(NULL, SET_SNARFINTERVAL, (void *) rvl);
584 * Lookups of long login names which don't exist are very slow in aix.
585 * This would normally get set in system-wide config if not needed.
587 if(F_ON(F_DISABLE_SHARED_NAMESPACES, ps_global))
588 mail_parameters(NULL, SET_DISABLEAUTOSHAREDNS, (void *) TRUE);
590 if(F_ON(F_HIDE_NNTP_PATH, ps_global))
591 mail_parameters(NULL, SET_NNTPHIDEPATH, (void *) TRUE);
593 if(F_ON(F_MAILDROPS_PRESERVE_STATE, ps_global))
594 mail_parameters(NULL, SET_SNARFPRESERVE, (void *) TRUE);
596 rvl = 0L;
597 if(pine_state->VAR_NNTPRANGE){
598 if(!SVAR_NNTPRANGE(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF))
599 if(rvl > 0L)
600 mail_parameters(NULL, SET_NNTPRANGE, (void *) rvl);
604 * Tell c-client not to be so aggressive about uid mappings
606 mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20);
609 * Setup referral handling
611 mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral);
612 mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy);
615 * Setup multiple newsrc transition
617 mail_parameters(NULL, SET_NEWSRCQUERY, (void *) pine_newsrcquery);
620 * Disable some drivers if requested.
622 if(ps_global->VAR_DISABLE_DRIVERS &&
623 ps_global->VAR_DISABLE_DRIVERS[0] &&
624 ps_global->VAR_DISABLE_DRIVERS[0][0]){
625 char **t;
627 for(t = ps_global->VAR_DISABLE_DRIVERS; t[0] && t[0][0]; t++)
628 if(mail_parameters(NULL, DISABLE_DRIVER, (void *)(*t))){
629 dprint((2, "Disabled mail driver \"%s\"\n", *t));
631 else{
632 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
633 _("Failed to disable mail driver \"%s\": name not found"),
634 *t);
635 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
636 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
641 * Disable some authenticators if requested.
643 if(ps_global->VAR_DISABLE_AUTHS &&
644 ps_global->VAR_DISABLE_AUTHS[0] &&
645 ps_global->VAR_DISABLE_AUTHS[0][0]){
646 char **t;
648 for(t = ps_global->VAR_DISABLE_AUTHS; t[0] && t[0][0]; t++)
649 if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *)(*t))){
650 dprint((2,"Disabled SASL authenticator \"%s\"\n", *t));
652 else{
653 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
654 _("Failed to disable SASL authenticator \"%s\": name not found"),
655 *t);
656 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
657 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
661 #ifdef DF_ENCRYPTION_RANGE
662 if(ps_global->VAR_ENCRYPTION_RANGE
663 && ps_global->VAR_ENCRYPTION_RANGE[0]){
664 char *min_s, *max_s, *s;
665 int min_v, max_v;
667 if((s = strchr(ps_global->VAR_ENCRYPTION_RANGE, ',')) == NULL){
668 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
669 _("Bad encryption range: \"%s\": resetting to default"),
670 ps_global->VAR_ENCRYPTION_RANGE);
671 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
672 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
673 fs_give((void **) &ps_global->VAR_ENCRYPTION_RANGE);
674 ps_global->VAR_ENCRYPTION_RANGE = cpystr(DF_ENCRYPTION_RANGE);
675 s = strchr(ps_global->VAR_ENCRYPTION_RANGE, ','); /* try again */
678 if(s == NULL){
679 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
680 _("Bad default encryption range: \"%s\""),
681 ps_global->VAR_ENCRYPTION_RANGE);
682 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
683 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
685 else {
686 *s = ' ';
687 get_pair(ps_global->VAR_ENCRYPTION_RANGE, &min_s, &max_s, 1, 0);
688 *s = ',';
690 min_v = pith_ssl_encryption_version(min_s);
691 max_v = pith_ssl_encryption_version(max_s);
693 if(min_s != NULL) fs_give((void **) &min_s);
694 if(max_s != NULL) fs_give((void **) &max_s);
696 if(min_v < 0 || max_v < 0){
697 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
698 _("Bad encryption range: \"%s\": resetting to default"),
699 ps_global->VAR_ENCRYPTION_RANGE);
700 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
701 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
702 min_v = max_v = 0;
705 if(min_v > max_v){
706 int bubble;
707 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
708 _("Minimum encryption protocol (%s) bigger than maximum value (%s). Reversing..."),
709 min_s, max_s);
710 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
711 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
712 bubble = min_v;
713 min_v = max_v;
714 max_v = bubble;
717 if(max_v > 0 && max_v < (long) pith_ssl_encryption_version("tls1")){
718 strncpy(tmp_20k_buf, _("Security alert: SSL maximum encryption version was set to SSLv3."), SIZEOF_20KBUF);
719 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
720 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
723 mail_parameters(NULL, SET_ENCRYPTION_RANGE_MIN, (void *) &min_v);
724 mail_parameters(NULL, SET_ENCRYPTION_RANGE_MAX, (void *) &max_v);
727 #endif /* DF_ENCRYPTION_RANGE */
730 * setup alternative authentication driver preference for IMAP opens
732 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
733 mail_parameters(NULL, SET_IMAPTRYALT, (void *) TRUE);
736 * Install handler to let us know about potential delays
738 (void) mail_parameters(NULL, SET_BLOCKNOTIFY, (void *) pine_block_notify);
740 if(ps_global->dump_supported_options){
741 dump_supported_options();
742 exit(0);
746 * Install extra headers to fetch along with all the other stuff
747 * mail_fetch_structure and mail_fetch_overview requests.
749 calc_extra_hdrs();
750 if(get_extra_hdrs())
751 (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
752 (void *) get_extra_hdrs());
754 if(init_username(pine_state) < 0){
755 fprintf(stderr, _("Who are you? (Unable to look up login name)\n"));
756 exit(-1);
759 if(init_userdir(pine_state) < 0)
760 exit(-1);
762 if(init_hostname(pine_state) < 0)
763 exit(-1);
766 * Verify mail dir if we're not in send only mode...
768 if(args.action == aaFolder && init_mail_dir(pine_state) < 0)
769 exit(-1);
771 init_signals();
773 /*--- input side ---*/
774 if(init_tty_driver(pine_state)){
775 #ifndef _WINDOWS /* always succeeds under _WINDOWS */
776 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);
777 exit(-1);
778 #endif /* !_WINDOWS */
782 /*--- output side ---*/
783 rv = config_screen(&(pine_state->ttyo));
784 #ifndef _WINDOWS /* always succeeds under _WINDOWS */
785 if(rv){
786 switch(rv){
787 case -1:
788 printf(_("Terminal type (environment variable TERM) not set.\n"));
789 break;
790 case -2:
791 printf(_("Terminal type \"%s\" is unknown.\n"), getenv("TERM"));
792 break;
793 case -3:
794 printf(_("Can't open terminal capabilities database.\n"));
795 break;
796 case -4:
797 printf(_("Your terminal, of type \"%s\", is lacking functions needed to run alpine.\n"), getenv("TERM"));
798 break;
801 printf("\r");
802 end_tty_driver(pine_state);
803 exit(-1);
805 #endif /* !_WINDOWS */
807 if(F_ON(F_BLANK_KEYMENU,ps_global))
808 FOOTER_ROWS(ps_global) = 1;
810 init_screen();
811 init_keyboard(pine_state->orig_use_fkeys);
812 strncpy(pine_state->inbox_name, INBOX_NAME,
813 sizeof(pine_state->inbox_name)-1);
814 init_folders(pine_state); /* digest folder spec's */
816 pine_state->in_init_seq = 0; /* so output (& ClearScreen) show up */
817 pine_state->dont_use_init_cmds = 1; /* don't use up initial_commands yet */
818 ClearScreen();
820 /* initialize titlebar in case we use it */
821 set_titlebar("", NULL, NULL, NULL, NULL, 0, FolderName, 0, 0, NULL);
824 * Prep storage object driver for PicoText
826 so_register_external_driver(pine_pico_get, pine_pico_give, pine_pico_writec, pine_pico_readc,
827 pine_pico_puts, pine_pico_seek, NULL, NULL);
829 #ifdef DEBUG
830 if(ps_global->debug_imap > 4 || debug > 9 || ps_global->debug_http > 0){
831 q_status_message(SM_ORDER | SM_DING, 5, 9,
832 _("Warning: sensitive authentication data included in debug file"));
833 flush_status_messages(0);
835 #endif
837 if(args.action == aaPrcCopy || args.action == aaAbookCopy){
838 int exit_val = -1;
839 char *err_msg = NULL;
842 * Don't translate these into UTF-8 because we'll be using them
843 * before we translate next time. User should use ascii.
845 if(args.data.copy.local && args.data.copy.remote){
846 switch(args.action){
847 case aaPrcCopy:
848 exit_val = copy_pinerc(args.data.copy.local,
849 args.data.copy.remote, &err_msg);
850 break;
852 case aaAbookCopy:
853 exit_val = copy_abook(args.data.copy.local,
854 args.data.copy.remote, &err_msg);
855 break;
857 default:
858 break;
861 if(err_msg){
862 q_status_message(SM_ORDER | SM_DING, 3, 4, err_msg);
863 fs_give((void **)&err_msg);
865 goodnight_gracey(pine_state, exit_val);
868 if(args.action == aaFolder
869 && (pine_state->first_time_user || pine_state->show_new_version)){
870 pine_state->mangled_header = 1;
871 show_main_screen(pine_state, 0, FirstMenu, &main_keymenu, 0,
872 (Pos *) NULL);
873 new_user_or_version(pine_state);
874 ClearScreen();
877 /* put back in case we need to suppress output */
878 pine_state->in_init_seq = pine_state->save_in_init_seq;
880 /* queue any init errors so they get displayed in a screen below */
881 queue_init_errors(ps_global);
883 /* "Page" the given file? */
884 if(args.action == aaMore){
885 int dice = 1, redir = 0;
887 if(pine_state->in_init_seq){
888 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
889 clear_cursor_pos();
890 if(pine_state->free_initial_cmds)
891 fs_give((void **)&(pine_state->free_initial_cmds));
893 pine_state->initial_cmds = NULL;
896 /*======= Requested that we simply page the given file =======*/
897 if(args.data.file){ /* Open the requested file... */
898 SourceType src;
899 STORE_S *store = NULL;
900 char *decode_error = NULL;
901 char filename[MAILTMPLEN];
903 if(args.data.file[0] == '\0'){
904 HelpType help = NO_HELP;
906 pine_state->mangled_footer = 1;
907 filename[0] = '\0';
908 while(1){
909 int flags = OE_APPEND_CURRENT;
911 rv = optionally_enter(filename, -FOOTER_ROWS(pine_state),
912 0, sizeof(filename),
913 /* TRANSLATORS: file is computer data */
914 _("File to open : "),
915 NULL, help, &flags);
916 if(rv == 3){
917 help = (help == NO_HELP) ? h_no_F_arg : NO_HELP;
918 continue;
921 if(rv != 4)
922 break;
925 if(rv == 1){
926 q_status_message(SM_ORDER, 0, 2, _("Cancelled"));
927 goodnight_gracey(pine_state, -1);
930 if(*filename){
931 removing_trailing_white_space(filename);
932 removing_leading_white_space(filename);
933 if(is_absolute_path(filename))
934 fnexpand(filename, sizeof(filename));
936 args.data.file = filename;
939 if(!*filename){
940 /* TRANSLATORS: file is computer data */
941 q_status_message(SM_ORDER, 0, 2 ,_("No file to open"));
942 goodnight_gracey(pine_state, -1);
946 if(stdin_getc){
947 redir++;
948 src = CharStar;
949 if(isatty(0) && (store = so_get(src, NULL, EDIT_ACCESS))){
950 gf_o_t pc;
952 gf_set_so_writec(&pc, store);
953 gf_filter_init();
954 if((decode_error = gf_pipe(stdin_getc, pc)) != NULL){
955 dice = 0;
956 q_status_message1(SM_ORDER, 3, 4,
957 _("Problem reading standard input: %s"),
958 decode_error);
961 gf_clear_so_writec(store);
963 else
964 dice = 0;
966 else{
967 src = FileStar;
968 strncpy(ps_global->cur_folder, args.data.file,
969 sizeof(ps_global->cur_folder)-1);
970 ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
971 if(!(store = so_get(src, args.data.file, READ_ACCESS|READ_FROM_LOCALE)))
972 dice = 0;
975 if(dice){
976 SCROLL_S sargs;
978 memset(&sargs, 0, sizeof(SCROLL_S));
979 sargs.text.text = so_text(store);
980 sargs.text.src = src;
981 /* TRANSLATORS: file is computer file being read by user */
982 sargs.text.desc = _("file");
983 /* TRANSLATORS: this is in the title bar at top of screen */
984 sargs.bar.title = _("FILE VIEW");
985 sargs.bar.style = FileTextPercent;
986 sargs.keys.menu = &simple_file_keymenu;
987 setbitmap(sargs.keys.bitmap);
988 scrolltool(&sargs);
990 printf("\n\n");
991 so_give(&store);
995 if(!dice){
996 q_status_message2(SM_ORDER, 3, 4,
997 _("Can't display \"%s\": %s"),
998 (redir) ? _("Standard Input")
999 : args.data.file ? args.data.file : "NULL",
1000 error_description(errno));
1003 goodnight_gracey(pine_state, 0);
1005 else if(args.action == aaMail || (stdin_getc && (args.action != aaURL))){
1006 /*======= address on command line/send one message mode ============*/
1007 char *to = NULL, *error = NULL, *addr = NULL;
1008 int len, good_addr = 1;
1009 int exit_val = 0;
1010 BUILDER_ARG fcc;
1012 if(pine_state->in_init_seq){
1013 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
1014 clear_cursor_pos();
1015 if(pine_state->free_initial_cmds)
1016 fs_give((void **) &(pine_state->free_initial_cmds));
1018 pine_state->initial_cmds = NULL;
1021 /*----- Format the To: line with commas for the composer ---*/
1022 if(args.data.mail.addrlist){
1023 STRLIST_S *p;
1025 for(p = args.data.mail.addrlist, len = 0; p; p = p->next)
1026 len += strlen(p->name) + 2;
1028 to = (char *) fs_get((len + 5) * sizeof(char));
1029 for(p = args.data.mail.addrlist, *to = '\0'; p; p = p->next){
1030 if(*to){
1031 strncat(to, ", ", len+5-strlen(to)-1);
1032 to[len+5-1] = '\0';
1035 strncat(to, p->name, len+5-strlen(to)-1);
1036 to[len+5-1] = '\0';
1039 memset((void *)&fcc, 0, sizeof(BUILDER_ARG));
1040 dprint((2, "building addr: -->%s<--\n", to ? to : "?"));
1041 good_addr = (build_address(to, &addr, &error, &fcc, NULL) >= 0);
1042 dprint((2, "mailing to: -->%s<--\n", addr ? addr : "?"));
1043 free_strlist(&args.data.mail.addrlist);
1045 else
1046 memset(&fcc, 0, sizeof(fcc));
1048 if(good_addr){
1049 compose_mail(addr, fcc.tptr, NULL,
1050 args.data.mail.attachlist, stdin_getc);
1052 else{
1053 /* TRANSLATORS: refers to bad email address */
1054 q_status_message1(SM_ORDER, 3, 4, _("Bad address: %s"), error);
1055 exit_val = -1;
1058 if(addr)
1059 fs_give((void **) &addr);
1061 if(fcc.tptr)
1062 fs_give((void **) &fcc.tptr);
1064 if(args.data.mail.attachlist)
1065 free_attachment_list(&args.data.mail.attachlist);
1067 if(to)
1068 fs_give((void **) &to);
1070 if(error)
1071 fs_give((void **) &error);
1073 goodnight_gracey(pine_state, exit_val);
1075 else{
1076 char int_mail[MAXPATH+1];
1077 struct key_menu *km = &main_keymenu;
1079 /*========== Normal pine mail reading mode ==========*/
1081 pine_state->mail_stream = NULL;
1082 pine_state->mangled_screen = 1;
1084 if(args.action == aaURL){
1085 url_tool_t f;
1087 if(pine_state->in_init_seq){
1088 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
1089 clear_cursor_pos();
1090 if(pine_state->free_initial_cmds)
1091 fs_give((void **) &(pine_state->free_initial_cmds));
1092 pine_state->initial_cmds = NULL;
1094 if((f = url_local_handler(args.url)) != NULL){
1095 if(args.data.mail.attachlist){
1096 if(f == url_local_mailto){
1097 if(!(url_local_mailto_and_atts(args.url,
1098 args.data.mail.attachlist)
1099 && pine_state->next_screen))
1100 free_attachment_list(&args.data.mail.attachlist);
1101 goodnight_gracey(pine_state, 0);
1103 else {
1104 q_status_message(SM_ORDER | SM_DING, 3, 4,
1105 _("Only mailto URLs are allowed with file attachments"));
1106 goodnight_gracey(pine_state, -1); /* no return */
1109 else if(!((*f)(args.url) && pine_state->next_screen))
1110 goodnight_gracey(pine_state, 0); /* no return */
1112 else{
1113 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1114 _("Unrecognized URL \"%s\""), args.url);
1115 goodnight_gracey(pine_state, -1); /* no return */
1118 else if(!pine_state->start_in_index){
1119 /* flash message about executing initial commands */
1120 if(pine_state->in_init_seq){
1121 pine_state->in_init_seq = 0;
1122 clear_cursor_pos();
1123 pine_state->mangled_header = 1;
1124 pine_state->mangled_footer = 1;
1125 pine_state->mangled_screen = 0;
1126 /* show that this is Alpine */
1127 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
1128 pine_state->mangled_screen = 1;
1129 pine_state->painted_footer_on_startup = 1;
1130 if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
1131 char buf1[6*MAX_SCREEN_COLS+1];
1132 char buf2[6*MAX_SCREEN_COLS+1];
1133 int wid;
1135 /* TRANSLATORS: Initial Keystroke List is the literal name of an option */
1136 strncpy(buf1, _("Executing Initial Keystroke List......"), sizeof(buf1));
1137 buf1[sizeof(buf1)-1] = '\0';
1138 wid = utf8_width(buf1);
1139 if(wid > ps_global->ttyo->screen_cols){
1140 utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
1141 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
1143 else{
1144 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
1145 MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
1149 pine_state->in_init_seq = 1;
1151 else{
1152 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
1153 pine_state->painted_body_on_startup = 1;
1154 pine_state->painted_footer_on_startup = 1;
1157 else{
1158 /* cancel any initial commands, overridden by cmd line */
1159 if(pine_state->in_init_seq){
1160 pine_state->in_init_seq = 0;
1161 pine_state->save_in_init_seq = 0;
1162 clear_cursor_pos();
1163 if(pine_state->initial_cmds){
1164 if(pine_state->free_initial_cmds)
1165 fs_give((void **)&(pine_state->free_initial_cmds));
1167 pine_state->initial_cmds = NULL;
1170 F_SET(F_USE_FK,pine_state, pine_state->orig_use_fkeys);
1173 (void) do_index_border(pine_state->context_current,
1174 pine_state->cur_folder,
1175 pine_state->mail_stream,
1176 pine_state->msgmap, MsgIndex, NULL,
1177 INDX_CLEAR|INDX_HEADER|INDX_FOOTER);
1178 pine_state->painted_footer_on_startup = 1;
1179 if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
1180 char buf1[6*MAX_SCREEN_COLS+1];
1181 char buf2[6*MAX_SCREEN_COLS+1];
1182 int wid;
1184 strncpy(buf1, _("Please wait, opening mail folder......"), sizeof(buf1));
1185 buf1[sizeof(buf1)-1] = '\0';
1186 wid = utf8_width(buf1);
1187 if(wid > ps_global->ttyo->screen_cols){
1188 utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
1189 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
1191 else{
1192 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
1193 MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
1198 fflush(stdout);
1200 #if !defined(_WINDOWS) && !defined(LEAVEOUTFIFO)
1201 if(ps_global->VAR_FIFOPATH && ps_global->VAR_FIFOPATH[0])
1202 init_newmailfifo(ps_global->VAR_FIFOPATH);
1203 #endif
1205 if(pine_state->in_init_seq){
1206 pine_state->in_init_seq = 0;
1207 clear_cursor_pos();
1210 if(args.action == aaFolder && args.data.folder){
1211 CONTEXT_S *cntxt = NULL, *tc = NULL;
1212 char foldername[MAILTMPLEN];
1213 int notrealinbox = 0;
1215 if(args.data.folder[0] == '\0'){
1216 char *fldr;
1217 unsigned save_def_goto_rule;
1219 foldername[0] = '\0';
1220 save_def_goto_rule = pine_state->goto_default_rule;
1221 pine_state->goto_default_rule = GOTO_FIRST_CLCTN;
1222 tc = default_save_context(pine_state->context_list);
1223 fldr = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
1224 pine_state->goto_default_rule = save_def_goto_rule;
1225 if(fldr){
1226 strncpy(foldername, fldr, sizeof(foldername)-1);
1227 foldername[sizeof(foldername)-1] = '\0';
1230 if(*foldername){
1231 removing_trailing_white_space(foldername);
1232 removing_leading_white_space(foldername);
1233 args.data.folder = cpystr(foldername);
1236 if(!*foldername){
1237 q_status_message(SM_ORDER, 0, 2 ,_("No folder to open"));
1238 goodnight_gracey(pine_state, -1);
1242 if(tc)
1243 cntxt = tc;
1244 else if((rv = pine_state->init_context) < 0)
1246 * As with almost all the folder vars in the pinerc,
1247 * we subvert the collection "breakout" here if the
1248 * folder name given looks like an absolute path on
1249 * this system...
1251 cntxt = (is_absolute_path(args.data.folder))
1252 ? NULL : pine_state->context_current;
1253 else if(rv == 0)
1254 cntxt = NULL;
1255 else
1256 for(cntxt = pine_state->context_list;
1257 rv > 1 && cntxt->next;
1258 rv--, cntxt = cntxt->next)
1261 if(pine_state && pine_state->ttyo){
1262 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1263 pine_state->painted_footer_on_startup = 0;
1264 pine_state->mangled_footer = 1;
1267 if(args.data.folder && *args.data.folder
1268 && !strucmp(args.data.folder, ps_global->inbox_name)
1269 && cntxt != ps_global->context_list)
1270 notrealinbox = 1;
1272 if(do_broach_folder(args.data.folder, cntxt, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) <= 0){
1273 q_status_message1(SM_ORDER, 3, 4,
1274 _("Unable to open folder \"%s\""), args.data.folder);
1276 fs_give((void **) &args.data.folder);
1278 goodnight_gracey(pine_state, -1);
1281 else if(args.action == aaFolder){
1282 #ifdef _WINDOWS
1284 * need to ask for the inbox name if no default under DOS
1285 * since there is no "inbox"
1288 if(!pine_state->VAR_INBOX_PATH || !pine_state->VAR_INBOX_PATH[0]
1289 || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0){
1290 HelpType help = NO_HELP;
1291 static ESCKEY_S ekey[] = {{ctrl(T), 2, "^T", "To Fldrs"},
1292 {-1, 0, NULL, NULL}};
1294 pine_state->mangled_footer = 1;
1295 int_mail[0] = '\0';
1296 while(1){
1297 int flags = OE_APPEND_CURRENT;
1299 rv = optionally_enter(int_mail, -FOOTER_ROWS(pine_state),
1300 0, sizeof(int_mail),
1301 _("No inbox! Folder to open as inbox : "),
1302 /* ekey */ NULL, help, &flags);
1303 if(rv == 3){
1304 help = (help == NO_HELP) ? h_sticky_inbox : NO_HELP;
1305 continue;
1308 if(rv != 4)
1309 break;
1312 if(rv == 1){
1313 q_status_message(SM_ORDER, 0, 2 ,_("Folder open cancelled"));
1314 rv = 0; /* reset rv */
1316 else if(rv == 2){
1317 show_main_screen(pine_state,0,FirstMenu,km,0,(Pos *)NULL);
1320 if(*int_mail){
1321 removing_trailing_white_space(int_mail);
1322 removing_leading_white_space(int_mail);
1323 if((!pine_state->VAR_INBOX_PATH
1324 || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0)
1325 /* TRANSLATORS: Inbox-Path and PINERC are literal, not to be translated */
1326 && want_to(_("Preserve folder as \"Inbox-Path\" in PINERC"),
1327 'y', 'n', NO_HELP, WT_NORM) == 'y'){
1328 set_variable(V_INBOX_PATH, int_mail, 1, 1, Main);
1330 else{
1331 if(pine_state->VAR_INBOX_PATH)
1332 fs_give((void **)&pine_state->VAR_INBOX_PATH);
1334 pine_state->VAR_INBOX_PATH = cpystr(int_mail);
1337 if(pine_state && pine_state->ttyo){
1338 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1339 pine_state->painted_footer_on_startup = 0;
1340 pine_state->mangled_footer = 1;
1343 do_broach_folder(pine_state->inbox_name,
1344 pine_state->context_list, NULL, DB_INBOXWOCNTXT);
1346 else
1347 q_status_message(SM_ORDER, 0, 2 ,_("No folder opened"));
1350 else
1352 #endif /* _WINDOWS */
1353 if(F_ON(F_PREOPEN_STAYOPENS, ps_global))
1354 preopen_stayopen_folders();
1356 if(pine_state && pine_state->ttyo){
1357 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1358 pine_state->painted_footer_on_startup = 0;
1359 pine_state->mangled_footer = 1;
1362 /* open inbox */
1363 do_broach_folder(pine_state->inbox_name,
1364 pine_state->context_list, NULL, DB_INBOXWOCNTXT);
1367 if(pine_state->mangled_footer)
1368 pine_state->painted_footer_on_startup = 0;
1370 if(args.action == aaFolder
1371 && pine_state->mail_stream
1372 && expire_sent_mail())
1373 pine_state->painted_footer_on_startup = 0;
1376 * Initialize the defaults. Initializing here means that
1377 * if they're remote, the user isn't prompted for an imap login
1378 * before the display's drawn, AND there's the chance that
1379 * we can climb onto the already opened folder's stream...
1381 if(ps_global->first_time_user)
1382 init_save_defaults(); /* initialize default save folders */
1384 build_path(int_mail,
1385 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
1386 : pine_state->home_dir,
1387 INTERRUPTED_MAIL, sizeof(int_mail));
1388 if(args.action == aaFolder
1389 && (folder_exists(NULL, int_mail) & FEX_ISFILE))
1390 q_status_message(SM_ORDER | SM_DING, 4, 5,
1391 _("Use Compose command to continue interrupted message."));
1393 if(args.action == aaFolder && args.data.folder)
1394 fs_give((void **) &args.data.folder);
1396 #if defined(USE_QUOTAS)
1398 long q;
1399 int over;
1400 q = disk_quota(pine_state->home_dir, &over);
1401 if(q > 0 && over){
1402 q_status_message2(SM_ASYNC | SM_DING, 4, 5,
1403 _("WARNING! Over your disk quota by %s bytes (%s)"),
1404 comatose(q),byte_string(q));
1407 #endif
1409 pine_state->in_init_seq = pine_state->save_in_init_seq;
1410 pine_state->dont_use_init_cmds = 0;
1411 clear_cursor_pos();
1413 if(pine_state->give_fixed_warning)
1414 q_status_message(SM_ASYNC, 0, 10,
1415 /* TRANSLATORS: config is an abbreviation for configuration */
1416 _("Note: some of your config options conflict with site policy and are ignored"));
1418 if(!prune_folders_ok())
1419 q_status_message(SM_ASYNC, 0, 10,
1420 /* TRANSLATORS: Pruned-Folders is literal */
1421 _("Note: ignoring Pruned-Folders outside of default collection for saves"));
1423 if(get_input_timeout() == 0 &&
1424 ps_global->VAR_INBOX_PATH &&
1425 ps_global->VAR_INBOX_PATH[0] == '{')
1426 q_status_message(SM_ASYNC, 0, 10,
1427 _("Note: Mail-Check-Interval=0 may cause IMAP server connection to time out"));
1429 #ifdef _WINDOWS
1430 mswin_setnewmailwidth(ps_global->nmw_width);
1431 #endif
1434 /*-------------------------------------------------------------------
1435 Loop executing the commands
1437 This is done like this so that one command screen can cause
1438 another one to execute it with out going through the main menu.
1439 ------------------------------------------------------------------*/
1440 if(!pine_state->next_screen)
1441 pine_state->next_screen = pine_state->start_in_index
1442 ? mail_index_screen : main_menu_screen;
1443 while(1){
1444 if(pine_state->next_screen == SCREEN_FUN_NULL)
1445 pine_state->next_screen = main_menu_screen;
1447 (*(pine_state->next_screen))(pine_state);
1451 exit(0);
1456 * The arguments need to be converted to UTF-8 for our internal use.
1457 * Not all arguments are converted because some are used before we
1458 * are able to do the conversion, like the pinerc name.
1460 void
1461 convert_args_to_utf8(struct pine *ps, ARGDATA_S *args)
1463 char *fromcharset = NULL;
1464 char *conv;
1466 if(args){
1467 if(ps->keyboard_charmap && strucmp(ps->keyboard_charmap, "UTF-8")
1468 && strucmp(ps->keyboard_charmap, "US-ASCII"))
1469 fromcharset = ps->keyboard_charmap;
1470 else if(ps->display_charmap && strucmp(ps->display_charmap, "UTF-8")
1471 && strucmp(ps->display_charmap, "US-ASCII"))
1472 fromcharset = ps->display_charmap;
1473 #ifndef _WINDOWS
1474 else if(ps->VAR_OLD_CHAR_SET && strucmp(ps->VAR_OLD_CHAR_SET, "UTF-8")
1475 && strucmp(ps->VAR_OLD_CHAR_SET, "US-ASCII"))
1476 fromcharset = ps->VAR_OLD_CHAR_SET;
1477 #endif /* ! _WINDOWS */
1479 if(args->action == aaURL && args->url){
1480 conv = convert_to_utf8(args->url, fromcharset, 0);
1481 if(conv){
1482 fs_give((void **) &args->url);
1483 args->url = conv;
1487 if(args->action == aaFolder && args->data.folder){
1488 conv = convert_to_utf8(args->data.folder, fromcharset, 0);
1489 if(conv){
1490 fs_give((void **) &args->data.folder);
1491 args->data.folder = conv;
1495 if(args->action == aaMore && args->data.file){
1496 conv = convert_to_utf8(args->data.file, fromcharset, 0);
1497 if(conv){
1498 fs_give((void **) &args->data.file);
1499 args->data.file = conv;
1503 if(args->action == aaURL || args->action == aaMail){
1504 if(args->data.mail.addrlist){
1505 STRLIST_S *p;
1507 for(p = args->data.mail.addrlist; p; p=p->next){
1508 if(p->name){
1509 conv = convert_to_utf8(p->name, fromcharset, 0);
1510 if(conv){
1511 fs_give((void **) &p->name);
1512 p->name = conv;
1518 if(args->data.mail.attachlist){
1519 PATMT *p;
1521 for(p = args->data.mail.attachlist; p; p=p->next){
1522 if(p->filename){
1523 conv = convert_to_utf8(p->filename, fromcharset, 0);
1524 if(conv){
1525 fs_give((void **) &p->filename);
1526 p->filename = conv;
1536 void
1537 preopen_stayopen_folders(void)
1539 char **open_these;
1541 for(open_these = ps_global->VAR_PERMLOCKED;
1542 open_these && *open_these; open_these++)
1543 (void) do_broach_folder(*open_these, ps_global->context_list,
1544 NULL, DB_NOVISIT);
1549 * read_stdin_char - simple function to return a character from
1550 * redirected stdin
1553 read_stdin_char(unsigned char *c)
1555 int rv;
1557 /* it'd probably be a good idea to fix this to pre-read blocks */
1558 while(1){
1559 rv = read(PIPED_FD, c, 1);
1560 if(rv < 0){
1561 if(errno == EINTR){
1562 dprint((2, "read_stdin_char: read interrupted, restarting\n"));
1563 continue;
1565 else
1566 dprint((1, "read_stdin_char: read FAILED: %s\n",
1567 error_description(errno)));
1569 break;
1571 return(rv);
1575 /* this default is from the array of structs below */
1576 #define DEFAULT_MENU_ITEM ((unsigned) 3) /* LIST FOLDERS */
1577 #define ABOOK_MENU_ITEM ((unsigned) 4) /* ADDRESS BOOK */
1578 #define MAX_MENU_ITEM ((unsigned) 6)
1580 * Skip this many spaces between rows of main menu screen.
1581 * We have MAX_MENU_ITEM+1 = # of commands in menu
1582 * 1 = copyright line
1583 * MAX_MENU_ITEM = rows between commands
1584 * 1 = extra row above commands
1585 * 1 = row between commands and copyright
1587 * To make it simple, if there is enough room for all of that include all the
1588 * extra space, if not, cut it all out.
1590 #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)
1592 static unsigned menu_index = DEFAULT_MENU_ITEM;
1595 * One of these for each line that gets printed in the middle of the
1596 * screen in the main menu.
1598 static struct menu_key {
1599 char *key_and_name,
1600 *news_addition;
1601 int key_index; /* index into keymenu array for this cmd */
1602 } mkeys[] = {
1604 * TRANSLATORS: These next few are headings on the Main alpine menu.
1605 * It's nice if the dashes can be made to line up vertically.
1607 {N_(" %s HELP - Get help using Alpine"),
1608 NULL, MAIN_HELP_KEY},
1609 {N_(" %s COMPOSE MESSAGE - Compose and send%s a message"),
1610 /* TRANSLATORS: We think of sending an email message or posting a news message.
1611 The message is shown as Compose and send/post a message */
1612 N_("/post"), MAIN_COMPOSE_KEY},
1613 {N_(" %s MESSAGE INDEX - View messages in current folder"),
1614 NULL, MAIN_INDEX_KEY},
1615 {N_(" %s FOLDER LIST - Select a folder%s to view"),
1616 /* TRANSLATORS: When news is supported the message above becomes
1617 Select a folder OR news group to view */
1618 N_(" OR news group"), MAIN_FOLDER_KEY},
1619 {N_(" %s ADDRESS BOOK - Update address book"),
1620 NULL, MAIN_ADDRESS_KEY},
1621 {N_(" %s SETUP - Configure Alpine Options"),
1622 NULL, MAIN_SETUP_KEY},
1623 /* TRANSLATORS: final Main menu line */
1624 {N_(" %s QUIT - Leave the Alpine program"),
1625 NULL, MAIN_QUIT_KEY}
1630 /*----------------------------------------------------------------------
1631 display main menu and execute main menu commands
1633 Args: The usual pine structure
1635 Result: main menu commands are executed
1638 M A I N M E N U S C R E E N
1640 Paint the main menu on the screen, get the commands and either execute
1641 the function or pass back the name of the function to execute for the menu
1642 selection. Only simple functions that always return here can be executed
1643 here.
1645 This functions handling of new mail, redrawing, errors and such can
1646 serve as a template for the other screen that do much the same thing.
1648 There is a loop that fetches and executes commands until a command to leave
1649 this screen is given. Then the name of the next screen to display is
1650 stored in next_screen member of the structure and this function is exited
1651 with a return.
1653 First a check for new mail is performed. This might involve reading the new
1654 mail into the inbox which might then cause the screen to be repainted.
1656 Then the general screen painting is done. This is usually controlled
1657 by a few flags and some other position variables. If they change they
1658 tell this part of the code what to repaint. This will include cursor
1659 motion and so on.
1660 ----*/
1661 void
1662 main_menu_screen(struct pine *pine_state)
1664 UCS ch;
1665 int cmd, just_a_navigate_cmd, setup_command, km_popped;
1666 int notrealinbox;
1667 char *new_folder, *utf8str;
1668 CONTEXT_S *tc;
1669 struct key_menu *km;
1670 OtherMenu what;
1671 Pos curs_pos;
1673 ps_global = pine_state;
1674 just_a_navigate_cmd = 0;
1675 km_popped = 0;
1676 menu_index = DEFAULT_MENU_ITEM;
1677 what = FirstMenu; /* which keymenu to display */
1678 ch = 'x'; /* For display_message 1st time through */
1679 pine_state->next_screen = SCREEN_FUN_NULL;
1680 pine_state->prev_screen = main_menu_screen;
1681 curs_pos.row = pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
1682 curs_pos.col = 0;
1683 km = &main_keymenu;
1685 mailcap_free(); /* free resources we won't be using for a while */
1687 if(!pine_state->painted_body_on_startup
1688 && !pine_state->painted_footer_on_startup){
1689 pine_state->mangled_screen = 1;
1692 dprint((1, "\n\n ---- MAIN_MENU_SCREEN ----\n"));
1694 while(1){
1695 if(km_popped){
1696 km_popped--;
1697 if(km_popped == 0){
1698 clearfooter(pine_state);
1699 pine_state->mangled_body = 1;
1704 * fix up redrawer just in case some submenu caused it to get
1705 * reassigned...
1707 pine_state->redrawer = main_redrawer;
1709 /*----------- Check for new mail -----------*/
1710 if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
1711 pine_state->mangled_header = 1;
1713 if(streams_died())
1714 pine_state->mangled_header = 1;
1716 show_main_screen(pine_state, just_a_navigate_cmd, what, km,
1717 km_popped, &curs_pos);
1718 just_a_navigate_cmd = 0;
1719 what = SameMenu;
1721 /*---- This displays new mail notification, or errors ---*/
1722 if(km_popped){
1723 FOOTER_ROWS(pine_state) = 3;
1724 mark_status_dirty();
1727 display_message(ch);
1728 if(km_popped){
1729 FOOTER_ROWS(pine_state) = 1;
1730 mark_status_dirty();
1733 if(F_OFF(F_SHOW_CURSOR, ps_global)){
1734 curs_pos.row =pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
1735 curs_pos.col =0;
1738 MoveCursor(curs_pos.row, curs_pos.col);
1740 /*------ Read the command from the keyboard ----*/
1741 #ifdef MOUSE
1742 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
1743 register_mfunc(mouse_in_content, HEADER_ROWS(pine_state), 0,
1744 pine_state->ttyo->screen_rows-(FOOTER_ROWS(pine_state)+1),
1745 pine_state->ttyo->screen_cols);
1746 #endif
1747 #if defined(DOS) || defined(OS2)
1749 * AND pre-build header lines. This works just fine under
1750 * DOS since we wait for characters in a loop. Something will
1751 * will have to change under UNIX if we want to do the same.
1753 /* while_waiting = build_header_cache; */
1754 #ifdef _WINDOWS
1755 mswin_sethelptextcallback(pcpine_help_main);
1756 mswin_mousetrackcallback(pcpine_main_cursor);
1757 #endif
1758 #endif
1759 ch = READ_COMMAND(&utf8str);
1760 #ifdef MOUSE
1761 clear_mfunc(mouse_in_content);
1762 #endif
1763 #if defined(DOS) || defined(OS2)
1764 /* while_waiting = NULL; */
1765 #ifdef _WINDOWS
1766 mswin_sethelptextcallback(NULL);
1767 mswin_mousetrackcallback(NULL);
1768 #endif
1769 #endif
1771 /* No matter what, Quit here always works */
1772 if(ch == 'q' || ch == 'Q'){
1773 cmd = MC_QUIT;
1775 #ifdef DEBUG
1776 else if(debug && ch && ch < 0x80 && strchr("123456789", ch)){
1777 int olddebug;
1779 olddebug = debug;
1780 debug = ch - '0';
1781 if(debug > 7)
1782 ps_global->debug_timestamp = 1;
1783 else
1784 ps_global->debug_timestamp = 0;
1786 if(debug > 7)
1787 ps_global->debug_imap = 4;
1788 else if(debug > 6)
1789 ps_global->debug_imap = 3;
1790 else if(debug > 4)
1791 ps_global->debug_imap = 2;
1792 else if(debug > 2)
1793 ps_global->debug_imap = 1;
1794 else
1795 ps_global->debug_imap = 0;
1797 if(ps_global->mail_stream){
1798 if(ps_global->debug_imap > 0){
1799 mail_debug(ps_global->mail_stream);
1800 #ifdef _WINDOWS
1801 mswin_enableimaptelemetry(TRUE);
1802 #endif
1804 else{
1805 mail_nodebug(ps_global->mail_stream);
1806 #ifdef _WINDOWS
1807 mswin_enableimaptelemetry(FALSE);
1808 #endif
1812 if(debug > 7 && olddebug <= 7)
1813 mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
1814 else if(debug <= 7 && olddebug > 7 && !ps_global->debugmem)
1815 mail_parameters(NULL, SET_TCPDEBUG, (void *) FALSE);
1817 if(debug > 7 && olddebug <= 7)
1818 mail_parameters(NULL, SET_HTTPDEBUG, (void *) TRUE);
1819 else if(debug <= 7 && olddebug > 7 && !ps_global->debugmem)
1820 mail_parameters(NULL, SET_HTTPDEBUG, (void *) FALSE);
1822 dprint((1, "*** Debug level set to %d ***\n", debug));
1823 if(debugfile)
1824 fflush(debugfile);
1826 q_status_message1(SM_ORDER, 0, 1, _("Debug level set to %s"),
1827 int2string(debug));
1828 continue;
1830 #endif /* DEBUG */
1831 else{
1832 cmd = menu_command(ch, km);
1834 if(km_popped)
1835 switch(cmd){
1836 case MC_NONE :
1837 case MC_OTHER :
1838 case MC_RESIZE :
1839 case MC_REPAINT :
1840 km_popped++;
1841 break;
1843 default:
1844 clearfooter(pine_state);
1845 break;
1849 /*------ Execute the command ------*/
1850 switch (cmd){
1851 help_case :
1852 /*------ HELP ------*/
1853 case MC_HELP :
1855 if(FOOTER_ROWS(pine_state) == 1 && km_popped == 0){
1856 km_popped = 2;
1857 pine_state->mangled_footer = 1;
1859 else{
1860 /* TRANSLATORS: This is a screen title */
1861 helper(main_menu_tx, _("HELP FOR MAIN MENU"), 0);
1862 pine_state->mangled_screen = 1;
1865 break;
1868 /*---------- display other key bindings ------*/
1869 case MC_OTHER :
1870 if(ch == 'o')
1871 warn_other_cmds();
1873 what = NextMenu;
1874 pine_state->mangled_footer = 1;
1875 break;
1878 /*---------- Previous item in menu ----------*/
1879 case MC_PREVITEM :
1880 if(menu_index > 0) {
1881 menu_index--;
1882 pine_state->mangled_body = 1;
1883 if(km->which == 0)
1884 pine_state->mangled_footer = 1;
1886 just_a_navigate_cmd++;
1888 else
1889 /* TRANSLATORS: list refers to list of commands in main menu */
1890 q_status_message(SM_ORDER, 0, 2, _("Already at top of list"));
1892 break;
1895 /*---------- Next item in menu ----------*/
1896 case MC_NEXTITEM :
1897 if(menu_index < MAX_MENU_ITEM){
1898 menu_index++;
1899 pine_state->mangled_body = 1;
1900 if(km->which == 0)
1901 pine_state->mangled_footer = 1;
1903 just_a_navigate_cmd++;
1905 else
1906 q_status_message(SM_ORDER, 0, 2, _("Already at bottom of list"));
1908 break;
1911 /*---------- Release Notes ----------*/
1912 case MC_RELNOTES :
1913 /* TRANSLATORS: This is a screen title */
1914 helper(h_news, _("ALPINE RELEASE NOTES"), 0);
1915 pine_state->mangled_screen = 1;
1916 break;
1919 #ifdef KEYBOARD_LOCK
1920 /*---------- Keyboard lock ----------*/
1921 case MC_KBLOCK :
1922 (void) lock_keyboard();
1923 pine_state->mangled_screen = 1;
1924 break;
1925 #endif /* KEYBOARD_LOCK */
1928 /*---------- Quit pine ----------*/
1929 case MC_QUIT :
1930 pine_state->next_screen = quit_screen;
1931 return;
1934 /*---------- Go to composer ----------*/
1935 case MC_COMPOSE :
1936 pine_state->next_screen = compose_screen;
1937 return;
1940 /*---- Go to alternate composer ------*/
1941 case MC_ROLE :
1942 pine_state->next_screen = alt_compose_screen;
1943 return;
1946 /*---------- Top of Folder list ----------*/
1947 case MC_COLLECTIONS :
1948 pine_state->next_screen = folder_screen;
1949 return;
1952 /*---------- Goto new folder ----------*/
1953 case MC_GOTO :
1954 tc = ps_global->context_current;
1955 new_folder = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
1956 if(new_folder)
1957 visit_folder(ps_global, new_folder, tc, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT);
1959 return;
1962 /*---------- Go to index ----------*/
1963 case MC_INDEX :
1964 if(THREADING()
1965 && sp_viewing_a_thread(pine_state->mail_stream)
1966 && unview_thread(pine_state, pine_state->mail_stream,
1967 pine_state->msgmap)){
1968 pine_state->view_skipped_index = 0;
1969 pine_state->mangled_screen = 1;
1972 pine_state->next_screen = mail_index_screen;
1973 return;
1976 /*---------- Review Status Messages ----------*/
1977 case MC_JOURNAL :
1978 review_messages();
1979 pine_state->mangled_screen = 1;
1980 break;
1983 /*---------- Setup mini menu ----------*/
1984 case MC_SETUP :
1985 setup_case :
1986 setup_command = setup_menu(pine_state);
1987 pine_state->mangled_footer = 1;
1988 do_setup_task(setup_command);
1989 if(ps_global->next_screen != main_menu_screen)
1990 return;
1992 break;
1995 /*---------- Go to address book ----------*/
1996 case MC_ADDRBOOK :
1997 pine_state->next_screen = addr_book_screen;
1998 return;
2001 /*------ Repaint the works -------*/
2002 case MC_RESIZE :
2003 case MC_REPAINT :
2004 ClearScreen();
2005 pine_state->mangled_screen = 1;
2006 break;
2009 #ifdef MOUSE
2010 /*------- Mouse event ------*/
2011 case MC_MOUSE :
2013 MOUSEPRESS mp;
2014 unsigned ndmi;
2015 struct pine *ps = pine_state;
2017 mouse_get_last (NULL, &mp);
2019 #ifdef _WINDOWS
2020 if(mp.button == M_BUTTON_RIGHT){
2021 if(!mp.doubleclick){
2022 static MPopup main_popup[] = {
2023 {tQueue, {"Folder List", lNormal}, {'L'}},
2024 {tQueue, {"Message Index", lNormal}, {'I'}},
2025 {tSeparator},
2026 {tQueue, {"Address Book", lNormal}, {'A'}},
2027 {tQueue, {"Setup Options", lNormal}, {'S'}},
2028 {tTail}
2031 mswin_popup(main_popup);
2034 else {
2035 #endif
2036 if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps)))
2037 ndmi = (mp.row+1 - HEADER_ROWS(ps) - (MNSKIP(ps)+1))/(MNSKIP(ps)+1);
2039 if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps))
2040 && !(MNSKIP(ps) && (mp.row+1) & 0x01)
2041 && ndmi <= MAX_MENU_ITEM
2042 && FOOTER_ROWS(ps) + (ndmi+1)*(MNSKIP(ps)+1)
2043 + MNSKIP(ps) + FOOTER_ROWS(ps) <= ps->ttyo->screen_rows){
2044 if(mp.doubleclick){
2045 switch(ndmi){ /* fake main_screen request */
2046 case 0 :
2047 goto help_case;
2049 case 1 :
2050 pine_state->next_screen = compose_screen;
2051 return;
2053 case 2 :
2054 pine_state->next_screen = mail_index_screen;
2055 return;
2057 case 3 :
2058 pine_state->next_screen = folder_screen;
2059 return;
2061 case 4 :
2062 pine_state->next_screen = addr_book_screen;
2063 return;
2065 case 5 :
2066 goto setup_case;
2068 case 6 :
2069 pine_state->next_screen = quit_screen;
2070 return;
2072 default: /* no op */
2073 break;
2076 else{
2077 menu_index = ndmi;
2078 pine_state->mangled_body = 1;
2079 if(km->which == 0)
2080 pine_state->mangled_footer = 1;
2082 just_a_navigate_cmd++;
2085 #ifdef _WINDOWS
2087 #endif
2090 break;
2091 #endif
2094 /*------ Input timeout ------*/
2095 case MC_NONE :
2096 break; /* noop for timeout loop mail check */
2099 /*------ Bogus Input ------*/
2100 case MC_UNKNOWN :
2101 if(ch == 'm' || ch == 'M'){
2102 q_status_message(SM_ORDER, 0, 1, "Already in Main Menu");
2103 break;
2106 default:
2107 bogus_command(ch, F_ON(F_USE_FK,pine_state) ? "F1" : "?");
2108 break;
2110 case MC_UTF8:
2111 bogus_utf8_command(utf8str, F_ON(F_USE_FK, pine_state) ? "F1" : "?");
2112 break;
2113 } /* the switch */
2114 } /* the BIG while loop! */
2118 /*----------------------------------------------------------------------
2119 Re-Draw the main menu
2121 Args: none.
2123 Result: main menu is re-displayed
2124 ----*/
2125 void
2126 main_redrawer(void)
2128 struct key_menu *km = &main_keymenu;
2130 ps_global->mangled_screen = 1;
2131 show_main_screen(ps_global, 0, FirstMenu, km, 0, (Pos *)NULL);
2135 /*----------------------------------------------------------------------
2136 Draw the main menu
2138 Args: pine_state - the usual struct
2139 quick_draw - tells do_menu() it can skip some drawing
2140 what - tells which section of keymenu to draw
2141 km - the keymenu
2142 cursor_pos - returns a good position for the cursor to be located
2144 Result: main menu is displayed
2145 ----*/
2146 void
2147 show_main_screen(struct pine *ps, int quick_draw, OtherMenu what,
2148 struct key_menu *km, int km_popped, Pos *cursor_pos)
2150 if(ps->painted_body_on_startup || ps->painted_footer_on_startup){
2151 ps->mangled_screen = 0; /* only worry about it here */
2152 ps->mangled_header = 1; /* we have to redo header */
2153 if(!ps->painted_body_on_startup)
2154 ps->mangled_body = 1; /* make sure to paint body*/
2156 if(!ps->painted_footer_on_startup)
2157 ps->mangled_footer = 1; /* make sure to paint footer*/
2159 ps->painted_body_on_startup = 0;
2160 ps->painted_footer_on_startup = 0;
2163 if(ps->mangled_screen){
2164 ps->mangled_header = 1;
2165 ps->mangled_body = 1;
2166 ps->mangled_footer = 1;
2167 ps->mangled_screen = 0;
2170 #ifdef _WINDOWS
2171 /* Reset the scroll range. Main screen never scrolls. */
2172 scroll_setrange (0L, 0L);
2173 mswin_beginupdate();
2174 #endif
2176 /* paint the titlebar if needed */
2177 if(ps->mangled_header){
2178 /* TRANSLATORS: screen title */
2179 set_titlebar(_("MAIN MENU"), ps->mail_stream, ps->context_current,
2180 ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0, NULL);
2181 ps->mangled_header = 0;
2184 /* paint the body if needed */
2185 if(ps->mangled_body){
2186 if(!quick_draw)
2187 ClearBody();
2189 do_menu(quick_draw, cursor_pos, km);
2190 ps->mangled_body = 0;
2193 /* paint the keymenu if needed */
2194 if(km && ps->mangled_footer){
2195 static char label[LONGEST_LABEL + 2 + 1], /* label + brackets + \0 */
2196 name[8];
2197 bitmap_t bitmap;
2199 setbitmap(bitmap);
2201 #ifdef KEYBOARD_LOCK
2202 if(ps_global->restricted || F_ON(F_DISABLE_KBLOCK_CMD,ps_global))
2203 #endif
2204 clrbitn(MAIN_KBLOCK_KEY, bitmap);
2206 menu_clear_binding(km, '>');
2207 menu_clear_binding(km, '.');
2208 menu_clear_binding(km, KEY_RIGHT);
2209 menu_clear_binding(km, ctrl('M'));
2210 menu_clear_binding(km, ctrl('J'));
2211 km->keys[MAIN_DEFAULT_KEY].bind
2212 = km->keys[mkeys[menu_index].key_index].bind;
2213 km->keys[MAIN_DEFAULT_KEY].label
2214 = km->keys[mkeys[menu_index].key_index].label;
2216 /* put brackets around the default action */
2217 snprintf(label, sizeof(label), "[%s]", km->keys[mkeys[menu_index].key_index].label);
2218 label[sizeof(label)-1] = '\0';
2219 strncpy(name, ">", sizeof(name));
2220 name[sizeof(name)-1] = '\0';
2221 km->keys[MAIN_DEFAULT_KEY].label = label;
2222 km->keys[MAIN_DEFAULT_KEY].name = name;
2223 menu_add_binding(km, '>', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2224 menu_add_binding(km, '.', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2225 menu_add_binding(km, ctrl('M'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2226 menu_add_binding(km, ctrl('J'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2228 if(F_ON(F_ARROW_NAV,ps_global))
2229 menu_add_binding(km, KEY_RIGHT, km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2231 if(km_popped){
2232 FOOTER_ROWS(ps) = 3;
2233 clearfooter(ps);
2236 draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
2237 1-FOOTER_ROWS(ps_global), 0, what);
2238 ps->mangled_footer = 0;
2239 if(km_popped){
2240 FOOTER_ROWS(ps) = 1;
2241 mark_keymenu_dirty();
2245 #ifdef _WINDOWS
2246 mswin_endupdate();
2247 #endif
2251 /*----------------------------------------------------------------------
2252 Actually display the main menu
2254 Args: quick_draw - just a next or prev command was typed so we only have
2255 to redraw the highlighting
2256 cursor_pos - a place to return a good value for cursor location
2258 Result: Main menu is displayed
2259 ---*/
2260 void
2261 do_menu(int quick_draw, Pos *cursor_pos, struct key_menu *km)
2263 struct pine *ps = ps_global;
2264 int dline, indent, longest = 0, cmd;
2265 char buf[4*MAX_SCREEN_COLS+1];
2266 char buf2[4*MAX_SCREEN_COLS+1];
2267 static int last_inverse = -1;
2268 Pos pos;
2270 /* find the longest command */
2271 for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
2272 memset((void *) buf, ' ', sizeof(buf));
2273 snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
2274 (F_OFF(F_USE_FK,ps)
2275 && km->keys[mkeys[cmd].key_index].name)
2276 ? km->keys[mkeys[cmd].key_index].name : "",
2277 (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
2278 ? _(mkeys[cmd].news_addition) : "");
2279 buf[sizeof(buf)-1] = '\0';
2281 if(longest < (indent = utf8_width(buf)))
2282 longest = indent;
2285 indent = MAX(((ps->ttyo->screen_cols - longest)/2) - 1, 0);
2287 dline = HEADER_ROWS(ps) + MNSKIP(ps);
2288 for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
2289 /* leave room for copyright and footer */
2290 if(dline + MNSKIP(ps) + 1 + FOOTER_ROWS(ps) >= ps->ttyo->screen_rows)
2291 break;
2293 if(quick_draw && !(cmd == last_inverse || cmd == menu_index)){
2294 dline += (1 + MNSKIP(ps));
2295 continue;
2298 if(cmd == menu_index)
2299 StartInverse();
2301 memset((void *) buf, ' ', sizeof(buf));
2302 snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
2303 (F_OFF(F_USE_FK,ps)
2304 && km->keys[mkeys[cmd].key_index].name)
2305 ? km->keys[mkeys[cmd].key_index].name : "",
2306 (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
2307 ? _(mkeys[cmd].news_addition) : "");
2308 buf[sizeof(buf)-1] = '\0';
2310 utf8_pad_to_width(buf2, buf, sizeof(buf2),
2311 MIN(ps->ttyo->screen_cols-indent,longest+1), 1);
2312 pos.row = dline++;
2313 pos.col = indent;
2314 PutLine0(pos.row, pos.col, buf2);
2316 if(MNSKIP(ps))
2317 dline++;
2319 if(cmd == menu_index){
2320 if(cursor_pos){
2321 cursor_pos->row = pos.row;
2322 /* 6 is 1 for the letter plus 5 spaces */
2323 cursor_pos->col = pos.col + 6;
2324 if(F_OFF(F_USE_FK,ps))
2325 cursor_pos->col++;
2327 cursor_pos->col = MIN(cursor_pos->col, ps->ttyo->screen_cols);
2330 EndInverse();
2335 last_inverse = menu_index;
2337 if(!quick_draw && FOOTER_ROWS(ps)+1 < ps->ttyo->screen_rows){
2338 utf8_to_width(buf2, LEGAL_NOTICE, sizeof(buf2),
2339 ps->ttyo->screen_cols-3, NULL);
2340 PutLine0(ps->ttyo->screen_rows - (FOOTER_ROWS(ps)+1),
2341 MAX(0, ((ps->ttyo->screen_cols-utf8_width(buf2))/2)),
2342 buf2);
2345 fflush(stdout);
2350 choose_setup_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2352 int rv = 1;
2353 SRV_S *srv;
2355 if(!(srv = (SRV_S *)sparms->proc.data.p)){
2356 sparms->proc.data.p = (SRV_S *)fs_get(sizeof(*srv));
2357 srv = (SRV_S *)sparms->proc.data.p;
2358 memset(srv, 0, sizeof(*srv));
2361 ps_global->next_screen = SCREEN_FUN_NULL;
2363 switch(cmd){
2364 case MC_PRINTER :
2365 srv->cmd = 'p';
2366 break;
2368 case MC_PASSWD :
2369 srv->cmd = 'n';
2370 break;
2372 case MC_CONFIG :
2373 srv->cmd = 'c';
2374 break;
2376 case MC_XOAUTH2 :
2377 srv->cmd = 'u';
2378 break;
2380 case MC_SIG :
2381 srv->cmd = 's';
2382 break;
2384 case MC_ABOOKS :
2385 srv->cmd = 'a';
2386 break;
2388 case MC_CLISTS :
2389 srv->cmd = 'l';
2390 break;
2392 case MC_RULES :
2393 srv->cmd = 'r';
2394 break;
2396 case MC_DIRECTORY :
2397 srv->cmd = 'd';
2398 break;
2400 case MC_KOLOR :
2401 srv->cmd = 'k';
2402 break;
2404 case MC_REMOTE :
2405 srv->cmd = 'z';
2406 break;
2408 case MC_SECURITY : /* S/MIME setup screen */
2409 srv->cmd = 'm';
2410 break;
2412 case MC_EXCEPT :
2413 srv->exc = !srv->exc;
2414 menu_clear_binding(sparms->keys.menu, 'x');
2415 if(srv->exc){
2416 if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
2417 /* TRANSLATORS: screen title */
2418 sparms->bar.title = cpystr(_("SETUP EXCEPTIONS"));
2419 ps_global->mangled_header = 1;
2420 /* TRANSLATORS: The reason the X is upper case in eXceptions
2421 is because the command key is X. It isn't necessary, just
2422 nice if it works. */
2423 menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
2424 N_("not eXceptions"), SETUP_EXCEPT);
2426 else{
2427 if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
2428 /* TRANSLATORS: screen title */
2429 sparms->bar.title = cpystr(_("SETUP"));
2430 ps_global->mangled_header = 1;
2431 menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
2432 N_("eXceptions"), SETUP_EXCEPT);
2435 if(sparms->keys.menu->which == 1)
2436 ps_global->mangled_footer = 1;
2438 rv = 0;
2439 break;
2441 case MC_NO_EXCEPT :
2442 #if defined(DOS) || defined(OS2)
2443 q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \"PINERCEX\" file to use eXceptions"));
2444 #else
2445 q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \".pinercex\" file to use eXceptions"));
2446 #endif
2447 rv = 0;
2448 break;
2450 default:
2451 alpine_panic("Unexpected command in choose_setup_cmd");
2452 break;
2455 return(rv);
2460 setup_menu(struct pine *ps)
2462 int ret = 0, exceptions = 0;
2463 int printer = 0, passwd = 0, config = 0, sig = 0, dir = 0, smime = 0, exc = 0;
2464 SCROLL_S sargs;
2465 SRV_S *srv;
2466 STORE_S *store;
2468 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2469 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
2470 return(ret);
2473 #if !defined(DOS)
2474 if(!ps_global->vars[V_PRINTER].is_fixed) /* printer can be changed */
2475 printer++;
2476 #endif
2478 #ifdef PASSWD_PROG
2479 if(F_OFF(F_DISABLE_PASSWORD_CMD,ps_global)) /* password is allowed */
2480 passwd++;
2481 #endif
2483 if(F_OFF(F_DISABLE_CONFIG_SCREEN,ps_global)) /* config allowed */
2484 config++;
2486 if(F_OFF(F_DISABLE_SIGEDIT_CMD,ps_global)) /* .sig editing is allowed */
2487 sig++;
2489 #ifdef ENABLE_LDAP
2490 dir++;
2491 #endif
2493 #ifdef SMIME
2494 smime++;
2495 #endif
2497 if(ps_global->post_prc)
2498 exc++;
2500 /* TRANSLATORS: starting here we have a whole screen of help text */
2501 so_puts(store, _("This is the Setup screen for Alpine. Choose from the following commands:\n"));
2503 so_puts(store, "\n");
2504 so_puts(store, _("(E) Exit Setup:\n"));
2505 so_puts(store, _(" This puts you back at the Main Menu.\n"));
2507 if(exc){
2508 so_puts(store, "\n");
2509 so_puts(store, _("(X) eXceptions:\n"));
2510 so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
2511 so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
2512 so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
2513 so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
2514 so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
2515 so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
2516 so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
2517 so_puts(store, _(" on a particular platform.\n"));
2518 so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
2519 so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
2520 so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
2523 if(printer){
2524 so_puts(store, "\n");
2525 so_puts(store, _("(P) Printer:\n"));
2526 so_puts(store, _(" Allows you to set a default printer and to define custom\n"));
2527 so_puts(store, _(" print commands.\n"));
2530 if(passwd){
2531 so_puts(store, "\n");
2532 so_puts(store, _("(N) Newpassword:\n"));
2533 so_puts(store, _(" Change your password.\n"));
2536 if(config){
2537 so_puts(store, "\n");
2538 so_puts(store, _("(C) Config:\n"));
2539 so_puts(store, _(" Allows you to set or unset many features of Alpine.\n"));
2540 so_puts(store, _(" You may also set the values of many options with this command.\n"));
2543 if(sig){
2544 so_puts(store, "\n");
2545 so_puts(store, _("(S) Signature:\n"));
2546 so_puts(store, _(" Enter or edit a custom signature which will\n"));
2547 so_puts(store, _(" be included with each new message you send.\n"));
2550 so_puts(store, "\n");
2551 so_puts(store, _("(A) AddressBooks:\n"));
2552 so_puts(store, _(" Define a non-default address book.\n"));
2554 so_puts(store, "\n");
2555 so_puts(store, _("(L) collectionLists:\n"));
2556 so_puts(store, _(" You may define groups of folders to help you better organize your mail.\n"));
2558 so_puts(store, "\n");
2559 so_puts(store, _("(R) Rules:\n"));
2560 so_puts(store, _(" This has up to six sub-categories: Roles, Index Colors, Filters,\n"));
2561 so_puts(store, _(" SetScores, Search, and Other. If the Index Colors option is\n"));
2562 so_puts(store, _(" missing you may turn it on (if possible) with Setup/Kolor.\n"));
2563 so_puts(store, _(" If Roles is missing it has probably been administratively disabled.\n"));
2565 if(dir){
2566 so_puts(store, "\n");
2567 so_puts(store, _("(D) Directory:\n"));
2568 so_puts(store, _(" Define an LDAP Directory server for Alpine's use. A directory server is\n"));
2569 so_puts(store, _(" similar to an address book, but it is usually maintained by an\n"));
2570 so_puts(store, _(" organization. It is similar to a telephone directory.\n"));
2573 so_puts(store, "\n");
2574 so_puts(store, _("(K) Kolor:\n"));
2575 so_puts(store, _(" Set custom colors for various parts of the Alpine screens. For example, the\n"));
2576 so_puts(store, _(" command key labels, the titlebar at the top of each page, and quoted\n"));
2577 so_puts(store, _(" sections of messages you are viewing.\n"));
2579 if(smime){
2580 so_puts(store, "\n");
2581 so_puts(store, _("(M) S/MIME:\n"));
2582 so_puts(store, _(" Setup for using S/MIME to verify signed messages, decrypt\n"));
2583 so_puts(store, _(" encrypted messages, and to sign or encrypt outgoing messages.\n"));
2586 so_puts(store, "\n");
2587 so_puts(store, _("(U) xoaUth2:\n"));
2588 so_puts(store, _(" Set client-id and client-secret to use the XOAUTH2\n"));
2589 so_puts(store, _(" authenticator.\n"));
2591 so_puts(store, "\n");
2592 so_puts(store, _("(Z) RemoteConfigSetup:\n"));
2593 so_puts(store, _(" This is a command you will probably only want to use once, if at all.\n"));
2594 so_puts(store, _(" It helps you transfer your Alpine configuration data to an IMAP server,\n"));
2595 so_puts(store, _(" where it will be accessible from any of the computers you read mail\n"));
2596 so_puts(store, _(" from (using Alpine). The idea behind a remote configuration is that you\n"));
2597 so_puts(store, _(" can change your configuration in one place and have that change show\n"));
2598 so_puts(store, _(" up on all of the computers you use.\n"));
2599 so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
2600 so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
2601 so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
2603 /* put this down here for people who don't have exceptions */
2604 if(!exc){
2605 so_puts(store, "\n");
2606 so_puts(store, _("(X) eXceptions:\n"));
2607 so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
2608 so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
2609 so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
2610 so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
2611 so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
2612 so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
2613 so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
2614 so_puts(store, _(" on a particular platform.\n"));
2615 so_puts(store, _(" (Note: this command does not do anything unless you have a configuration\n"));
2616 so_puts(store, _(" with exceptions enabled (you don't have that). Common ways to enable an\n"));
2617 so_puts(store, _(" exceptions config are the command line argument \"-x <exception_config>\";\n"));
2618 so_puts(store, _(" or the existence of the file \".pinercex\" for Unix Alpine, or \"PINERCEX\")\n"));
2619 so_puts(store, _(" for PC-Alpine.)\n"));
2620 so_puts(store, _(" (Another note: this command does not show up on the keymenu at the bottom\n"));
2621 so_puts(store, _(" of the screen unless you press \"O\" for \"Other Commands\" --but you\n"));
2622 so_puts(store, _(" don't need to press the \"O\" in order to invoke the command.)\n"));
2625 memset(&sargs, 0, sizeof(SCROLL_S));
2626 sargs.text.text = so_text(store);
2627 sargs.text.src = CharStar;
2628 sargs.text.desc = _("Information About Setup Command");
2629 sargs.bar.title = cpystr(_("SETUP"));
2630 sargs.proc.tool = choose_setup_cmd;
2631 sargs.help.text = NO_HELP;
2632 sargs.help.title = NULL;
2633 sargs.keys.menu = &choose_setup_keymenu;
2634 sargs.keys.menu->how_many = 2;
2636 setbitmap(sargs.keys.bitmap);
2637 if(!printer)
2638 clrbitn(SETUP_PRINTER, sargs.keys.bitmap);
2640 if(!passwd)
2641 clrbitn(SETUP_PASSWD, sargs.keys.bitmap);
2643 if(!config)
2644 clrbitn(SETUP_CONFIG, sargs.keys.bitmap);
2646 if(!sig)
2647 clrbitn(SETUP_SIG, sargs.keys.bitmap);
2649 if(!dir)
2650 clrbitn(SETUP_DIRECTORY, sargs.keys.bitmap);
2652 if(!smime)
2653 clrbitn(SETUP_SMIME, sargs.keys.bitmap);
2655 if(exc)
2656 menu_init_binding(sargs.keys.menu, 'x', MC_EXCEPT, "X",
2657 N_("eXceptions"), SETUP_EXCEPT);
2658 else
2659 menu_init_binding(sargs.keys.menu, 'x', MC_NO_EXCEPT, "X",
2660 N_("eXceptions"), SETUP_EXCEPT);
2663 scrolltool(&sargs);
2665 ps->mangled_screen = 1;
2667 srv = (SRV_S *)sargs.proc.data.p;
2669 exceptions = srv ? srv->exc : 0;
2671 so_give(&store);
2673 if(sargs.bar.title) fs_give((void**)&sargs.bar.title);
2674 if(srv){
2675 ret = srv->cmd;
2676 fs_give((void **)&sargs.proc.data.p);
2678 else
2679 ret = 'e';
2681 return(ret | (exceptions ? EDIT_EXCEPTION : 0));
2685 /*----------------------------------------------------------------------
2687 Args: command -- command char to perform
2689 ----*/
2690 void
2691 do_setup_task(int command)
2693 char *err = NULL;
2694 int rtype;
2695 int edit_exceptions = 0;
2696 int do_lit_sig = 0;
2698 if(command & EDIT_EXCEPTION){
2699 edit_exceptions = 1;
2700 command &= ~EDIT_EXCEPTION;
2703 switch(command) {
2704 /*----- EDIT SIGNATURE -----*/
2705 case 's':
2706 if(ps_global->VAR_LITERAL_SIG)
2707 do_lit_sig = 1;
2708 else {
2709 char sig_path[MAXPATH+1];
2711 if(!signature_path(ps_global->VAR_SIGNATURE_FILE, sig_path, MAXPATH))
2712 do_lit_sig = 1;
2713 else if((!IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
2714 && can_access(sig_path, READ_ACCESS) == 0)
2715 ||(IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
2716 && (folder_exists(NULL, sig_path) & FEX_ISFILE)))
2717 do_lit_sig = 0;
2718 else if(!ps_global->vars[V_SIGNATURE_FILE].main_user_val.p
2719 && !ps_global->vars[V_SIGNATURE_FILE].cmdline_val.p
2720 && !ps_global->vars[V_SIGNATURE_FILE].fixed_val.p)
2721 do_lit_sig = 1;
2722 else
2723 do_lit_sig = 0;
2726 if(do_lit_sig){
2727 char *result = NULL;
2728 char **apval;
2729 EditWhich ew;
2730 int readonly = 0;
2732 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
2734 if(ps_global->restricted)
2735 readonly = 1;
2736 else switch(ew){
2737 case Main:
2738 readonly = ps_global->prc->readonly;
2739 break;
2740 case Post:
2741 readonly = ps_global->post_prc->readonly;
2742 break;
2743 default:
2744 break;
2747 if(readonly)
2748 err = cpystr(ps_global->restricted
2749 ? "Alpine demo can't change config file"
2750 : _("Config file not changeable"));
2752 if(!err){
2753 apval = APVAL(&ps_global->vars[V_LITERAL_SIG], ew);
2754 if(!apval)
2755 err = cpystr(_("Problem accessing configuration"));
2756 else{
2757 char *input;
2759 input = (char *)fs_get((strlen(*apval ? *apval : "")+1) *
2760 sizeof(char));
2761 input[0] = '\0';
2762 cstring_to_string(*apval, input);
2763 err = signature_edit_lit(input, &result,
2764 _("SIGNATURE EDITOR"),
2765 h_composer_sigedit);
2766 fs_give((void **)&input);
2770 if(!err){
2771 char *cstring_version;
2773 cstring_version = string_to_cstring(result);
2775 set_variable(V_LITERAL_SIG, cstring_version, 0, 0, ew);
2777 if(cstring_version)
2778 fs_give((void **)&cstring_version);
2781 if(result)
2782 fs_give((void **)&result);
2784 else
2785 err = signature_edit(ps_global->VAR_SIGNATURE_FILE,
2786 _("SIGNATURE EDITOR"));
2788 if(err){
2789 q_status_message(SM_ORDER, 3, 4, err);
2790 fs_give((void **)&err);
2793 ps_global->mangled_screen = 1;
2794 break;
2796 /*----- ADD ADDRESSBOOK ----*/
2797 case 'a':
2798 addr_book_config(ps_global, edit_exceptions);
2799 menu_index = ABOOK_MENU_ITEM;
2800 ps_global->mangled_screen = 1;
2801 break;
2803 #ifdef ENABLE_LDAP
2804 /*--- ADD DIRECTORY SERVER --*/
2805 case 'd':
2806 directory_config(ps_global, edit_exceptions);
2807 ps_global->mangled_screen = 1;
2808 break;
2809 #endif
2811 #ifdef SMIME
2812 /*--- S/MIME --*/
2813 case 'm':
2814 smime_config_screen(ps_global, edit_exceptions);
2815 ps_global->mangled_screen = 1;
2816 break;
2817 #endif
2819 /*----- CONFIGURE OPTIONS -----*/
2820 case 'c':
2821 option_screen(ps_global, edit_exceptions);
2822 ps_global->mangled_screen = 1;
2823 break;
2825 /*----- XOAUTH2 CLIENT CONFIGURATION -----*/
2826 case 'u':
2827 alpine_xoauth2_configuration(ps_global, edit_exceptions);
2828 ps_global->mangled_screen = 1;
2829 break;
2831 /*----- COLLECTION LIST -----*/
2832 case 'l':
2833 folder_config_screen(ps_global, edit_exceptions);
2834 ps_global->mangled_screen = 1;
2835 break;
2837 /*----- RULES -----*/
2838 case 'r':
2839 rtype = rule_setup_type(ps_global, RS_RULES | RS_INCFILTNOW,
2840 _("Type of rule setup : "));
2841 switch(rtype){
2842 case 'r':
2843 case 's':
2844 case 'i':
2845 case 'f':
2846 case 'o':
2847 case 'c':
2848 role_config_screen(ps_global, (rtype == 'r') ? ROLE_DO_ROLES :
2849 (rtype == 's') ? ROLE_DO_SCORES :
2850 (rtype == 'o') ? ROLE_DO_OTHER :
2851 (rtype == 'f') ? ROLE_DO_FILTER :
2852 (rtype == 'c') ? ROLE_DO_SRCH :
2853 ROLE_DO_INCOLS,
2854 edit_exceptions);
2855 break;
2857 case 'Z':
2858 q_status_message(SM_ORDER | SM_DING, 3, 5,
2859 _("Try turning on color with the Setup/Kolor command."));
2860 break;
2862 case 'n':
2863 role_process_filters();
2864 break;
2866 default:
2867 cmd_cancelled(NULL);
2868 break;
2871 ps_global->mangled_screen = 1;
2872 break;
2874 /*----- COLOR -----*/
2875 case 'k':
2876 color_config_screen(ps_global, edit_exceptions);
2877 ps_global->mangled_screen = 1;
2878 break;
2880 case 'z':
2881 convert_to_remote_config(ps_global, edit_exceptions);
2882 ps_global->mangled_screen = 1;
2883 break;
2885 /*----- EXIT -----*/
2886 case 'e':
2887 break;
2889 /*----- NEW PASSWORD -----*/
2890 case 'n':
2891 #ifdef PASSWD_PROG
2892 if(ps_global->restricted){
2893 q_status_message(SM_ORDER, 3, 5,
2894 "Password change unavailable in restricted demo version of Alpine.");
2895 }else {
2896 change_passwd();
2897 ClearScreen();
2898 ps_global->mangled_screen = 1;
2900 #else
2901 q_status_message(SM_ORDER, 0, 5,
2902 _("Password changing not configured for this version of Alpine."));
2903 display_message('x');
2904 #endif /* DOS */
2905 break;
2907 #if !defined(DOS)
2908 /*----- CHOOSE PRINTER ------*/
2909 case 'p':
2910 select_printer(ps_global, edit_exceptions);
2911 ps_global->mangled_screen = 1;
2912 break;
2913 #endif
2919 rule_setup_type(struct pine *ps, int flags, char *prompt)
2921 ESCKEY_S opts[9];
2922 int ekey_num = 0, deefault = 0;
2924 if(flags & RS_INCADDR){
2925 deefault = 'a';
2926 opts[ekey_num].ch = 'a';
2927 opts[ekey_num].rval = 'a';
2928 opts[ekey_num].name = "A";
2929 opts[ekey_num++].label = "Addrbook";
2932 if(flags & RS_RULES){
2934 if(F_OFF(F_DISABLE_ROLES_SETUP,ps)){ /* roles are allowed */
2935 if(deefault != 'a')
2936 deefault = 'r';
2938 opts[ekey_num].ch = 'r';
2939 opts[ekey_num].rval = 'r';
2940 opts[ekey_num].name = "R";
2941 opts[ekey_num++].label = "Roles";
2943 else if(deefault != 'a')
2944 deefault = 's';
2946 opts[ekey_num].ch = 's';
2947 opts[ekey_num].rval = 's';
2948 opts[ekey_num].name = "S";
2949 opts[ekey_num++].label = "SetScores";
2951 #ifndef _WINDOWS
2952 if(ps->color_style != COL_NONE && pico_hascolor()){
2953 #endif
2954 if(deefault != 'a')
2955 deefault = 'i';
2957 opts[ekey_num].ch = 'i';
2958 opts[ekey_num].rval = 'i';
2959 opts[ekey_num].name = "I";
2960 opts[ekey_num++].label = "Indexcolor";
2961 #ifndef _WINDOWS
2963 else{
2964 opts[ekey_num].ch = 'i';
2965 opts[ekey_num].rval = 'Z'; /* notice this rval! */
2966 opts[ekey_num].name = "I";
2967 opts[ekey_num++].label = "Indexcolor";
2969 #endif
2971 opts[ekey_num].ch = 'f';
2972 opts[ekey_num].rval = 'f';
2973 opts[ekey_num].name = "F";
2974 opts[ekey_num++].label = "Filters";
2976 opts[ekey_num].ch = 'o';
2977 opts[ekey_num].rval = 'o';
2978 opts[ekey_num].name = "O";
2979 opts[ekey_num++].label = "Other";
2981 opts[ekey_num].ch = 'c';
2982 opts[ekey_num].rval = 'c';
2983 opts[ekey_num].name = "C";
2984 opts[ekey_num++].label = "searCh";
2988 if(flags & RS_INCEXP){
2989 opts[ekey_num].ch = 'e';
2990 opts[ekey_num].rval = 'e';
2991 opts[ekey_num].name = "E";
2992 opts[ekey_num++].label = "Export";
2995 if(flags & RS_INCFILTNOW){
2996 opts[ekey_num].ch = 'n';
2997 opts[ekey_num].rval = 'n';
2998 opts[ekey_num].name = "N";
2999 opts[ekey_num++].label = "filterNow";
3002 opts[ekey_num].ch = -1;
3004 return(radio_buttons(prompt, -FOOTER_ROWS(ps), opts,
3005 deefault, 'x', NO_HELP, RB_NORM));
3011 * Process the command list, changing function key notation into
3012 * lexical equivalents.
3014 void
3015 process_init_cmds(struct pine *ps, char **list)
3017 char **p;
3018 int i = 0;
3019 int j;
3020 int lpm1;
3021 #define MAX_INIT_CMDS 500
3022 /* this is just a temporary stack array, the real one is allocated below */
3023 int i_cmds[MAX_INIT_CMDS];
3024 int fkeys = 0;
3025 int not_fkeys = 0;
3027 if(list){
3028 for(p = list; *p; p++){
3029 if(i >= MAX_INIT_CMDS){
3030 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3031 "Initial keystroke list too long at \"%s\"", *p);
3032 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
3033 break;
3037 /* regular character commands */
3038 if(strlen(*p) == 1){
3039 i_cmds[i++] = **p;
3040 not_fkeys++;
3043 /* special commands */
3044 else if(strucmp(*p, "SPACE") == 0)
3045 i_cmds[i++] = ' ';
3046 else if(strucmp(*p, "CR") == 0)
3047 i_cmds[i++] = '\n';
3048 else if(strucmp(*p, "TAB") == 0)
3049 i_cmds[i++] = '\t';
3050 else if(strucmp(*p, "UP") == 0)
3051 i_cmds[i++] = KEY_UP;
3052 else if(strucmp(*p, "DOWN") == 0)
3053 i_cmds[i++] = KEY_DOWN;
3054 else if(strucmp(*p, "LEFT") == 0)
3055 i_cmds[i++] = KEY_LEFT;
3056 else if(strucmp(*p, "RIGHT") == 0)
3057 i_cmds[i++] = KEY_RIGHT;
3059 /* control chars */
3060 else if(strlen(*p) == 2 && **p == '^')
3061 i_cmds[i++] = ctrl(*((*p)+1));
3063 /* function keys */
3064 else if(**p == 'F' || **p == 'f'){
3065 int v;
3067 fkeys++;
3068 v = atoi((*p)+1);
3069 if(v >= 1 && v <= 12)
3070 i_cmds[i++] = PF1 + v - 1;
3071 else
3072 i_cmds[i++] = KEY_JUNK;
3075 /* literal string */
3076 else if(**p == '"' && (*p)[lpm1 = strlen(*p) - 1] == '"'){
3077 if(lpm1 + i - 1 > MAX_INIT_CMDS){
3078 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3079 "Initial keystroke list too long, truncated at %s\n", *p);
3080 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
3081 break; /* Bail out of this loop! */
3082 } else
3083 for(j = 1; j < lpm1; j++)
3084 i_cmds[i++] = (*p)[j];
3086 else {
3087 snprintf(tmp_20k_buf,SIZEOF_20KBUF,
3088 "Bad initial keystroke \"%.500s\" (missing comma?)", *p);
3089 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
3090 break;
3096 * We don't handle the case where function keys are used to specify the
3097 * commands but some non-function key input is also required. For example,
3098 * you might want to jump to a specific message number and view it
3099 * on start up. To do that, you need to use character commands instead
3100 * of function key commands in the initial-keystroke-list.
3102 if(fkeys && not_fkeys){
3103 init_error(ps, SM_ORDER | SM_DING, 3, 5,
3104 "Mixed characters and function keys in \"initial-keystroke-list\", skipping.");
3105 i = 0;
3108 if(fkeys && !not_fkeys)
3109 F_TURN_ON(F_USE_FK,ps);
3110 if(!fkeys && not_fkeys)
3111 F_TURN_OFF(F_USE_FK,ps);
3113 if(i > 0){
3114 ps->initial_cmds = (int *)fs_get((i+1) * sizeof(int));
3115 ps->free_initial_cmds = ps->initial_cmds;
3116 for(j = 0; j < i; j++)
3117 ps->initial_cmds[j] = i_cmds[j];
3119 ps->initial_cmds[i] = 0;
3120 ps->in_init_seq = ps->save_in_init_seq = 1;
3125 UCS *
3126 user_wordseps(char **list)
3128 char **p;
3129 int i = 0;
3130 int j;
3131 #define MAX_SEPARATORS 500
3133 * This is just a temporary stack array, the real one is allocated below.
3134 * This is supposed to be way large enough.
3136 UCS seps[MAX_SEPARATORS+1];
3137 UCS *u;
3138 UCS *return_array = NULL;
3139 size_t l;
3141 seps[0] = '\0';
3143 if(list){
3144 for(p = list; *p; p++){
3145 if(i >= MAX_SEPARATORS){
3146 q_status_message(SM_ORDER | SM_DING, 3, 3,
3147 "Warning: composer-word-separators list is too long");
3148 break;
3151 u = utf8_to_ucs4_cpystr(*p);
3153 if(u){
3154 if(ucs4_strlen(u) == 1)
3155 seps[i++] = *u;
3156 else if(*u == '"' && u[l = ucs4_strlen(u) - 1] == '"'){
3157 if(l + i - 1 > MAX_SEPARATORS){
3158 q_status_message(SM_ORDER | SM_DING, 3, 3,
3159 "Warning: composer-word-separators list is too long");
3160 break; /* Bail out of this loop! */
3162 else{
3163 for(j = 1; j < l; j++)
3164 seps[i++] = u[j];
3167 else{
3168 l = ucs4_strlen(u);
3169 if(l + i > MAX_SEPARATORS){
3170 q_status_message(SM_ORDER | SM_DING, 3, 3,
3171 "Warning: composer-word-separators list is too long");
3172 break; /* Bail out of this loop! */
3174 else{
3175 for(j = 0; j < l; j++)
3176 seps[i++] = u[j];
3180 fs_give((void **) &u);
3185 seps[i] = '\0';
3187 if(i > 0)
3188 return_array = ucs4_cpystr(seps);
3190 return(return_array);
3195 * Make sure any errors during initialization get queued for display
3197 void
3198 queue_init_errors(struct pine *ps)
3200 int i;
3202 if(ps->init_errs){
3203 for(i = 0; (ps->init_errs)[i].message; i++){
3204 q_status_message((ps->init_errs)[i].flags,
3205 (ps->init_errs)[i].min_time,
3206 (ps->init_errs)[i].max_time,
3207 (ps->init_errs)[i].message);
3208 fs_give((void **)&(ps->init_errs)[i].message);
3211 fs_give((void **)&ps->init_errs);
3216 /*----------------------------------------------------------------------
3217 Quit pine if the user wants to
3219 Args: The usual pine structure
3221 Result: User is asked if she wants to quit, if yes then execute quit.
3223 Q U I T S C R E E N
3225 Not really a full screen. Just count up deletions and ask if we really
3226 want to quit.
3227 ----*/
3228 void
3229 quit_screen(struct pine *pine_state)
3231 int quit = 0;
3233 dprint((1, "\n\n ---- QUIT SCREEN ----\n"));
3235 if(F_ON(F_CHECK_MAIL_ONQUIT,ps_global)
3236 && pine_state->mail_stream != NULL
3237 && new_mail(1, VeryBadTime, NM_STATUS_MSG | NM_DEFER_SORT) > 0){
3238 char *prompt = NULL;
3239 if(sp_mail_since_cmd(pine_state->mail_stream) == 1)
3240 prompt = cpystr(_("Quit even though 1 new mail just arrived"));
3241 else if(sp_mail_since_cmd(pine_state->mail_stream) > 1){
3242 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Quit even though %lu new messages just arrived"), sp_mail_since_cmd(pine_state->mail_stream));
3243 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3244 prompt = cpystr(tmp_20k_buf);
3246 else prompt = cpystr(_("Quit even though new mail just arrived"));
3247 quit = want_to(prompt, 'y', 0, NO_HELP, WT_NORM | WT_DING);
3248 if(prompt)
3249 fs_give((void **) &prompt);
3250 if(quit != 'y'){
3251 refresh_sort(pine_state->mail_stream, pine_state->msgmap, SRT_VRB);
3252 pine_state->next_screen = pine_state->prev_screen;
3253 return;
3257 if(quit != 'y'
3258 && F_OFF(F_QUIT_WO_CONFIRM,pine_state)
3259 && want_to(_("Really quit Alpine"), 'y', 0, NO_HELP, WT_NORM) != 'y'){
3260 pine_state->next_screen = pine_state->prev_screen;
3261 return;
3264 goodnight_gracey(pine_state, 0);
3268 /*----------------------------------------------------------------------
3269 The nuts and bolts of actually cleaning up and exiting pine
3271 Args: ps -- the usual pine structure,
3272 exit_val -- what to tell our parent
3274 Result: This never returns
3276 ----*/
3277 void
3278 goodnight_gracey(struct pine *pine_state, int exit_val)
3280 int i, cnt_user_streams = 0;
3281 char *final_msg = NULL;
3282 char msg[MAX_SCREEN_COLS+1];
3283 char *pf = _("Alpine finished");
3284 MAILSTREAM *m;
3285 extern KBESC_T *kbesc;
3287 dprint((2, "goodnight_gracey:\n"));
3289 /* We want to do this here before we close up the streams */
3290 trim_remote_adrbks();
3292 for(i = 0; i < ps_global->s_pool.nstream; i++){
3293 m = ps_global->s_pool.streams[i];
3294 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR))
3295 cnt_user_streams++;
3298 /* clean up open streams */
3300 if(pine_state->mail_stream
3301 && sp_flagged(pine_state->mail_stream, SP_LOCKED)
3302 && sp_flagged(pine_state->mail_stream, SP_USERFLDR)){
3303 dprint((5, "goodnight_gracey: close current stream\n"));
3304 expunge_and_close(pine_state->mail_stream,
3305 (cnt_user_streams <= 1) ? &final_msg : NULL, EC_NONE);
3306 cnt_user_streams--;
3309 pine_state->mail_stream = NULL;
3310 pine_state->redrawer = (void (*)(void))NULL;
3312 dprint((5,
3313 "goodnight_gracey: close other stream pool streams\n"));
3314 for(i = 0; i < ps_global->s_pool.nstream; i++){
3315 m = ps_global->s_pool.streams[i];
3317 * fix global for functions that depend(ed) on it sort_folder.
3318 * Hopefully those will get phased out.
3320 ps_global->mail_stream = m;
3321 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
3322 && !sp_flagged(m, SP_INBOX)){
3323 sp_set_expunge_count(m, 0L);
3324 expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
3325 EC_NONE);
3326 cnt_user_streams--;
3330 for(i = 0; i < ps_global->s_pool.nstream; i++){
3331 m = ps_global->s_pool.streams[i];
3333 * fix global for functions that depend(ed) on it (sort_folder).
3334 * Hopefully those will get phased out.
3336 ps_global->mail_stream = m;
3337 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
3338 && sp_flagged(m, SP_INBOX)){
3339 dprint((5,
3340 "goodnight_gracey: close inbox stream stream\n"));
3341 sp_set_expunge_count(m, 0L);
3342 expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
3343 EC_NONE);
3344 cnt_user_streams--;
3348 #ifdef _WINDOWS
3349 if(ps_global->ttyo)
3350 (void)get_windsize(ps_global->ttyo);
3351 #endif
3353 dprint((7, "goodnight_gracey: close config files\n"));
3355 free_pinerc_strings(&pine_state);
3357 strncpy(msg, pf, sizeof(msg));
3358 msg[sizeof(msg)-1] = '\0';
3359 if(final_msg){
3360 strncat(msg, " -- ", sizeof(msg)-strlen(msg)-1);
3361 msg[sizeof(msg)-1] = '\0';
3362 strncat(msg, final_msg, sizeof(msg)-strlen(msg)-1);
3363 msg[sizeof(msg)-1] = '\0';
3364 fs_give((void **)&final_msg);
3367 dprint((7, "goodnight_gracey: sp_end\n"));
3368 ps_global->noshow_error = 1;
3369 sp_end();
3371 #ifdef SMIME
3372 smime_deinit();
3373 #endif
3375 /* after sp_end, which might call a filter */
3376 completely_done_with_adrbks();
3378 dprint((7, "goodnight_gracey: end_screen\n"));
3379 end_screen(msg, exit_val);
3380 dprint((7, "goodnight_gracey: end_titlebar\n"));
3381 end_titlebar();
3382 dprint((7, "goodnight_gracey: end_keymenu\n"));
3383 end_keymenu();
3385 dprint((7, "goodnight_gracey: end_keyboard\n"));
3386 end_keyboard(F_ON(F_USE_FK,pine_state));
3387 dprint((7, "goodnight_gracey: end_ttydriver\n"));
3388 end_tty_driver(pine_state);
3389 #if !defined(DOS) && !defined(OS2)
3390 kbdestroy(kbesc);
3391 #if !defined(LEAVEOUTFIFO)
3392 close_newmailfifo();
3393 #endif
3394 #endif
3395 end_signals(0);
3396 if(filter_data_file(0))
3397 our_unlink(filter_data_file(0));
3399 imap_flush_passwd_cache(TRUE);
3400 free_newsgrp_cache();
3401 mailcap_free();
3402 close_every_pattern();
3403 free_extra_hdrs();
3404 free_contexts(&ps_global->context_list);
3405 free_charsetchecker();
3406 dprint((7, "goodnight_gracey: free more memory\n"));
3407 #ifdef ENABLE_LDAP
3408 free_saved_query_parameters();
3409 #endif
3411 html_dir_clean(1); /* force remove of remaining files */
3412 free_pine_struct(&pine_state);
3414 free_histlist();
3416 free_alpine_module_globals(); /* should we have module globals? */
3417 free_pith_module_globals();
3418 free_pico_module_globals();
3419 free_c_client_module_globals();
3421 #ifdef DEBUG
3422 if(debugfile){
3423 if(debug >= 2)
3424 fputs("goodnight_gracey finished\n", debugfile);
3426 fclose(debugfile);
3428 #endif
3430 exit(exit_val);
3434 /*----------------------------------------------------------------------
3435 Call back for c-client to feed us back the progress of network reads
3437 Input:
3439 Result:
3440 ----*/
3441 void
3442 pine_read_progress(GETS_DATA *md, long unsigned int count)
3444 gets_bytes += count; /* update counter */
3448 /*----------------------------------------------------------------------
3449 Function to fish the current byte count from a c-client fetch.
3451 Input: reset -- flag telling us to reset the count
3453 Result: Returns the number of bytes read by the c-client so far
3454 ----*/
3455 unsigned long
3456 pine_gets_bytes(int reset)
3458 if(reset)
3459 gets_bytes = 0L;
3461 return(gets_bytes);
3465 /*----------------------------------------------------------------------
3466 Panic pine - call on detected programmatic errors to exit pine
3468 Args: message -- message to record in debug file and to be printed for user
3470 Result: The various tty modes are restored
3471 If debugging is active a core dump will be generated
3472 Exits Alpine
3474 This is also called from imap routines and fs_get and fs_resize.
3475 ----*/
3476 void
3477 alpine_panic(char *message)
3479 char buf[256];
3481 /* global variable in .../pico/edef.h */
3482 in_panic = 1;
3484 if(ps_global->ttyo){
3485 end_screen(NULL, -1);
3486 end_keyboard(ps_global != NULL ? F_ON(F_USE_FK,ps_global) : 0);
3487 end_tty_driver(ps_global);
3488 end_signals(1);
3490 if(filter_data_file(0))
3491 our_unlink(filter_data_file(0));
3493 dprint((1, "\n===========================================\n\n"));
3494 dprint((1, " Alpine Panic: %s\n\n", message ? message : "?"));
3495 dprint((1, "===========================================\n\n"));
3497 /* intercept c-client "free storage" errors */
3498 if(strstr(message, "free storage"))
3499 snprintf(buf, sizeof(buf), _("No more available memory.\nAlpine Exiting"));
3500 else
3501 snprintf(buf, sizeof(buf), _("Problem detected: \"%s\".\nAlpine Exiting."), message);
3503 buf[sizeof(buf)-1] = '\0';
3505 #ifdef _WINDOWS
3506 /* Put up a message box. */
3507 mswin_messagebox (buf, 1);
3508 #else
3509 fprintf(stderr, "\n\n%s\n", buf);
3510 #endif
3512 #ifdef DEBUG
3513 if(debugfile){
3514 save_debug_on_crash(debugfile, recent_keystroke);
3517 coredump(); /*--- If we're debugging get a core dump --*/
3518 #endif
3520 exit(-1);
3521 fatal("ffo"); /* BUG -- hack to get fatal out of library in right order*/
3526 * panicking - function to test whether or not we're exiting under stress.
3530 panicking(void)
3532 return(in_panic);
3536 /*----------------------------------------------------------------------
3537 exceptional_exit - called to exit under unusual conditions (with no core)
3539 Args: message -- message to record in debug file and to be printed for user
3540 ev -- exit value
3542 ----*/
3543 void
3544 exceptional_exit(char *message, int ev)
3546 fprintf(stderr, "%s\n", message);
3547 exit(ev);
3552 * PicoText Storage Object Support Routines
3555 STORE_S *
3556 pine_pico_get(void)
3558 return((STORE_S *)pico_get());
3562 pine_pico_give(STORE_S **sop)
3564 pico_give((void *)sop);
3565 return(1);
3569 pine_pico_writec(int c, STORE_S *so)
3571 unsigned char ch = (unsigned char) c;
3573 return(pico_writec(so->txt, ch, PICOREADC_NONE));
3577 pine_pico_writec_noucs(int c, STORE_S *so)
3579 unsigned char ch = (unsigned char) c;
3581 return(pico_writec(so->txt, ch, PICOREADC_NOUCS));
3585 pine_pico_readc(unsigned char *c, STORE_S *so)
3587 return(pico_readc(so->txt, c, PICOREADC_NONE));
3591 pine_pico_readc_noucs(unsigned char *c, STORE_S *so)
3593 return(pico_readc(so->txt, c, PICOREADC_NOUCS));
3597 pine_pico_puts(STORE_S *so, char *s)
3599 return(pico_puts(so->txt, s, PICOREADC_NONE));
3603 pine_pico_puts_noucs(STORE_S *so, char *s)
3605 return(pico_puts(so->txt, s, PICOREADC_NOUCS));
3609 pine_pico_seek(STORE_S *so, long pos, int orig)
3611 return(pico_seek((void *)so, pos, orig));
3616 remote_pinerc_failure(void)
3618 #ifdef _WINDOWS
3619 if(ps_global->install_flag) /* just exit silently */
3620 exit(0);
3621 #endif /* _WINDOWS */
3623 if(ps_global->exit_if_no_pinerc){
3624 exceptional_exit("Exiting because -bail option is set and config file not readable.", -1);
3627 if(want_to("Trouble reading remote configuration! Continue anyway ",
3628 'n', 'n', NO_HELP, WT_FLUSH_IN) != 'y'){
3629 return(0);
3632 return(1);
3636 void
3637 dump_supported_options(void)
3639 char **config;
3641 config = get_supported_options();
3642 if(config){
3643 display_args_err(NULL, config, 0);
3644 free_list_array(&config);
3649 /*----------------------------------------------------------------------
3650 Check pruned-folders for validity, making sure they are in the
3651 same context as sent-mail.
3653 ----*/
3655 prune_folders_ok(void)
3657 char **p;
3659 for(p = ps_global->VAR_PRUNED_FOLDERS; p && *p && **p; p++)
3660 if(!context_isambig(*p))
3661 return(0);
3663 return(1);
3666 void
3667 free_alpine_module_globals(void)
3669 free_message_queue();
3670 free_titlebar_globals();
3673 #ifdef WIN32
3674 char *
3675 pine_user_callback()
3677 if(ps_global->VAR_USER_ID && ps_global->VAR_USER_ID[0]){
3678 return(ps_global->VAR_USER_ID);
3680 else{
3681 /* SHOULD PROMPT HERE! */
3682 return(NULL);
3685 #endif
3687 #ifdef _WINDOWS
3689 * windows callback to get/set function keys mode state
3692 fkey_mode_callback(set, args)
3693 int set;
3694 long args;
3696 return(F_ON(F_USE_FK, ps_global) != 0);
3700 void
3701 imap_telemetry_on()
3703 if(ps_global->mail_stream)
3704 mail_debug(ps_global->mail_stream);
3708 void
3709 imap_telemetry_off()
3711 if(ps_global->mail_stream)
3712 mail_nodebug(ps_global->mail_stream);
3716 char *
3717 pcpine_help_main(title)
3718 char *title;
3720 if(title)
3721 strncpy(title, _("PC-Alpine MAIN MENU Help"), 256);
3723 return(pcpine_help(main_menu_tx));
3728 pcpine_main_cursor(col, row)
3729 int col;
3730 long row;
3732 unsigned ndmi;
3734 if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global)))
3735 ndmi = (row+1 - HEADER_ROWS(ps_global) - (MNSKIP(ps_global)+1))/(MNSKIP(ps_global)+1);
3737 if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global))
3738 && !(MNSKIP(ps_global) && (row+1) & 0x01)
3739 && ndmi <= MAX_MENU_ITEM
3740 && FOOTER_ROWS(ps_global) + (ndmi+1)*(MNSKIP(ps_global)+1)
3741 + MNSKIP(ps_global) + FOOTER_ROWS(ps_global) <= ps_global->ttyo->screen_rows)
3742 return(MSWIN_CURSOR_HAND);
3743 else
3744 return(MSWIN_CURSOR_ARROW);
3746 #endif /* _WINDOWS */