* new version 2.20.4
[alpine.git] / alpine / alpine.c
blobb444c17e96a60a65d99a5ad6a510e43433b8c116
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: alpine.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2015 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "headers.h"
21 #include "../pith/newmail.h"
22 #include "../pith/init.h"
23 #include "../pith/sort.h"
24 #include "../pith/options.h"
25 #include "../pith/list.h"
26 #include "../pith/conf.h"
28 #include "osdep/debuging.h"
29 #include "osdep/termout.gen.h"
30 #include "osdep/chnge_pw.h"
32 #include "alpine.h"
33 #include "mailindx.h"
34 #include "mailcmd.h"
35 #include "addrbook.h"
36 #include "reply.h"
37 #include "arg.h"
38 #include "keymenu.h"
39 #include "status.h"
40 #include "context.h"
41 #include "mailview.h"
42 #include "imap.h"
43 #include "radio.h"
44 #include "folder.h"
45 #include "send.h"
46 #include "help.h"
47 #include "titlebar.h"
48 #include "takeaddr.h"
49 #include "dispfilt.h"
50 #include "init.h"
51 #include "remote.h"
52 #include "pattern.h"
53 #include "setup.h"
54 #include "newuser.h"
55 #include "adrbkcmd.h"
56 #include "signal.h"
57 #include "kblock.h"
58 #include "ldapconf.h"
59 #include "roleconf.h"
60 #include "colorconf.h"
61 #include "print.h"
62 #include "after.h"
63 #include "smime.h"
64 #include "newmail.h"
65 #ifndef _WINDOWS
66 #include "../pico/osdep/raw.h" /* for STD*_FD */
67 #endif
70 #define PIPED_FD 5 /* Some innocuous desc */
73 /* look for my_timer_period in pico directory for an explanation */
74 int my_timer_period = ((IDLE_TIMEOUT + 1)*1000);
76 /* byte count used by our gets routine to keep track */
77 static unsigned long gets_bytes;
81 * Internal prototypes
83 void convert_args_to_utf8(struct pine *, ARGDATA_S *);
84 void preopen_stayopen_folders(void);
85 int read_stdin_char(char *);
86 void main_redrawer(void);
87 void show_main_screen(struct pine *, int, OtherMenu, struct key_menu *, int, Pos *);
88 void do_menu(int, Pos *, struct key_menu *);
89 int choose_setup_cmd(int, MSGNO_S *, SCROLL_S *);
90 int setup_menu(struct pine *);
91 void do_setup_task(int);
92 void queue_init_errors(struct pine *);
93 void process_init_cmds(struct pine *, char **);
94 void goodnight_gracey(struct pine *, int);
95 void pine_read_progress(GETS_DATA *, unsigned long);
96 int remote_pinerc_failure(void);
97 void dump_supported_options(void);
98 int prune_folders_ok(void);
99 #ifdef WIN32
100 char *pine_user_callback(void);
101 #endif
102 #ifdef _WINDOWS
103 int fkey_mode_callback(int, long);
104 void imap_telemetry_on(void);
105 void imap_telemetry_off(void);
106 char *pcpine_help_main(char *);
107 int pcpine_main_cursor(int, long);
108 #define main app_main
109 #endif
112 typedef struct setup_return_val {
113 int cmd;
114 int exc;
115 }SRV_S;
119 * strlen of longest label from keymenu, of labels corresponding to
120 * commands in the middle of the screen. 9 is length of ListFldrs
122 #define LONGEST_LABEL 9 /* length of longest label from keymenu */
124 #define EDIT_EXCEPTION (0x100)
127 static int in_panic = 0;
130 /*----------------------------------------------------------------------
131 main routine -- entry point
133 Args: argv, argc -- The command line arguments
136 Initialize pine, parse arguments and so on
138 If there is a user address on the command line go into send mode and exit,
139 otherwise loop executing the various screens in Alpine.
141 NOTE: The Windows port def's this to "app_main"
142 ----*/
145 main(int argc, char **argv)
147 ARGDATA_S args;
148 int rv;
149 long rvl;
150 struct pine *pine_state;
151 gf_io_t stdin_getc = NULL;
152 char *args_for_debug = NULL, *init_pinerc_debugging = NULL;
154 /*----------------------------------------------------------------------
155 Set up buffering and some data structures
156 ----------------------------------------------------------------------*/
158 pine_state = new_pine_struct();
159 ps_global = pine_state;
162 * fill in optional pith-offered behavior hooks
164 pith_opt_read_msg_prompt = read_msg_prompt;
165 pith_opt_paint_index_hline = paint_index_hline;
166 pith_opt_rfc2369_editorial = rfc2369_editorial;
167 pith_opt_condense_thread_cue = condensed_thread_cue;
168 pith_opt_truncate_sfstr = truncate_subj_and_from_strings;
169 pith_opt_save_and_restore = save_and_restore;
170 pith_opt_newmail_announce = newmail_status_message;
171 pith_opt_newmail_check_cue = newmail_check_cue;
172 pith_opt_checkpoint_cue = newmail_check_point_cue;
173 pith_opt_icon_text = icon_text;
174 pith_opt_rd_metadata_name = rd_metadata_name;
175 pith_opt_remote_pinerc_failure = remote_pinerc_failure;
176 pith_opt_reopen_folder = ask_mailbox_reopen;
177 pith_opt_expunge_prompt = expunge_prompt;
178 pith_opt_begin_closing = expunge_and_close_begins;
179 pith_opt_replyto_prompt = reply_using_replyto_query;
180 pith_opt_reply_to_all_prompt = reply_to_all_query;
181 pith_opt_save_create_prompt = create_for_save_prompt;
182 pith_opt_daemon_confirm = confirm_daemon_send;
183 pith_opt_save_size_changed_prompt = save_size_changed_prompt;
184 pith_opt_save_index_state = setup_index_state;
185 pith_opt_filter_pattern_cmd = pattern_filter_command;
186 pith_opt_get_signature_file = get_signature_file;
187 pith_opt_pretty_var_name = pretty_var_name;
188 pith_opt_pretty_feature_name = pretty_feature_name;
189 pith_opt_closing_stream = titlebar_stream_closing;
190 pith_opt_current_expunged = mm_expunged_current;
191 #ifdef SMIME
192 pith_opt_smime_get_passphrase = smime_get_passphrase;
193 pith_smime_import_certificate = smime_import_certificate;
194 pith_smime_enter_password = alpine_get_password;
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 && args.action == aaURL){
271 display_args_err(
272 "Cannot read stdin when using -url\nFor mailto URLs, use \'body=\' instead",
273 NULL, 1);
274 args_help();
275 exit(-1);
279 #else /* _WINDOWS */
281 * We now have enough information to do some of the basic registry settings.
283 if(ps_global->update_registry != UREG_NEVER_SET){
284 mswin_reg(MSWR_OP_SET
285 | ((ps_global->update_registry == UREG_ALWAYS_SET)
286 ? MSWR_OP_FORCE : 0),
287 MSWR_PINE_DIR, ps_global->pine_dir, (size_t)NULL);
288 mswin_reg(MSWR_OP_SET
289 | ((ps_global->update_registry == UREG_ALWAYS_SET)
290 ? MSWR_OP_FORCE : 0),
291 MSWR_PINE_EXE, ps_global->pine_name, (size_t)NULL);
294 #endif /* _WINDOWS */
296 if(ps_global->convert_sigs &&
297 (!ps_global->pinerc || !ps_global->pinerc[0])){
298 fprintf(stderr, "Use -p <pinerc> with -convert_sigs\n");
299 exit(-1);
302 mail_parameters(NULL, SET_UCS4WIDTH, (void *) pith_ucs4width);
303 mail_parameters(NULL, SET_QUOTA, (void *) pine_parse_quota);
304 /* set some default timeouts in case pinerc is remote */
305 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)30);
306 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)15);
307 mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
308 /* could be TO_BAIL_THRESHOLD, 15 seems more appropriate for now */
309 pine_state->tcp_query_timeout = 15;
311 mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
312 mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
313 mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
314 #ifdef SMIME
315 mail_parameters(NULL, SET_FREEBODYSPAREP, (void *) free_smime_body_sparep);
316 #endif
318 init_pinerc(pine_state, &init_pinerc_debugging);
320 #ifdef DEBUG
321 /* Since this is specific debugging we don't mind if the
322 ifdef is the type of system.
324 #if defined(HAVE_SMALLOC) || defined(NXT)
325 if(ps_global->debug_malloc)
326 malloc_debug(ps_global->debug_malloc);
327 #endif
328 #ifdef CSRIMALLOC
329 if(ps_global->debug_malloc)
330 mal_debug(ps_global->debug_malloc);
331 #endif
333 if(!ps_global->convert_sigs
334 #ifdef _WINDOWS
335 && !ps_global->install_flag
336 #endif /* _WINDOWS */
338 init_debug();
340 if(args_for_debug){
341 dprint((0, " %s (PID=%ld)\n\n", args_for_debug,
342 (long) getpid()));
343 fs_give((void **)&args_for_debug);
347 char *env_to_free;
348 if((env_to_free = our_getenv("HOME")) != NULL){
349 dprint((2, "Setting home dir from $HOME: \"%s\"\n",
350 env_to_free));
351 fs_give((void **)&env_to_free);
353 else{
354 dprint((2, "Setting home dir: \"%s\"\n",
355 pine_state->home_dir ? pine_state->home_dir : "<?>"));
359 /* Watch out. Sensitive information in debug file. */
360 if(ps_global->debug_imap > 4)
361 mail_parameters(NULL, SET_DEBUGSENSITIVE, (void *) TRUE);
363 #ifndef DEBUGJOURNAL
364 if(ps_global->debug_tcp)
365 #endif
366 mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
368 #ifdef _WINDOWS
369 mswin_setdebug(debug, debugfile);
370 mswin_setdebugoncallback (imap_telemetry_on);
371 mswin_setdebugoffcallback (imap_telemetry_off);
372 mswin_enableimaptelemetry(ps_global->debug_imap != 0);
373 #endif
374 #endif /* DEBUG */
376 #ifdef _WINDOWS
377 mswin_setsortcallback(index_sort_callback);
378 mswin_setflagcallback(flag_callback);
379 mswin_sethdrmodecallback(header_mode_callback);
380 mswin_setselectedcallback(any_selected_callback);
381 mswin_setzoomodecallback(zoom_mode_callback);
382 mswin_setfkeymodecallback(fkey_mode_callback);
383 #endif
385 /*------- Set up c-client drivers -------*/
386 #include "../c-client/linkage.c"
388 /*------- ... then tune the drivers just installed -------*/
389 #ifdef _WINDOWS
390 if(_tgetenv(TEXT("HOME")))
391 mail_parameters(NULL, SET_HOMEDIR, (void *) pine_state->home_dir);
393 mail_parameters(NULL, SET_USERPROMPT, (void *) pine_user_callback);
396 * Sniff the environment for timezone offset. We need to do this
397 * here since Windows needs help figuring out UTC, and will adjust
398 * what time() returns based on TZ. THIS WILL SCREW US because
399 * we use time() differences to manage status messages. So, if
400 * rfc822_date, which calls localtime() and thus needs tzset(),
401 * is called while a status message is displayed, it's possible
402 * for time() to return a time *before* what we remember as the
403 * time we put the status message on the display. Sheesh.
405 tzset();
406 #else /* !_WINDOWS */
408 * We used to let c-client do this for us automatically, but it declines
409 * to do so for root. This forces c-client to establish an environment,
410 * even if the uid is 0.
412 env_init(ps_global->ui.login, ps_global->ui.homedir);
415 * Install callback to let us know the progress of network reads...
417 (void) mail_parameters(NULL, SET_READPROGRESS, (void *)pine_read_progress);
418 #endif /* !_WINDOWS */
421 * Install callback to handle certificate validation failures,
422 * allowing the user to continue if they wish.
424 mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) pine_sslcertquery);
425 mail_parameters(NULL, SET_SSLFAILURE, (void *) pine_sslfailure);
427 if(init_pinerc_debugging){
428 dprint((2, init_pinerc_debugging));
429 fs_give((void **)&init_pinerc_debugging);
433 * Initial allocation of array of stream pool pointers.
434 * We do this before init_vars so that we can re-use streams used for
435 * remote config files. These sizes may get changed later.
437 ps_global->s_pool.max_remstream = 2;
438 dprint((9,
439 "Setting initial max_remstream to %d for remote config re-use\n",
440 ps_global->s_pool.max_remstream));
442 init_vars(pine_state, process_init_cmds);
444 #ifdef SMIME
445 if(F_ON(F_DONT_DO_SMIME, ps_global))
446 smime_deinit();
447 #endif /* SMIME */
449 #ifdef ENABLE_NLS
451 * LC_CTYPE is already set from the set_collation call above.
453 * We can't use gettext calls before we do this stuff so it doesn't
454 * help to translate strings that come before this in the program.
455 * Maybe we could rearrange things to accomodate that.
457 setlocale(LC_MESSAGES, "");
458 bindtextdomain(PACKAGE, LOCALEDIR);
459 bind_textdomain_codeset(PACKAGE, "UTF-8");
460 textdomain(PACKAGE);
461 #endif /* ENABLE_NLS */
463 convert_args_to_utf8(pine_state, &args);
465 if(args.action == aaFolder){
466 pine_state->beginning_of_month = first_run_of_month();
467 pine_state->beginning_of_year = first_run_of_year();
470 /* Set up optional for user-defined display filtering */
471 pine_state->tools.display_filter = dfilter;
472 pine_state->tools.display_filter_trigger = dfilter_trigger;
474 #ifdef _WINDOWS
475 if(ps_global->install_flag){
476 init_install_get_vars();
478 if(ps_global->prc)
479 free_pinerc_s(&ps_global->prc);
481 exit(0);
483 #endif
485 if(ps_global->convert_sigs){
486 if(convert_sigs_to_literal(ps_global, 0) == -1){
487 /* TRANSLATORS: sigs refers to signatures, which the user was trying to convert */
488 fprintf(stderr, _("trouble converting sigs\n"));
489 exit(-1);
492 if(ps_global->prc){
493 if(ps_global->prc->outstanding_pinerc_changes)
494 write_pinerc(ps_global, Main, WRP_NONE);
496 free_pinerc_s(&pine_state->prc);
499 exit(0);
503 * Set up a c-client read timeout and timeout handler. In general,
504 * it shouldn't happen, but a server crash or dead link can cause
505 * pine to appear wedged if we don't set this up...
507 rv = 30;
508 if(pine_state->VAR_TCPOPENTIMEO)
509 (void)SVAR_TCP_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
510 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)rv);
512 rv = 15;
513 if(pine_state->VAR_TCPREADWARNTIMEO)
514 (void)SVAR_TCP_READWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
515 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)rv);
517 rv = 0;
518 if(pine_state->VAR_TCPWRITEWARNTIMEO){
519 if(!SVAR_TCP_WRITEWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
520 if(rv == 0 || rv > 4) /* making sure */
521 mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long)rv);
524 mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
526 rv = 15;
527 if(pine_state->VAR_RSHOPENTIMEO){
528 if(!SVAR_RSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
529 if(rv == 0 || rv > 4) /* making sure */
530 mail_parameters(NULL, SET_RSHTIMEOUT, (void *)(long)rv);
533 rv = 15;
534 if(pine_state->VAR_SSHOPENTIMEO){
535 if(!SVAR_SSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
536 if(rv == 0 || rv > 4) /* making sure */
537 mail_parameters(NULL, SET_SSHTIMEOUT, (void *)(long)rv);
540 rvl = 60L;
541 if(pine_state->VAR_MAILDROPCHECK){
542 if(!SVAR_MAILDCHK(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF)){
543 if(rvl == 0L)
544 rvl = (60L * 60L * 24L * 100L); /* 100 days */
546 if(rvl >= 60L) /* making sure */
547 mail_parameters(NULL, SET_SNARFINTERVAL, (void *) rvl);
552 * Lookups of long login names which don't exist are very slow in aix.
553 * This would normally get set in system-wide config if not needed.
555 if(F_ON(F_DISABLE_SHARED_NAMESPACES, ps_global))
556 mail_parameters(NULL, SET_DISABLEAUTOSHAREDNS, (void *) TRUE);
558 if(F_ON(F_HIDE_NNTP_PATH, ps_global))
559 mail_parameters(NULL, SET_NNTPHIDEPATH, (void *) TRUE);
561 if(F_ON(F_MAILDROPS_PRESERVE_STATE, ps_global))
562 mail_parameters(NULL, SET_SNARFPRESERVE, (void *) TRUE);
564 rvl = 0L;
565 if(pine_state->VAR_NNTPRANGE){
566 if(!SVAR_NNTPRANGE(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF))
567 if(rvl > 0L)
568 mail_parameters(NULL, SET_NNTPRANGE, (void *) rvl);
572 * Tell c-client not to be so aggressive about uid mappings
574 mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20);
577 * Setup referral handling
579 mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral);
580 mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy);
583 * Setup multiple newsrc transition
585 mail_parameters(NULL, SET_NEWSRCQUERY, (void *) pine_newsrcquery);
588 * Disable some drivers if requested.
590 if(ps_global->VAR_DISABLE_DRIVERS &&
591 ps_global->VAR_DISABLE_DRIVERS[0] &&
592 ps_global->VAR_DISABLE_DRIVERS[0][0]){
593 char **t;
595 for(t = ps_global->VAR_DISABLE_DRIVERS; t[0] && t[0][0]; t++)
596 if(mail_parameters(NULL, DISABLE_DRIVER, (void *)(*t))){
597 dprint((2, "Disabled mail driver \"%s\"\n", *t));
599 else{
600 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
601 _("Failed to disable mail driver \"%s\": name not found"),
602 *t);
603 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
604 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
609 * Disable some authenticators if requested.
611 if(ps_global->VAR_DISABLE_AUTHS &&
612 ps_global->VAR_DISABLE_AUTHS[0] &&
613 ps_global->VAR_DISABLE_AUTHS[0][0]){
614 char **t;
616 for(t = ps_global->VAR_DISABLE_AUTHS; t[0] && t[0][0]; t++)
617 if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *)(*t))){
618 dprint((2,"Disabled SASL authenticator \"%s\"\n", *t));
620 else{
621 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
622 _("Failed to disable SASL authenticator \"%s\": name not found"),
623 *t);
624 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
625 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
630 * setup alternative authentication driver preference for IMAP opens
632 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
633 mail_parameters(NULL, SET_IMAPTRYALT, (void *) TRUE);
636 * Install handler to let us know about potential delays
638 (void) mail_parameters(NULL, SET_BLOCKNOTIFY, (void *) pine_block_notify);
640 if(ps_global->dump_supported_options){
641 dump_supported_options();
642 exit(0);
646 * Install extra headers to fetch along with all the other stuff
647 * mail_fetch_structure and mail_fetch_overview requests.
649 calc_extra_hdrs();
650 if(get_extra_hdrs())
651 (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
652 (void *) get_extra_hdrs());
654 if(init_username(pine_state) < 0){
655 fprintf(stderr, _("Who are you? (Unable to look up login name)\n"));
656 exit(-1);
659 if(init_userdir(pine_state) < 0)
660 exit(-1);
662 if(init_hostname(pine_state) < 0)
663 exit(-1);
666 * Verify mail dir if we're not in send only mode...
668 if(args.action == aaFolder && init_mail_dir(pine_state) < 0)
669 exit(-1);
671 init_signals();
673 /*--- input side ---*/
674 if(init_tty_driver(pine_state)){
675 #ifndef _WINDOWS /* always succeeds under _WINDOWS */
676 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);
677 exit(-1);
678 #endif /* !_WINDOWS */
682 /*--- output side ---*/
683 rv = config_screen(&(pine_state->ttyo));
684 #ifndef _WINDOWS /* always succeeds under _WINDOWS */
685 if(rv){
686 switch(rv){
687 case -1:
688 printf(_("Terminal type (environment variable TERM) not set.\n"));
689 break;
690 case -2:
691 printf(_("Terminal type \"%s\" is unknown.\n"), getenv("TERM"));
692 break;
693 case -3:
694 printf(_("Can't open terminal capabilities database.\n"));
695 break;
696 case -4:
697 printf(_("Your terminal, of type \"%s\", is lacking functions needed to run alpine.\n"), getenv("TERM"));
698 break;
701 printf("\r");
702 end_tty_driver(pine_state);
703 exit(-1);
705 #endif /* !_WINDOWS */
707 if(F_ON(F_BLANK_KEYMENU,ps_global))
708 FOOTER_ROWS(ps_global) = 1;
710 init_screen();
711 init_keyboard(pine_state->orig_use_fkeys);
712 strncpy(pine_state->inbox_name, INBOX_NAME,
713 sizeof(pine_state->inbox_name)-1);
714 init_folders(pine_state); /* digest folder spec's */
716 pine_state->in_init_seq = 0; /* so output (& ClearScreen) show up */
717 pine_state->dont_use_init_cmds = 1; /* don't use up initial_commands yet */
718 ClearScreen();
720 /* initialize titlebar in case we use it */
721 set_titlebar("", NULL, NULL, NULL, NULL, 0, FolderName, 0, 0, NULL);
724 * Prep storage object driver for PicoText
726 so_register_external_driver(pine_pico_get, pine_pico_give, pine_pico_writec, pine_pico_readc,
727 pine_pico_puts, pine_pico_seek, NULL, NULL);
729 #ifdef DEBUG
730 if(ps_global->debug_imap > 4 || debug > 9){
731 q_status_message(SM_ORDER | SM_DING, 5, 9,
732 _("Warning: sensitive authentication data included in debug file"));
733 flush_status_messages(0);
735 #endif
737 if(args.action == aaPrcCopy || args.action == aaAbookCopy){
738 int exit_val = -1;
739 char *err_msg = NULL;
742 * Don't translate these into UTF-8 because we'll be using them
743 * before we translate next time. User should use ascii.
745 if(args.data.copy.local && args.data.copy.remote){
746 switch(args.action){
747 case aaPrcCopy:
748 exit_val = copy_pinerc(args.data.copy.local,
749 args.data.copy.remote, &err_msg);
750 break;
752 case aaAbookCopy:
753 exit_val = copy_abook(args.data.copy.local,
754 args.data.copy.remote, &err_msg);
755 break;
757 default:
758 break;
761 if(err_msg){
762 q_status_message(SM_ORDER | SM_DING, 3, 4, err_msg);
763 fs_give((void **)&err_msg);
765 goodnight_gracey(pine_state, exit_val);
768 if(args.action == aaFolder
769 && (pine_state->first_time_user || pine_state->show_new_version)){
770 pine_state->mangled_header = 1;
771 show_main_screen(pine_state, 0, FirstMenu, &main_keymenu, 0,
772 (Pos *) NULL);
773 new_user_or_version(pine_state);
774 ClearScreen();
777 /* put back in case we need to suppress output */
778 pine_state->in_init_seq = pine_state->save_in_init_seq;
780 /* queue any init errors so they get displayed in a screen below */
781 queue_init_errors(ps_global);
783 /* "Page" the given file? */
784 if(args.action == aaMore){
785 int dice = 1, redir = 0;
787 if(pine_state->in_init_seq){
788 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
789 clear_cursor_pos();
790 if(pine_state->free_initial_cmds)
791 fs_give((void **)&(pine_state->free_initial_cmds));
793 pine_state->initial_cmds = NULL;
796 /*======= Requested that we simply page the given file =======*/
797 if(args.data.file){ /* Open the requested file... */
798 SourceType src;
799 STORE_S *store = NULL;
800 char *decode_error = NULL;
801 char filename[MAILTMPLEN];
803 if(args.data.file[0] == '\0'){
804 HelpType help = NO_HELP;
806 pine_state->mangled_footer = 1;
807 filename[0] = '\0';
808 while(1){
809 int flags = OE_APPEND_CURRENT;
811 rv = optionally_enter(filename, -FOOTER_ROWS(pine_state),
812 0, sizeof(filename),
813 /* TRANSLATORS: file is computer data */
814 _("File to open : "),
815 NULL, help, &flags);
816 if(rv == 3){
817 help = (help == NO_HELP) ? h_no_F_arg : NO_HELP;
818 continue;
821 if(rv != 4)
822 break;
825 if(rv == 1){
826 q_status_message(SM_ORDER, 0, 2, _("Cancelled"));
827 goodnight_gracey(pine_state, -1);
830 if(*filename){
831 removing_trailing_white_space(filename);
832 removing_leading_white_space(filename);
833 if(is_absolute_path(filename))
834 fnexpand(filename, sizeof(filename));
836 args.data.file = filename;
839 if(!*filename){
840 /* TRANSLATORS: file is computer data */
841 q_status_message(SM_ORDER, 0, 2 ,_("No file to open"));
842 goodnight_gracey(pine_state, -1);
846 if(stdin_getc){
847 redir++;
848 src = CharStar;
849 if(isatty(0) && (store = so_get(src, NULL, EDIT_ACCESS))){
850 gf_io_t pc;
852 gf_set_so_writec(&pc, store);
853 gf_filter_init();
854 if((decode_error = gf_pipe(stdin_getc, pc)) != NULL){
855 dice = 0;
856 q_status_message1(SM_ORDER, 3, 4,
857 _("Problem reading standard input: %s"),
858 decode_error);
861 gf_clear_so_writec(store);
863 else
864 dice = 0;
866 else{
867 src = FileStar;
868 strncpy(ps_global->cur_folder, args.data.file,
869 sizeof(ps_global->cur_folder)-1);
870 ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
871 if(!(store = so_get(src, args.data.file, READ_ACCESS|READ_FROM_LOCALE)))
872 dice = 0;
875 if(dice){
876 SCROLL_S sargs;
878 memset(&sargs, 0, sizeof(SCROLL_S));
879 sargs.text.text = so_text(store);
880 sargs.text.src = src;
881 /* TRANSLATORS: file is computer file being read by user */
882 sargs.text.desc = _("file");
883 /* TRANSLATORS: this is in the title bar at top of screen */
884 sargs.bar.title = _("FILE VIEW");
885 sargs.bar.style = FileTextPercent;
886 sargs.keys.menu = &simple_file_keymenu;
887 setbitmap(sargs.keys.bitmap);
888 scrolltool(&sargs);
890 printf("\n\n");
891 so_give(&store);
895 if(!dice){
896 q_status_message2(SM_ORDER, 3, 4,
897 _("Can't display \"%s\": %s"),
898 (redir) ? _("Standard Input")
899 : args.data.file ? args.data.file : "NULL",
900 error_description(errno));
903 goodnight_gracey(pine_state, 0);
905 else if(args.action == aaMail || (stdin_getc && (args.action != aaURL))){
906 /*======= address on command line/send one message mode ============*/
907 char *to = NULL, *error = NULL, *addr = NULL;
908 int len, good_addr = 1;
909 int exit_val = 0;
910 BUILDER_ARG fcc;
912 if(pine_state->in_init_seq){
913 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
914 clear_cursor_pos();
915 if(pine_state->free_initial_cmds)
916 fs_give((void **) &(pine_state->free_initial_cmds));
918 pine_state->initial_cmds = NULL;
921 /*----- Format the To: line with commas for the composer ---*/
922 if(args.data.mail.addrlist){
923 STRLIST_S *p;
925 for(p = args.data.mail.addrlist, len = 0; p; p = p->next)
926 len += strlen(p->name) + 2;
928 to = (char *) fs_get((len + 5) * sizeof(char));
929 for(p = args.data.mail.addrlist, *to = '\0'; p; p = p->next){
930 if(*to){
931 strncat(to, ", ", len+5-strlen(to)-1);
932 to[len+5-1] = '\0';
935 strncat(to, p->name, len+5-strlen(to)-1);
936 to[len+5-1] = '\0';
939 memset((void *)&fcc, 0, sizeof(BUILDER_ARG));
940 dprint((2, "building addr: -->%s<--\n", to ? to : "?"));
941 good_addr = (build_address(to, &addr, &error, &fcc, NULL) >= 0);
942 dprint((2, "mailing to: -->%s<--\n", addr ? addr : "?"));
943 free_strlist(&args.data.mail.addrlist);
945 else
946 memset(&fcc, 0, sizeof(fcc));
948 if(good_addr){
949 compose_mail(addr, fcc.tptr, NULL,
950 args.data.mail.attachlist, stdin_getc);
952 else{
953 /* TRANSLATORS: refers to bad email address */
954 q_status_message1(SM_ORDER, 3, 4, _("Bad address: %s"), error);
955 exit_val = -1;
958 if(addr)
959 fs_give((void **) &addr);
961 if(fcc.tptr)
962 fs_give((void **) &fcc.tptr);
964 if(args.data.mail.attachlist)
965 free_attachment_list(&args.data.mail.attachlist);
967 if(to)
968 fs_give((void **) &to);
970 if(error)
971 fs_give((void **) &error);
973 goodnight_gracey(pine_state, exit_val);
975 else{
976 char int_mail[MAXPATH+1];
977 struct key_menu *km = &main_keymenu;
979 /*========== Normal pine mail reading mode ==========*/
981 pine_state->mail_stream = NULL;
982 pine_state->mangled_screen = 1;
984 if(args.action == aaURL){
985 url_tool_t f;
987 if(pine_state->in_init_seq){
988 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
989 clear_cursor_pos();
990 if(pine_state->free_initial_cmds)
991 fs_give((void **) &(pine_state->free_initial_cmds));
992 pine_state->initial_cmds = NULL;
994 if((f = url_local_handler(args.url)) != NULL){
995 if(args.data.mail.attachlist){
996 if(f == url_local_mailto){
997 if(!(url_local_mailto_and_atts(args.url,
998 args.data.mail.attachlist)
999 && pine_state->next_screen))
1000 free_attachment_list(&args.data.mail.attachlist);
1001 goodnight_gracey(pine_state, 0);
1003 else {
1004 q_status_message(SM_ORDER | SM_DING, 3, 4,
1005 _("Only mailto URLs are allowed with file attachments"));
1006 goodnight_gracey(pine_state, -1); /* no return */
1009 else if(!((*f)(args.url) && pine_state->next_screen))
1010 goodnight_gracey(pine_state, 0); /* no return */
1012 else{
1013 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1014 _("Unrecognized URL \"%s\""), args.url);
1015 goodnight_gracey(pine_state, -1); /* no return */
1018 else if(!pine_state->start_in_index){
1019 /* flash message about executing initial commands */
1020 if(pine_state->in_init_seq){
1021 pine_state->in_init_seq = 0;
1022 clear_cursor_pos();
1023 pine_state->mangled_header = 1;
1024 pine_state->mangled_footer = 1;
1025 pine_state->mangled_screen = 0;
1026 /* show that this is Alpine */
1027 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
1028 pine_state->mangled_screen = 1;
1029 pine_state->painted_footer_on_startup = 1;
1030 if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
1031 char buf1[6*MAX_SCREEN_COLS+1];
1032 char buf2[6*MAX_SCREEN_COLS+1];
1033 int wid;
1035 /* TRANSLATORS: Initial Keystroke List is the literal name of an option */
1036 strncpy(buf1, _("Executing Initial Keystroke List......"), sizeof(buf1));
1037 buf1[sizeof(buf1)-1] = '\0';
1038 wid = utf8_width(buf1);
1039 if(wid > ps_global->ttyo->screen_cols){
1040 utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
1041 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
1043 else{
1044 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
1045 MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
1049 pine_state->in_init_seq = 1;
1051 else{
1052 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
1053 pine_state->painted_body_on_startup = 1;
1054 pine_state->painted_footer_on_startup = 1;
1057 else{
1058 /* cancel any initial commands, overridden by cmd line */
1059 if(pine_state->in_init_seq){
1060 pine_state->in_init_seq = 0;
1061 pine_state->save_in_init_seq = 0;
1062 clear_cursor_pos();
1063 if(pine_state->initial_cmds){
1064 if(pine_state->free_initial_cmds)
1065 fs_give((void **)&(pine_state->free_initial_cmds));
1067 pine_state->initial_cmds = NULL;
1070 F_SET(F_USE_FK,pine_state, pine_state->orig_use_fkeys);
1073 (void) do_index_border(pine_state->context_current,
1074 pine_state->cur_folder,
1075 pine_state->mail_stream,
1076 pine_state->msgmap, MsgIndex, NULL,
1077 INDX_CLEAR|INDX_HEADER|INDX_FOOTER);
1078 pine_state->painted_footer_on_startup = 1;
1079 if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
1080 char buf1[6*MAX_SCREEN_COLS+1];
1081 char buf2[6*MAX_SCREEN_COLS+1];
1082 int wid;
1084 strncpy(buf1, _("Please wait, opening mail folder......"), sizeof(buf1));
1085 buf1[sizeof(buf1)-1] = '\0';
1086 wid = utf8_width(buf1);
1087 if(wid > ps_global->ttyo->screen_cols){
1088 utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
1089 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
1091 else{
1092 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
1093 MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
1098 fflush(stdout);
1100 #if !defined(_WINDOWS) && !defined(LEAVEOUTFIFO)
1101 if(ps_global->VAR_FIFOPATH && ps_global->VAR_FIFOPATH[0])
1102 init_newmailfifo(ps_global->VAR_FIFOPATH);
1103 #endif
1105 if(pine_state->in_init_seq){
1106 pine_state->in_init_seq = 0;
1107 clear_cursor_pos();
1110 if(args.action == aaFolder && args.data.folder){
1111 CONTEXT_S *cntxt = NULL, *tc = NULL;
1112 char foldername[MAILTMPLEN];
1113 int notrealinbox = 0;
1115 if(args.data.folder[0] == '\0'){
1116 char *fldr;
1117 unsigned save_def_goto_rule;
1119 foldername[0] = '\0';
1120 save_def_goto_rule = pine_state->goto_default_rule;
1121 pine_state->goto_default_rule = GOTO_FIRST_CLCTN;
1122 tc = default_save_context(pine_state->context_list);
1123 fldr = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
1124 pine_state->goto_default_rule = save_def_goto_rule;
1125 if(fldr){
1126 strncpy(foldername, fldr, sizeof(foldername)-1);
1127 foldername[sizeof(foldername)-1] = '\0';
1130 if(*foldername){
1131 removing_trailing_white_space(foldername);
1132 removing_leading_white_space(foldername);
1133 args.data.folder = cpystr(foldername);
1136 if(!*foldername){
1137 q_status_message(SM_ORDER, 0, 2 ,_("No folder to open"));
1138 goodnight_gracey(pine_state, -1);
1142 if(tc)
1143 cntxt = tc;
1144 else if((rv = pine_state->init_context) < 0)
1146 * As with almost all the folder vars in the pinerc,
1147 * we subvert the collection "breakout" here if the
1148 * folder name given looks like an asolute path on
1149 * this system...
1151 cntxt = (is_absolute_path(args.data.folder))
1152 ? NULL : pine_state->context_current;
1153 else if(rv == 0)
1154 cntxt = NULL;
1155 else
1156 for(cntxt = pine_state->context_list;
1157 rv > 1 && cntxt->next;
1158 rv--, cntxt = cntxt->next)
1161 if(pine_state && pine_state->ttyo){
1162 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1163 pine_state->painted_footer_on_startup = 0;
1164 pine_state->mangled_footer = 1;
1167 if(do_broach_folder(args.data.folder, cntxt, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) <= 0){
1168 q_status_message1(SM_ORDER, 3, 4,
1169 _("Unable to open folder \"%s\""), args.data.folder);
1171 goodnight_gracey(pine_state, -1);
1174 else if(args.action == aaFolder){
1175 #ifdef _WINDOWS
1177 * need to ask for the inbox name if no default under DOS
1178 * since there is no "inbox"
1181 if(!pine_state->VAR_INBOX_PATH || !pine_state->VAR_INBOX_PATH[0]
1182 || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0){
1183 HelpType help = NO_HELP;
1184 static ESCKEY_S ekey[] = {{ctrl(T), 2, "^T", "To Fldrs"},
1185 {-1, 0, NULL, NULL}};
1187 pine_state->mangled_footer = 1;
1188 int_mail[0] = '\0';
1189 while(1){
1190 int flags = OE_APPEND_CURRENT;
1192 rv = optionally_enter(int_mail, -FOOTER_ROWS(pine_state),
1193 0, sizeof(int_mail),
1194 _("No inbox! Folder to open as inbox : "),
1195 /* ekey */ NULL, help, &flags);
1196 if(rv == 3){
1197 help = (help == NO_HELP) ? h_sticky_inbox : NO_HELP;
1198 continue;
1201 if(rv != 4)
1202 break;
1205 if(rv == 1){
1206 q_status_message(SM_ORDER, 0, 2 ,_("Folder open cancelled"));
1207 rv = 0; /* reset rv */
1209 else if(rv == 2){
1210 show_main_screen(pine_state,0,FirstMenu,km,0,(Pos *)NULL);
1213 if(*int_mail){
1214 removing_trailing_white_space(int_mail);
1215 removing_leading_white_space(int_mail);
1216 if((!pine_state->VAR_INBOX_PATH
1217 || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0)
1218 /* TRANSLATORS: Inbox-Path and PINERC are literal, not to be translated */
1219 && want_to(_("Preserve folder as \"Inbox-Path\" in PINERC"),
1220 'y', 'n', NO_HELP, WT_NORM) == 'y'){
1221 set_variable(V_INBOX_PATH, int_mail, 1, 1, Main);
1223 else{
1224 if(pine_state->VAR_INBOX_PATH)
1225 fs_give((void **)&pine_state->VAR_INBOX_PATH);
1227 pine_state->VAR_INBOX_PATH = cpystr(int_mail);
1230 if(pine_state && pine_state->ttyo){
1231 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1232 pine_state->painted_footer_on_startup = 0;
1233 pine_state->mangled_footer = 1;
1236 do_broach_folder(pine_state->inbox_name,
1237 pine_state->context_list, NULL, DB_INBOXWOCNTXT);
1239 else
1240 q_status_message(SM_ORDER, 0, 2 ,_("No folder opened"));
1243 else
1245 #endif /* _WINDOWS */
1246 if(F_ON(F_PREOPEN_STAYOPENS, ps_global))
1247 preopen_stayopen_folders();
1249 if(pine_state && pine_state->ttyo){
1250 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1251 pine_state->painted_footer_on_startup = 0;
1252 pine_state->mangled_footer = 1;
1255 /* open inbox */
1256 do_broach_folder(pine_state->inbox_name,
1257 pine_state->context_list, NULL, DB_INBOXWOCNTXT);
1260 if(pine_state->mangled_footer)
1261 pine_state->painted_footer_on_startup = 0;
1263 if(args.action == aaFolder
1264 && pine_state->mail_stream
1265 && expire_sent_mail())
1266 pine_state->painted_footer_on_startup = 0;
1269 * Initialize the defaults. Initializing here means that
1270 * if they're remote, the user isn't prompted for an imap login
1271 * before the display's drawn, AND there's the chance that
1272 * we can climb onto the already opened folder's stream...
1274 if(ps_global->first_time_user)
1275 init_save_defaults(); /* initialize default save folders */
1277 build_path(int_mail,
1278 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
1279 : pine_state->home_dir,
1280 INTERRUPTED_MAIL, sizeof(int_mail));
1281 if(args.action == aaFolder
1282 && (folder_exists(NULL, int_mail) & FEX_ISFILE))
1283 q_status_message(SM_ORDER | SM_DING, 4, 5,
1284 _("Use Compose command to continue interrupted message."));
1286 #if defined(USE_QUOTAS)
1288 long q;
1289 int over;
1290 q = disk_quota(pine_state->home_dir, &over);
1291 if(q > 0 && over){
1292 q_status_message2(SM_ASYNC | SM_DING, 4, 5,
1293 _("WARNING! Over your disk quota by %s bytes (%s)"),
1294 comatose(q),byte_string(q));
1297 #endif
1299 pine_state->in_init_seq = pine_state->save_in_init_seq;
1300 pine_state->dont_use_init_cmds = 0;
1301 clear_cursor_pos();
1303 if(pine_state->give_fixed_warning)
1304 q_status_message(SM_ASYNC, 0, 10,
1305 /* TRANSLATORS: config is an abbreviation for configuration */
1306 _("Note: some of your config options conflict with site policy and are ignored"));
1308 if(!prune_folders_ok())
1309 q_status_message(SM_ASYNC, 0, 10,
1310 /* TRANSLATORS: Pruned-Folders is literal */
1311 _("Note: ignoring Pruned-Folders outside of default collection for saves"));
1313 if(get_input_timeout() == 0 &&
1314 ps_global->VAR_INBOX_PATH &&
1315 ps_global->VAR_INBOX_PATH[0] == '{')
1316 q_status_message(SM_ASYNC, 0, 10,
1317 _("Note: Mail-Check-Interval=0 may cause IMAP server connection to time out"));
1319 #ifdef _WINDOWS
1320 mswin_setnewmailwidth(ps_global->nmw_width);
1321 #endif
1324 /*-------------------------------------------------------------------
1325 Loop executing the commands
1327 This is done like this so that one command screen can cause
1328 another one to execute it with out going through the main menu.
1329 ------------------------------------------------------------------*/
1330 if(!pine_state->next_screen)
1331 pine_state->next_screen = pine_state->start_in_index
1332 ? mail_index_screen : main_menu_screen;
1333 while(1){
1334 if(pine_state->next_screen == SCREEN_FUN_NULL)
1335 pine_state->next_screen = main_menu_screen;
1337 (*(pine_state->next_screen))(pine_state);
1341 exit(0);
1346 * The arguments need to be converted to UTF-8 for our internal use.
1347 * Not all arguments are converted because some are used before we
1348 * are able to do the conversion, like the pinerc name.
1350 void
1351 convert_args_to_utf8(struct pine *ps, ARGDATA_S *args)
1353 char *fromcharset = NULL;
1354 char *conv;
1356 if(args){
1357 if(ps->keyboard_charmap && strucmp(ps->keyboard_charmap, "UTF-8")
1358 && strucmp(ps->keyboard_charmap, "US-ASCII"))
1359 fromcharset = ps->keyboard_charmap;
1360 else if(ps->display_charmap && strucmp(ps->display_charmap, "UTF-8")
1361 && strucmp(ps->display_charmap, "US-ASCII"))
1362 fromcharset = ps->display_charmap;
1363 #ifndef _WINDOWS
1364 else if(ps->VAR_OLD_CHAR_SET && strucmp(ps->VAR_OLD_CHAR_SET, "UTF-8")
1365 && strucmp(ps->VAR_OLD_CHAR_SET, "US-ASCII"))
1366 fromcharset = ps->VAR_OLD_CHAR_SET;
1367 #endif /* ! _WINDOWS */
1369 if(args->action == aaURL && args->url){
1370 conv = convert_to_utf8(args->url, fromcharset, 0);
1371 if(conv){
1372 fs_give((void **) &args->url);
1373 args->url = conv;
1377 if(args->action == aaFolder && args->data.folder){
1378 conv = convert_to_utf8(args->data.folder, fromcharset, 0);
1379 if(conv){
1380 fs_give((void **) &args->data.folder);
1381 args->data.folder = conv;
1385 if(args->action == aaMore && args->data.file){
1386 conv = convert_to_utf8(args->data.file, fromcharset, 0);
1387 if(conv){
1388 fs_give((void **) &args->data.file);
1389 args->data.file = conv;
1393 if(args->action == aaURL || args->action == aaMail){
1394 if(args->data.mail.addrlist){
1395 STRLIST_S *p;
1397 for(p = args->data.mail.addrlist; p; p=p->next){
1398 if(p->name){
1399 conv = convert_to_utf8(p->name, fromcharset, 0);
1400 if(conv){
1401 fs_give((void **) &p->name);
1402 p->name = conv;
1408 if(args->data.mail.attachlist){
1409 PATMT *p;
1411 for(p = args->data.mail.attachlist; p; p=p->next){
1412 if(p->filename){
1413 conv = convert_to_utf8(p->filename, fromcharset, 0);
1414 if(conv){
1415 fs_give((void **) &p->filename);
1416 p->filename = conv;
1426 void
1427 preopen_stayopen_folders(void)
1429 char **open_these;
1431 for(open_these = ps_global->VAR_PERMLOCKED;
1432 open_these && *open_these; open_these++)
1433 (void) do_broach_folder(*open_these, ps_global->context_list,
1434 NULL, DB_NOVISIT);
1439 * read_stdin_char - simple function to return a character from
1440 * redirected stdin
1443 read_stdin_char(char *c)
1445 int rv;
1447 /* it'd probably be a good idea to fix this to pre-read blocks */
1448 while(1){
1449 rv = read(PIPED_FD, c, 1);
1450 if(rv < 0){
1451 if(errno == EINTR){
1452 dprint((2, "read_stdin_char: read interrupted, restarting\n"));
1453 continue;
1455 else
1456 dprint((1, "read_stdin_char: read FAILED: %s\n",
1457 error_description(errno)));
1459 break;
1461 return(rv);
1465 /* this default is from the array of structs below */
1466 #define DEFAULT_MENU_ITEM ((unsigned) 3) /* LIST FOLDERS */
1467 #define ABOOK_MENU_ITEM ((unsigned) 4) /* ADDRESS BOOK */
1468 #define MAX_MENU_ITEM ((unsigned) 6)
1470 * Skip this many spaces between rows of main menu screen.
1471 * We have MAX_MENU_ITEM+1 = # of commands in menu
1472 * 1 = copyright line
1473 * MAX_MENU_ITEM = rows between commands
1474 * 1 = extra row above commands
1475 * 1 = row between commands and copyright
1477 * To make it simple, if there is enough room for all of that include all the
1478 * extra space, if not, cut it all out.
1480 #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)
1482 static unsigned menu_index = DEFAULT_MENU_ITEM;
1485 * One of these for each line that gets printed in the middle of the
1486 * screen in the main menu.
1488 static struct menu_key {
1489 char *key_and_name,
1490 *news_addition;
1491 int key_index; /* index into keymenu array for this cmd */
1492 } mkeys[] = {
1494 * TRANSLATORS: These next few are headings on the Main alpine menu.
1495 * It's nice if the dashes can be made to line up vertically.
1497 {N_(" %s HELP - Get help using Alpine"),
1498 NULL, MAIN_HELP_KEY},
1499 {N_(" %s COMPOSE MESSAGE - Compose and send%s a message"),
1500 /* TRANSLATORS: We think of sending an email message or posting a news message.
1501 The message is shown as Compose and send/post a message */
1502 N_("/post"), MAIN_COMPOSE_KEY},
1503 {N_(" %s MESSAGE INDEX - View messages in current folder"),
1504 NULL, MAIN_INDEX_KEY},
1505 {N_(" %s FOLDER LIST - Select a folder%s to view"),
1506 /* TRANSLATORS: When news is supported the message above becomes
1507 Select a folder OR news group to view */
1508 N_(" OR news group"), MAIN_FOLDER_KEY},
1509 {N_(" %s ADDRESS BOOK - Update address book"),
1510 NULL, MAIN_ADDRESS_KEY},
1511 {N_(" %s SETUP - Configure Alpine Options"),
1512 NULL, MAIN_SETUP_KEY},
1513 /* TRANSLATORS: final Main menu line */
1514 {N_(" %s QUIT - Leave the Alpine program"),
1515 NULL, MAIN_QUIT_KEY}
1520 /*----------------------------------------------------------------------
1521 display main menu and execute main menu commands
1523 Args: The usual pine structure
1525 Result: main menu commands are executed
1528 M A I N M E N U S C R E E N
1530 Paint the main menu on the screen, get the commands and either execute
1531 the function or pass back the name of the function to execute for the menu
1532 selection. Only simple functions that always return here can be executed
1533 here.
1535 This functions handling of new mail, redrawing, errors and such can
1536 serve as a template for the other screen that do much the same thing.
1538 There is a loop that fetchs and executes commands until a command to leave
1539 this screen is given. Then the name of the next screen to display is
1540 stored in next_screen member of the structure and this function is exited
1541 with a return.
1543 First a check for new mail is performed. This might involve reading the new
1544 mail into the inbox which might then cause the screen to be repainted.
1546 Then the general screen painting is done. This is usually controlled
1547 by a few flags and some other position variables. If they change they
1548 tell this part of the code what to repaint. This will include cursor
1549 motion and so on.
1550 ----*/
1551 void
1552 main_menu_screen(struct pine *pine_state)
1554 UCS ch;
1555 int cmd, just_a_navigate_cmd, setup_command, km_popped;
1556 int notrealinbox;
1557 char *new_folder, *utf8str;
1558 CONTEXT_S *tc;
1559 struct key_menu *km;
1560 OtherMenu what;
1561 Pos curs_pos;
1563 ps_global = pine_state;
1564 just_a_navigate_cmd = 0;
1565 km_popped = 0;
1566 menu_index = DEFAULT_MENU_ITEM;
1567 what = FirstMenu; /* which keymenu to display */
1568 ch = 'x'; /* For display_message 1st time through */
1569 pine_state->next_screen = SCREEN_FUN_NULL;
1570 pine_state->prev_screen = main_menu_screen;
1571 curs_pos.row = pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
1572 curs_pos.col = 0;
1573 km = &main_keymenu;
1575 mailcap_free(); /* free resources we won't be using for a while */
1577 if(!pine_state->painted_body_on_startup
1578 && !pine_state->painted_footer_on_startup){
1579 pine_state->mangled_screen = 1;
1582 dprint((1, "\n\n ---- MAIN_MENU_SCREEN ----\n"));
1584 while(1){
1585 if(km_popped){
1586 km_popped--;
1587 if(km_popped == 0){
1588 clearfooter(pine_state);
1589 pine_state->mangled_body = 1;
1594 * fix up redrawer just in case some submenu caused it to get
1595 * reassigned...
1597 pine_state->redrawer = main_redrawer;
1599 /*----------- Check for new mail -----------*/
1600 if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
1601 pine_state->mangled_header = 1;
1603 if(streams_died())
1604 pine_state->mangled_header = 1;
1606 show_main_screen(pine_state, just_a_navigate_cmd, what, km,
1607 km_popped, &curs_pos);
1608 just_a_navigate_cmd = 0;
1609 what = SameMenu;
1611 /*---- This displays new mail notification, or errors ---*/
1612 if(km_popped){
1613 FOOTER_ROWS(pine_state) = 3;
1614 mark_status_dirty();
1617 display_message(ch);
1618 if(km_popped){
1619 FOOTER_ROWS(pine_state) = 1;
1620 mark_status_dirty();
1623 if(F_OFF(F_SHOW_CURSOR, ps_global)){
1624 curs_pos.row =pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
1625 curs_pos.col =0;
1628 MoveCursor(curs_pos.row, curs_pos.col);
1630 /*------ Read the command from the keyboard ----*/
1631 #ifdef MOUSE
1632 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
1633 register_mfunc(mouse_in_content, HEADER_ROWS(pine_state), 0,
1634 pine_state->ttyo->screen_rows-(FOOTER_ROWS(pine_state)+1),
1635 pine_state->ttyo->screen_cols);
1636 #endif
1637 #if defined(DOS) || defined(OS2)
1639 * AND pre-build header lines. This works just fine under
1640 * DOS since we wait for characters in a loop. Something will
1641 * will have to change under UNIX if we want to do the same.
1643 /* while_waiting = build_header_cache; */
1644 #ifdef _WINDOWS
1645 mswin_sethelptextcallback(pcpine_help_main);
1646 mswin_mousetrackcallback(pcpine_main_cursor);
1647 #endif
1648 #endif
1649 ch = READ_COMMAND(&utf8str);
1650 #ifdef MOUSE
1651 clear_mfunc(mouse_in_content);
1652 #endif
1653 #if defined(DOS) || defined(OS2)
1654 /* while_waiting = NULL; */
1655 #ifdef _WINDOWS
1656 mswin_sethelptextcallback(NULL);
1657 mswin_mousetrackcallback(NULL);
1658 #endif
1659 #endif
1661 /* No matter what, Quit here always works */
1662 if(ch == 'q' || ch == 'Q'){
1663 cmd = MC_QUIT;
1665 #ifdef DEBUG
1666 else if(debug && ch && ch < 0x80 && strchr("123456789", ch)){
1667 int olddebug;
1669 olddebug = debug;
1670 debug = ch - '0';
1671 if(debug > 7)
1672 ps_global->debug_timestamp = 1;
1673 else
1674 ps_global->debug_timestamp = 0;
1676 if(debug > 7)
1677 ps_global->debug_imap = 4;
1678 else if(debug > 6)
1679 ps_global->debug_imap = 3;
1680 else if(debug > 4)
1681 ps_global->debug_imap = 2;
1682 else if(debug > 2)
1683 ps_global->debug_imap = 1;
1684 else
1685 ps_global->debug_imap = 0;
1687 if(ps_global->mail_stream){
1688 if(ps_global->debug_imap > 0){
1689 mail_debug(ps_global->mail_stream);
1690 #ifdef _WINDOWS
1691 mswin_enableimaptelemetry(TRUE);
1692 #endif
1694 else{
1695 mail_nodebug(ps_global->mail_stream);
1696 #ifdef _WINDOWS
1697 mswin_enableimaptelemetry(FALSE);
1698 #endif
1702 if(debug > 7 && olddebug <= 7)
1703 mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
1704 else if(debug <= 7 && olddebug > 7 && !ps_global->debugmem)
1705 mail_parameters(NULL, SET_TCPDEBUG, (void *) FALSE);
1707 dprint((1, "*** Debug level set to %d ***\n", debug));
1708 if(debugfile)
1709 fflush(debugfile);
1711 q_status_message1(SM_ORDER, 0, 1, _("Debug level set to %s"),
1712 int2string(debug));
1713 continue;
1715 #endif /* DEBUG */
1716 else{
1717 cmd = menu_command(ch, km);
1719 if(km_popped)
1720 switch(cmd){
1721 case MC_NONE :
1722 case MC_OTHER :
1723 case MC_RESIZE :
1724 case MC_REPAINT :
1725 km_popped++;
1726 break;
1728 default:
1729 clearfooter(pine_state);
1730 break;
1734 /*------ Execute the command ------*/
1735 switch (cmd){
1736 help_case :
1737 /*------ HELP ------*/
1738 case MC_HELP :
1740 if(FOOTER_ROWS(pine_state) == 1 && km_popped == 0){
1741 km_popped = 2;
1742 pine_state->mangled_footer = 1;
1744 else{
1745 /* TRANSLATORS: This is a screen title */
1746 helper(main_menu_tx, _("HELP FOR MAIN MENU"), 0);
1747 pine_state->mangled_screen = 1;
1750 break;
1753 /*---------- display other key bindings ------*/
1754 case MC_OTHER :
1755 if(ch == 'o')
1756 warn_other_cmds();
1758 what = NextMenu;
1759 pine_state->mangled_footer = 1;
1760 break;
1763 /*---------- Previous item in menu ----------*/
1764 case MC_PREVITEM :
1765 if(menu_index > 0) {
1766 menu_index--;
1767 pine_state->mangled_body = 1;
1768 if(km->which == 0)
1769 pine_state->mangled_footer = 1;
1771 just_a_navigate_cmd++;
1773 else
1774 /* TRANSLATORS: list refers to list of commands in main menu */
1775 q_status_message(SM_ORDER, 0, 2, _("Already at top of list"));
1777 break;
1780 /*---------- Next item in menu ----------*/
1781 case MC_NEXTITEM :
1782 if(menu_index < MAX_MENU_ITEM){
1783 menu_index++;
1784 pine_state->mangled_body = 1;
1785 if(km->which == 0)
1786 pine_state->mangled_footer = 1;
1788 just_a_navigate_cmd++;
1790 else
1791 q_status_message(SM_ORDER, 0, 2, _("Already at bottom of list"));
1793 break;
1796 /*---------- Release Notes ----------*/
1797 case MC_RELNOTES :
1798 /* TRANSLATORS: This is a screen title */
1799 helper(h_news, _("ALPINE RELEASE NOTES"), 0);
1800 pine_state->mangled_screen = 1;
1801 break;
1804 #ifdef KEYBOARD_LOCK
1805 /*---------- Keyboard lock ----------*/
1806 case MC_KBLOCK :
1807 (void) lock_keyboard();
1808 pine_state->mangled_screen = 1;
1809 break;
1810 #endif /* KEYBOARD_LOCK */
1813 /*---------- Quit pine ----------*/
1814 case MC_QUIT :
1815 pine_state->next_screen = quit_screen;
1816 return;
1819 /*---------- Go to composer ----------*/
1820 case MC_COMPOSE :
1821 pine_state->next_screen = compose_screen;
1822 return;
1825 /*---- Go to alternate composer ------*/
1826 case MC_ROLE :
1827 pine_state->next_screen = alt_compose_screen;
1828 return;
1831 /*---------- Top of Folder list ----------*/
1832 case MC_COLLECTIONS :
1833 pine_state->next_screen = folder_screen;
1834 return;
1837 /*---------- Goto new folder ----------*/
1838 case MC_GOTO :
1839 tc = ps_global->context_current;
1840 new_folder = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
1841 if(new_folder)
1842 visit_folder(ps_global, new_folder, tc, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT);
1844 return;
1847 /*---------- Go to index ----------*/
1848 case MC_INDEX :
1849 if(THREADING()
1850 && sp_viewing_a_thread(pine_state->mail_stream)
1851 && unview_thread(pine_state, pine_state->mail_stream,
1852 pine_state->msgmap)){
1853 pine_state->view_skipped_index = 0;
1854 pine_state->mangled_screen = 1;
1857 pine_state->next_screen = mail_index_screen;
1858 return;
1861 /*---------- Review Status Messages ----------*/
1862 case MC_JOURNAL :
1863 review_messages();
1864 pine_state->mangled_screen = 1;
1865 break;
1868 /*---------- Setup mini menu ----------*/
1869 case MC_SETUP :
1870 setup_case :
1871 setup_command = setup_menu(pine_state);
1872 pine_state->mangled_footer = 1;
1873 do_setup_task(setup_command);
1874 if(ps_global->next_screen != main_menu_screen)
1875 return;
1877 break;
1880 /*---------- Go to address book ----------*/
1881 case MC_ADDRBOOK :
1882 pine_state->next_screen = addr_book_screen;
1883 return;
1886 /*------ Repaint the works -------*/
1887 case MC_RESIZE :
1888 case MC_REPAINT :
1889 ClearScreen();
1890 pine_state->mangled_screen = 1;
1891 break;
1894 #ifdef MOUSE
1895 /*------- Mouse event ------*/
1896 case MC_MOUSE :
1898 MOUSEPRESS mp;
1899 unsigned ndmi;
1900 struct pine *ps = pine_state;
1902 mouse_get_last (NULL, &mp);
1904 #ifdef _WINDOWS
1905 if(mp.button == M_BUTTON_RIGHT){
1906 if(!mp.doubleclick){
1907 static MPopup main_popup[] = {
1908 {tQueue, {"Folder List", lNormal}, {'L'}},
1909 {tQueue, {"Message Index", lNormal}, {'I'}},
1910 {tSeparator},
1911 {tQueue, {"Address Book", lNormal}, {'A'}},
1912 {tQueue, {"Setup Options", lNormal}, {'S'}},
1913 {tTail}
1916 mswin_popup(main_popup);
1919 else {
1920 #endif
1921 if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps)))
1922 ndmi = (mp.row+1 - HEADER_ROWS(ps) - (MNSKIP(ps)+1))/(MNSKIP(ps)+1);
1924 if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps))
1925 && !(MNSKIP(ps) && (mp.row+1) & 0x01)
1926 && ndmi <= MAX_MENU_ITEM
1927 && FOOTER_ROWS(ps) + (ndmi+1)*(MNSKIP(ps)+1)
1928 + MNSKIP(ps) + FOOTER_ROWS(ps) <= ps->ttyo->screen_rows){
1929 if(mp.doubleclick){
1930 switch(ndmi){ /* fake main_screen request */
1931 case 0 :
1932 goto help_case;
1934 case 1 :
1935 pine_state->next_screen = compose_screen;
1936 return;
1938 case 2 :
1939 pine_state->next_screen = mail_index_screen;
1940 return;
1942 case 3 :
1943 pine_state->next_screen = folder_screen;
1944 return;
1946 case 4 :
1947 pine_state->next_screen = addr_book_screen;
1948 return;
1950 case 5 :
1951 goto setup_case;
1953 case 6 :
1954 pine_state->next_screen = quit_screen;
1955 return;
1957 default: /* no op */
1958 break;
1961 else{
1962 menu_index = ndmi;
1963 pine_state->mangled_body = 1;
1964 if(km->which == 0)
1965 pine_state->mangled_footer = 1;
1967 just_a_navigate_cmd++;
1970 #ifdef _WINDOWS
1972 #endif
1975 break;
1976 #endif
1979 /*------ Input timeout ------*/
1980 case MC_NONE :
1981 break; /* noop for timeout loop mail check */
1984 /*------ Bogus Input ------*/
1985 case MC_UNKNOWN :
1986 if(ch == 'm' || ch == 'M'){
1987 q_status_message(SM_ORDER, 0, 1, "Already in Main Menu");
1988 break;
1991 default:
1992 bogus_command(ch, F_ON(F_USE_FK,pine_state) ? "F1" : "?");
1993 break;
1995 case MC_UTF8:
1996 bogus_utf8_command(utf8str, F_ON(F_USE_FK, pine_state) ? "F1" : "?");
1997 break;
1998 } /* the switch */
1999 } /* the BIG while loop! */
2003 /*----------------------------------------------------------------------
2004 Re-Draw the main menu
2006 Args: none.
2008 Result: main menu is re-displayed
2009 ----*/
2010 void
2011 main_redrawer(void)
2013 struct key_menu *km = &main_keymenu;
2015 ps_global->mangled_screen = 1;
2016 show_main_screen(ps_global, 0, FirstMenu, km, 0, (Pos *)NULL);
2020 /*----------------------------------------------------------------------
2021 Draw the main menu
2023 Args: pine_state - the usual struct
2024 quick_draw - tells do_menu() it can skip some drawing
2025 what - tells which section of keymenu to draw
2026 km - the keymenu
2027 cursor_pos - returns a good position for the cursor to be located
2029 Result: main menu is displayed
2030 ----*/
2031 void
2032 show_main_screen(struct pine *ps, int quick_draw, OtherMenu what,
2033 struct key_menu *km, int km_popped, Pos *cursor_pos)
2035 if(ps->painted_body_on_startup || ps->painted_footer_on_startup){
2036 ps->mangled_screen = 0; /* only worry about it here */
2037 ps->mangled_header = 1; /* we have to redo header */
2038 if(!ps->painted_body_on_startup)
2039 ps->mangled_body = 1; /* make sure to paint body*/
2041 if(!ps->painted_footer_on_startup)
2042 ps->mangled_footer = 1; /* make sure to paint footer*/
2044 ps->painted_body_on_startup = 0;
2045 ps->painted_footer_on_startup = 0;
2048 if(ps->mangled_screen){
2049 ps->mangled_header = 1;
2050 ps->mangled_body = 1;
2051 ps->mangled_footer = 1;
2052 ps->mangled_screen = 0;
2055 #ifdef _WINDOWS
2056 /* Reset the scroll range. Main screen never scrolls. */
2057 scroll_setrange (0L, 0L);
2058 mswin_beginupdate();
2059 #endif
2061 /* paint the titlebar if needed */
2062 if(ps->mangled_header){
2063 /* TRANSLATORS: screen title */
2064 set_titlebar(_("MAIN MENU"), ps->mail_stream, ps->context_current,
2065 ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0, NULL);
2066 ps->mangled_header = 0;
2069 /* paint the body if needed */
2070 if(ps->mangled_body){
2071 if(!quick_draw)
2072 ClearBody();
2074 do_menu(quick_draw, cursor_pos, km);
2075 ps->mangled_body = 0;
2078 /* paint the keymenu if needed */
2079 if(km && ps->mangled_footer){
2080 static char label[LONGEST_LABEL + 2 + 1], /* label + brackets + \0 */
2081 name[8];
2082 bitmap_t bitmap;
2084 setbitmap(bitmap);
2086 #ifdef KEYBOARD_LOCK
2087 if(ps_global->restricted || F_ON(F_DISABLE_KBLOCK_CMD,ps_global))
2088 #endif
2089 clrbitn(MAIN_KBLOCK_KEY, bitmap);
2091 menu_clear_binding(km, '>');
2092 menu_clear_binding(km, '.');
2093 menu_clear_binding(km, KEY_RIGHT);
2094 menu_clear_binding(km, ctrl('M'));
2095 menu_clear_binding(km, ctrl('J'));
2096 km->keys[MAIN_DEFAULT_KEY].bind
2097 = km->keys[mkeys[menu_index].key_index].bind;
2098 km->keys[MAIN_DEFAULT_KEY].label
2099 = km->keys[mkeys[menu_index].key_index].label;
2101 /* put brackets around the default action */
2102 snprintf(label, sizeof(label), "[%s]", km->keys[mkeys[menu_index].key_index].label);
2103 label[sizeof(label)-1] = '\0';
2104 strncpy(name, ">", sizeof(name));
2105 name[sizeof(name)-1] = '\0';
2106 km->keys[MAIN_DEFAULT_KEY].label = label;
2107 km->keys[MAIN_DEFAULT_KEY].name = name;
2108 menu_add_binding(km, '>', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2109 menu_add_binding(km, '.', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2110 menu_add_binding(km, ctrl('M'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2111 menu_add_binding(km, ctrl('J'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2113 if(F_ON(F_ARROW_NAV,ps_global))
2114 menu_add_binding(km, KEY_RIGHT, km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2116 if(km_popped){
2117 FOOTER_ROWS(ps) = 3;
2118 clearfooter(ps);
2121 draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
2122 1-FOOTER_ROWS(ps_global), 0, what);
2123 ps->mangled_footer = 0;
2124 if(km_popped){
2125 FOOTER_ROWS(ps) = 1;
2126 mark_keymenu_dirty();
2130 #ifdef _WINDOWS
2131 mswin_endupdate();
2132 #endif
2136 /*----------------------------------------------------------------------
2137 Actually display the main menu
2139 Args: quick_draw - just a next or prev command was typed so we only have
2140 to redraw the highlighting
2141 cursor_pos - a place to return a good value for cursor location
2143 Result: Main menu is displayed
2144 ---*/
2145 void
2146 do_menu(int quick_draw, Pos *cursor_pos, struct key_menu *km)
2148 struct pine *ps = ps_global;
2149 int dline, indent, longest = 0, cmd;
2150 char buf[4*MAX_SCREEN_COLS+1];
2151 char buf2[4*MAX_SCREEN_COLS+1];
2152 static int last_inverse = -1;
2153 Pos pos;
2155 /* find the longest command */
2156 for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
2157 memset((void *) buf, ' ', sizeof(buf));
2158 snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
2159 (F_OFF(F_USE_FK,ps)
2160 && km->keys[mkeys[cmd].key_index].name)
2161 ? km->keys[mkeys[cmd].key_index].name : "",
2162 (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
2163 ? _(mkeys[cmd].news_addition) : "");
2164 buf[sizeof(buf)-1] = '\0';
2166 if(longest < (indent = utf8_width(buf)))
2167 longest = indent;
2170 indent = MAX(((ps->ttyo->screen_cols - longest)/2) - 1, 0);
2172 dline = HEADER_ROWS(ps) + MNSKIP(ps);
2173 for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
2174 /* leave room for copyright and footer */
2175 if(dline + MNSKIP(ps) + 1 + FOOTER_ROWS(ps) >= ps->ttyo->screen_rows)
2176 break;
2178 if(quick_draw && !(cmd == last_inverse || cmd == menu_index)){
2179 dline += (1 + MNSKIP(ps));
2180 continue;
2183 if(cmd == menu_index)
2184 StartInverse();
2186 memset((void *) buf, ' ', sizeof(buf));
2187 snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
2188 (F_OFF(F_USE_FK,ps)
2189 && km->keys[mkeys[cmd].key_index].name)
2190 ? km->keys[mkeys[cmd].key_index].name : "",
2191 (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
2192 ? _(mkeys[cmd].news_addition) : "");
2193 buf[sizeof(buf)-1] = '\0';
2195 utf8_pad_to_width(buf2, buf, sizeof(buf2),
2196 MIN(ps->ttyo->screen_cols-indent,longest+1), 1);
2197 pos.row = dline++;
2198 pos.col = indent;
2199 PutLine0(pos.row, pos.col, buf2);
2201 if(MNSKIP(ps))
2202 dline++;
2204 if(cmd == menu_index){
2205 if(cursor_pos){
2206 cursor_pos->row = pos.row;
2207 /* 6 is 1 for the letter plus 5 spaces */
2208 cursor_pos->col = pos.col + 6;
2209 if(F_OFF(F_USE_FK,ps))
2210 cursor_pos->col++;
2212 cursor_pos->col = MIN(cursor_pos->col, ps->ttyo->screen_cols);
2215 EndInverse();
2220 last_inverse = menu_index;
2222 if(!quick_draw && FOOTER_ROWS(ps)+1 < ps->ttyo->screen_rows){
2223 utf8_to_width(buf2, LEGAL_NOTICE, sizeof(buf2),
2224 ps->ttyo->screen_cols-3, NULL);
2225 PutLine0(ps->ttyo->screen_rows - (FOOTER_ROWS(ps)+1),
2226 MAX(0, ((ps->ttyo->screen_cols-utf8_width(buf2))/2)),
2227 buf2);
2230 fflush(stdout);
2235 choose_setup_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2237 int rv = 1;
2238 SRV_S *srv;
2240 if(!(srv = (SRV_S *)sparms->proc.data.p)){
2241 sparms->proc.data.p = (SRV_S *)fs_get(sizeof(*srv));
2242 srv = (SRV_S *)sparms->proc.data.p;
2243 memset(srv, 0, sizeof(*srv));
2246 ps_global->next_screen = SCREEN_FUN_NULL;
2248 switch(cmd){
2249 case MC_PRINTER :
2250 srv->cmd = 'p';
2251 break;
2253 case MC_PASSWD :
2254 srv->cmd = 'n';
2255 break;
2257 case MC_CONFIG :
2258 srv->cmd = 'c';
2259 break;
2261 case MC_SIG :
2262 srv->cmd = 's';
2263 break;
2265 case MC_ABOOKS :
2266 srv->cmd = 'a';
2267 break;
2269 case MC_CLISTS :
2270 srv->cmd = 'l';
2271 break;
2273 case MC_RULES :
2274 srv->cmd = 'r';
2275 break;
2277 case MC_DIRECTORY :
2278 srv->cmd = 'd';
2279 break;
2281 case MC_KOLOR :
2282 srv->cmd = 'k';
2283 break;
2285 case MC_REMOTE :
2286 srv->cmd = 'z';
2287 break;
2289 case MC_SECURITY : /* S/MIME setup screen */
2290 srv->cmd = 'm';
2291 break;
2293 case MC_EXCEPT :
2294 srv->exc = !srv->exc;
2295 menu_clear_binding(sparms->keys.menu, 'x');
2296 if(srv->exc){
2297 if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
2298 /* TRANSLATORS: screen title */
2299 sparms->bar.title = cpystr(_("SETUP EXCEPTIONS"));
2300 ps_global->mangled_header = 1;
2301 /* TRANSLATORS: The reason the X is upper case in eXceptions
2302 is because the command key is X. It isn't necessary, just
2303 nice if it works. */
2304 menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
2305 N_("not eXceptions"), SETUP_EXCEPT);
2307 else{
2308 if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
2309 /* TRANSLATORS: screen title */
2310 sparms->bar.title = cpystr(_("SETUP"));
2311 ps_global->mangled_header = 1;
2312 menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
2313 N_("eXceptions"), SETUP_EXCEPT);
2316 if(sparms->keys.menu->which == 1)
2317 ps_global->mangled_footer = 1;
2319 rv = 0;
2320 break;
2322 case MC_NO_EXCEPT :
2323 #if defined(DOS) || defined(OS2)
2324 q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \"PINERCEX\" file to use eXceptions"));
2325 #else
2326 q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \".pinercex\" file to use eXceptions"));
2327 #endif
2328 rv = 0;
2329 break;
2331 default:
2332 alpine_panic("Unexpected command in choose_setup_cmd");
2333 break;
2336 return(rv);
2341 setup_menu(struct pine *ps)
2343 int ret = 0, exceptions = 0;
2344 int printer = 0, passwd = 0, config = 0, sig = 0, dir = 0, smime = 0, exc = 0;
2345 SCROLL_S sargs;
2346 SRV_S *srv;
2347 STORE_S *store;
2349 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2350 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
2351 return(ret);
2354 #if !defined(DOS)
2355 if(!ps_global->vars[V_PRINTER].is_fixed) /* printer can be changed */
2356 printer++;
2357 #endif
2359 #ifdef PASSWD_PROG
2360 if(F_OFF(F_DISABLE_PASSWORD_CMD,ps_global)) /* password is allowed */
2361 passwd++;
2362 #endif
2364 if(F_OFF(F_DISABLE_CONFIG_SCREEN,ps_global)) /* config allowed */
2365 config++;
2367 if(F_OFF(F_DISABLE_SIGEDIT_CMD,ps_global)) /* .sig editing is allowed */
2368 sig++;
2370 #ifdef ENABLE_LDAP
2371 dir++;
2372 #endif
2374 #ifdef SMIME
2375 smime++;
2376 #endif
2378 if(ps_global->post_prc)
2379 exc++;
2381 /* TRANSLATORS: starting here we have a whole screen of help text */
2382 so_puts(store, _("This is the Setup screen for Alpine. Choose from the following commands:\n"));
2384 so_puts(store, "\n");
2385 so_puts(store, _("(E) Exit Setup:\n"));
2386 so_puts(store, _(" This puts you back at the Main Menu.\n"));
2388 if(exc){
2389 so_puts(store, "\n");
2390 so_puts(store, _("(X) eXceptions:\n"));
2391 so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
2392 so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
2393 so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
2394 so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
2395 so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
2396 so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
2397 so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
2398 so_puts(store, _(" on a particular platform.\n"));
2399 so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
2400 so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
2401 so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
2404 if(printer){
2405 so_puts(store, "\n");
2406 so_puts(store, _("(P) Printer:\n"));
2407 so_puts(store, _(" Allows you to set a default printer and to define custom\n"));
2408 so_puts(store, _(" print commands.\n"));
2411 if(passwd){
2412 so_puts(store, "\n");
2413 so_puts(store, _("(N) Newpassword:\n"));
2414 so_puts(store, _(" Change your password.\n"));
2417 if(config){
2418 so_puts(store, "\n");
2419 so_puts(store, _("(C) Config:\n"));
2420 so_puts(store, _(" Allows you to set or unset many features of Alpine.\n"));
2421 so_puts(store, _(" You may also set the values of many options with this command.\n"));
2424 if(sig){
2425 so_puts(store, "\n");
2426 so_puts(store, _("(S) Signature:\n"));
2427 so_puts(store, _(" Enter or edit a custom signature which will\n"));
2428 so_puts(store, _(" be included with each new message you send.\n"));
2431 so_puts(store, "\n");
2432 so_puts(store, _("(A) AddressBooks:\n"));
2433 so_puts(store, _(" Define a non-default address book.\n"));
2435 so_puts(store, "\n");
2436 so_puts(store, _("(L) collectionLists:\n"));
2437 so_puts(store, _(" You may define groups of folders to help you better organize your mail.\n"));
2439 so_puts(store, "\n");
2440 so_puts(store, _("(R) Rules:\n"));
2441 so_puts(store, _(" This has up to six sub-categories: Roles, Index Colors, Filters,\n"));
2442 so_puts(store, _(" SetScores, Search, and Other. If the Index Colors option is\n"));
2443 so_puts(store, _(" missing you may turn it on (if possible) with Setup/Kolor.\n"));
2444 so_puts(store, _(" If Roles is missing it has probably been administratively disabled.\n"));
2446 if(dir){
2447 so_puts(store, "\n");
2448 so_puts(store, _("(D) Directory:\n"));
2449 so_puts(store, _(" Define an LDAP Directory server for Alpine's use. A directory server is\n"));
2450 so_puts(store, _(" similar to an address book, but it is usually maintained by an\n"));
2451 so_puts(store, _(" organization. It is similar to a telephone directory.\n"));
2454 so_puts(store, "\n");
2455 so_puts(store, _("(K) Kolor:\n"));
2456 so_puts(store, _(" Set custom colors for various parts of the Alpine screens. For example, the\n"));
2457 so_puts(store, _(" command key labels, the titlebar at the top of each page, and quoted\n"));
2458 so_puts(store, _(" sections of messages you are viewing.\n"));
2460 if(smime){
2461 so_puts(store, "\n");
2462 so_puts(store, _("(M) S/MIME:\n"));
2463 so_puts(store, _(" Setup for using S/MIME to verify signed messages, decrypt\n"));
2464 so_puts(store, _(" encrypted messages, and to sign or encrypt outgoing messages.\n"));
2467 so_puts(store, "\n");
2468 so_puts(store, _("(Z) RemoteConfigSetup:\n"));
2469 so_puts(store, _(" This is a command you will probably only want to use once, if at all.\n"));
2470 so_puts(store, _(" It helps you transfer your Alpine configuration data to an IMAP server,\n"));
2471 so_puts(store, _(" where it will be accessible from any of the computers you read mail\n"));
2472 so_puts(store, _(" from (using Alpine). The idea behind a remote configuration is that you\n"));
2473 so_puts(store, _(" can change your configuration in one place and have that change show\n"));
2474 so_puts(store, _(" up on all of the computers you use.\n"));
2475 so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
2476 so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
2477 so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
2479 /* put this down here for people who don't have exceptions */
2480 if(!exc){
2481 so_puts(store, "\n");
2482 so_puts(store, _("(X) eXceptions:\n"));
2483 so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
2484 so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
2485 so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
2486 so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
2487 so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
2488 so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
2489 so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
2490 so_puts(store, _(" on a particular platform.\n"));
2491 so_puts(store, _(" (Note: this command does not do anything unless you have a configuration\n"));
2492 so_puts(store, _(" with exceptions enabled (you don't have that). Common ways to enable an\n"));
2493 so_puts(store, _(" exceptions config are the command line argument \"-x <exception_config>\";\n"));
2494 so_puts(store, _(" or the existence of the file \".pinercex\" for Unix Alpine, or \"PINERCEX\")\n"));
2495 so_puts(store, _(" for PC-Alpine.)\n"));
2496 so_puts(store, _(" (Another note: this command does not show up on the keymenu at the bottom\n"));
2497 so_puts(store, _(" of the screen unless you press \"O\" for \"Other Commands\" --but you\n"));
2498 so_puts(store, _(" don't need to press the \"O\" in order to invoke the command.)\n"));
2501 memset(&sargs, 0, sizeof(SCROLL_S));
2502 sargs.text.text = so_text(store);
2503 sargs.text.src = CharStar;
2504 sargs.text.desc = _("Information About Setup Command");
2505 sargs.bar.title = cpystr(_("SETUP"));
2506 sargs.proc.tool = choose_setup_cmd;
2507 sargs.help.text = NO_HELP;
2508 sargs.help.title = NULL;
2509 sargs.keys.menu = &choose_setup_keymenu;
2510 sargs.keys.menu->how_many = 2;
2512 setbitmap(sargs.keys.bitmap);
2513 if(!printer)
2514 clrbitn(SETUP_PRINTER, sargs.keys.bitmap);
2516 if(!passwd)
2517 clrbitn(SETUP_PASSWD, sargs.keys.bitmap);
2519 if(!config)
2520 clrbitn(SETUP_CONFIG, sargs.keys.bitmap);
2522 if(!sig)
2523 clrbitn(SETUP_SIG, sargs.keys.bitmap);
2525 if(!dir)
2526 clrbitn(SETUP_DIRECTORY, sargs.keys.bitmap);
2528 if(!smime)
2529 clrbitn(SETUP_SMIME, sargs.keys.bitmap);
2531 if(exc)
2532 menu_init_binding(sargs.keys.menu, 'x', MC_EXCEPT, "X",
2533 N_("eXceptions"), SETUP_EXCEPT);
2534 else
2535 menu_init_binding(sargs.keys.menu, 'x', MC_NO_EXCEPT, "X",
2536 N_("eXceptions"), SETUP_EXCEPT);
2539 scrolltool(&sargs);
2541 ps->mangled_screen = 1;
2543 srv = (SRV_S *)sargs.proc.data.p;
2545 exceptions = srv ? srv->exc : 0;
2547 so_give(&store);
2549 if(sargs.bar.title) fs_give((void**)&sargs.bar.title);
2550 if(srv){
2551 ret = srv->cmd;
2552 fs_give((void **)&sargs.proc.data.p);
2554 else
2555 ret = 'e';
2557 return(ret | (exceptions ? EDIT_EXCEPTION : 0));
2561 /*----------------------------------------------------------------------
2563 Args: command -- command char to perform
2565 ----*/
2566 void
2567 do_setup_task(int command)
2569 char *err = NULL;
2570 int rtype;
2571 int edit_exceptions = 0;
2572 int do_lit_sig = 0;
2574 if(command & EDIT_EXCEPTION){
2575 edit_exceptions = 1;
2576 command &= ~EDIT_EXCEPTION;
2579 switch(command) {
2580 /*----- EDIT SIGNATURE -----*/
2581 case 's':
2582 if(ps_global->VAR_LITERAL_SIG)
2583 do_lit_sig = 1;
2584 else {
2585 char sig_path[MAXPATH+1];
2587 if(!signature_path(ps_global->VAR_SIGNATURE_FILE, sig_path, MAXPATH))
2588 do_lit_sig = 1;
2589 else if((!IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
2590 && can_access(sig_path, READ_ACCESS) == 0)
2591 ||(IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
2592 && (folder_exists(NULL, sig_path) & FEX_ISFILE)))
2593 do_lit_sig = 0;
2594 else if(!ps_global->vars[V_SIGNATURE_FILE].main_user_val.p
2595 && !ps_global->vars[V_SIGNATURE_FILE].cmdline_val.p
2596 && !ps_global->vars[V_SIGNATURE_FILE].fixed_val.p)
2597 do_lit_sig = 1;
2598 else
2599 do_lit_sig = 0;
2602 if(do_lit_sig){
2603 char *result = NULL;
2604 char **apval;
2605 EditWhich ew;
2606 int readonly = 0;
2608 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
2610 if(ps_global->restricted)
2611 readonly = 1;
2612 else switch(ew){
2613 case Main:
2614 readonly = ps_global->prc->readonly;
2615 break;
2616 case Post:
2617 readonly = ps_global->post_prc->readonly;
2618 break;
2619 default:
2620 break;
2623 if(readonly)
2624 err = cpystr(ps_global->restricted
2625 ? "Alpine demo can't change config file"
2626 : _("Config file not changeable"));
2628 if(!err){
2629 apval = APVAL(&ps_global->vars[V_LITERAL_SIG], ew);
2630 if(!apval)
2631 err = cpystr(_("Problem accessing configuration"));
2632 else{
2633 char *input;
2635 input = (char *)fs_get((strlen(*apval ? *apval : "")+1) *
2636 sizeof(char));
2637 input[0] = '\0';
2638 cstring_to_string(*apval, input);
2639 err = signature_edit_lit(input, &result,
2640 _("SIGNATURE EDITOR"),
2641 h_composer_sigedit);
2642 fs_give((void **)&input);
2646 if(!err){
2647 char *cstring_version;
2649 cstring_version = string_to_cstring(result);
2651 set_variable(V_LITERAL_SIG, cstring_version, 0, 0, ew);
2653 if(cstring_version)
2654 fs_give((void **)&cstring_version);
2657 if(result)
2658 fs_give((void **)&result);
2660 else
2661 err = signature_edit(ps_global->VAR_SIGNATURE_FILE,
2662 _("SIGNATURE EDITOR"));
2664 if(err){
2665 q_status_message(SM_ORDER, 3, 4, err);
2666 fs_give((void **)&err);
2669 ps_global->mangled_screen = 1;
2670 break;
2672 /*----- ADD ADDRESSBOOK ----*/
2673 case 'a':
2674 addr_book_config(ps_global, edit_exceptions);
2675 menu_index = ABOOK_MENU_ITEM;
2676 ps_global->mangled_screen = 1;
2677 break;
2679 #ifdef ENABLE_LDAP
2680 /*--- ADD DIRECTORY SERVER --*/
2681 case 'd':
2682 directory_config(ps_global, edit_exceptions);
2683 ps_global->mangled_screen = 1;
2684 break;
2685 #endif
2687 #ifdef SMIME
2688 /*--- S/MIME --*/
2689 case 'm':
2690 smime_config_screen(ps_global, edit_exceptions);
2691 ps_global->mangled_screen = 1;
2692 break;
2693 #endif
2695 /*----- CONFIGURE OPTIONS -----*/
2696 case 'c':
2697 option_screen(ps_global, edit_exceptions);
2698 ps_global->mangled_screen = 1;
2699 break;
2701 /*----- COLLECTION LIST -----*/
2702 case 'l':
2703 folder_config_screen(ps_global, edit_exceptions);
2704 ps_global->mangled_screen = 1;
2705 break;
2707 /*----- RULES -----*/
2708 case 'r':
2709 rtype = rule_setup_type(ps_global, RS_RULES | RS_INCFILTNOW,
2710 _("Type of rule setup : "));
2711 switch(rtype){
2712 case 'r':
2713 case 's':
2714 case 'i':
2715 case 'f':
2716 case 'o':
2717 case 'c':
2718 role_config_screen(ps_global, (rtype == 'r') ? ROLE_DO_ROLES :
2719 (rtype == 's') ? ROLE_DO_SCORES :
2720 (rtype == 'o') ? ROLE_DO_OTHER :
2721 (rtype == 'f') ? ROLE_DO_FILTER :
2722 (rtype == 'c') ? ROLE_DO_SRCH :
2723 ROLE_DO_INCOLS,
2724 edit_exceptions);
2725 break;
2727 case 'Z':
2728 q_status_message(SM_ORDER | SM_DING, 3, 5,
2729 _("Try turning on color with the Setup/Kolor command."));
2730 break;
2732 case 'n':
2733 role_process_filters();
2734 break;
2736 default:
2737 cmd_cancelled(NULL);
2738 break;
2741 ps_global->mangled_screen = 1;
2742 break;
2744 /*----- COLOR -----*/
2745 case 'k':
2746 color_config_screen(ps_global, edit_exceptions);
2747 ps_global->mangled_screen = 1;
2748 break;
2750 case 'z':
2751 convert_to_remote_config(ps_global, edit_exceptions);
2752 ps_global->mangled_screen = 1;
2753 break;
2755 /*----- EXIT -----*/
2756 case 'e':
2757 break;
2759 /*----- NEW PASSWORD -----*/
2760 case 'n':
2761 #ifdef PASSWD_PROG
2762 if(ps_global->restricted){
2763 q_status_message(SM_ORDER, 3, 5,
2764 "Password change unavailable in restricted demo version of Alpine.");
2765 }else {
2766 change_passwd();
2767 ClearScreen();
2768 ps_global->mangled_screen = 1;
2770 #else
2771 q_status_message(SM_ORDER, 0, 5,
2772 _("Password changing not configured for this version of Alpine."));
2773 display_message('x');
2774 #endif /* DOS */
2775 break;
2777 #if !defined(DOS)
2778 /*----- CHOOSE PRINTER ------*/
2779 case 'p':
2780 select_printer(ps_global, edit_exceptions);
2781 ps_global->mangled_screen = 1;
2782 break;
2783 #endif
2789 rule_setup_type(struct pine *ps, int flags, char *prompt)
2791 ESCKEY_S opts[9];
2792 int ekey_num = 0, deefault = 0;
2794 if(flags & RS_INCADDR){
2795 deefault = 'a';
2796 opts[ekey_num].ch = 'a';
2797 opts[ekey_num].rval = 'a';
2798 opts[ekey_num].name = "A";
2799 opts[ekey_num++].label = "Addrbook";
2802 if(flags & RS_RULES){
2804 if(F_OFF(F_DISABLE_ROLES_SETUP,ps)){ /* roles are allowed */
2805 if(deefault != 'a')
2806 deefault = 'r';
2808 opts[ekey_num].ch = 'r';
2809 opts[ekey_num].rval = 'r';
2810 opts[ekey_num].name = "R";
2811 opts[ekey_num++].label = "Roles";
2813 else if(deefault != 'a')
2814 deefault = 's';
2816 opts[ekey_num].ch = 's';
2817 opts[ekey_num].rval = 's';
2818 opts[ekey_num].name = "S";
2819 opts[ekey_num++].label = "SetScores";
2821 #ifndef _WINDOWS
2822 if(ps->color_style != COL_NONE && pico_hascolor()){
2823 #endif
2824 if(deefault != 'a')
2825 deefault = 'i';
2827 opts[ekey_num].ch = 'i';
2828 opts[ekey_num].rval = 'i';
2829 opts[ekey_num].name = "I";
2830 opts[ekey_num++].label = "Indexcolor";
2831 #ifndef _WINDOWS
2833 else{
2834 opts[ekey_num].ch = 'i';
2835 opts[ekey_num].rval = 'Z'; /* notice this rval! */
2836 opts[ekey_num].name = "I";
2837 opts[ekey_num++].label = "Indexcolor";
2839 #endif
2841 opts[ekey_num].ch = 'f';
2842 opts[ekey_num].rval = 'f';
2843 opts[ekey_num].name = "F";
2844 opts[ekey_num++].label = "Filters";
2846 opts[ekey_num].ch = 'o';
2847 opts[ekey_num].rval = 'o';
2848 opts[ekey_num].name = "O";
2849 opts[ekey_num++].label = "Other";
2851 opts[ekey_num].ch = 'c';
2852 opts[ekey_num].rval = 'c';
2853 opts[ekey_num].name = "C";
2854 opts[ekey_num++].label = "searCh";
2858 if(flags & RS_INCEXP){
2859 opts[ekey_num].ch = 'e';
2860 opts[ekey_num].rval = 'e';
2861 opts[ekey_num].name = "E";
2862 opts[ekey_num++].label = "Export";
2865 if(flags & RS_INCFILTNOW){
2866 opts[ekey_num].ch = 'n';
2867 opts[ekey_num].rval = 'n';
2868 opts[ekey_num].name = "N";
2869 opts[ekey_num++].label = "filterNow";
2872 opts[ekey_num].ch = -1;
2874 return(radio_buttons(prompt, -FOOTER_ROWS(ps), opts,
2875 deefault, 'x', NO_HELP, RB_NORM));
2881 * Process the command list, changing function key notation into
2882 * lexical equivalents.
2884 void
2885 process_init_cmds(struct pine *ps, char **list)
2887 char **p;
2888 int i = 0;
2889 int j;
2890 int lpm1;
2891 #define MAX_INIT_CMDS 500
2892 /* this is just a temporary stack array, the real one is allocated below */
2893 int i_cmds[MAX_INIT_CMDS];
2894 int fkeys = 0;
2895 int not_fkeys = 0;
2897 if(list){
2898 for(p = list; *p; p++){
2899 if(i >= MAX_INIT_CMDS){
2900 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
2901 "Initial keystroke list too long at \"%s\"", *p);
2902 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
2903 break;
2907 /* regular character commands */
2908 if(strlen(*p) == 1){
2909 i_cmds[i++] = **p;
2910 not_fkeys++;
2913 /* special commands */
2914 else if(strucmp(*p, "SPACE") == 0)
2915 i_cmds[i++] = ' ';
2916 else if(strucmp(*p, "CR") == 0)
2917 i_cmds[i++] = '\n';
2918 else if(strucmp(*p, "TAB") == 0)
2919 i_cmds[i++] = '\t';
2920 else if(strucmp(*p, "UP") == 0)
2921 i_cmds[i++] = KEY_UP;
2922 else if(strucmp(*p, "DOWN") == 0)
2923 i_cmds[i++] = KEY_DOWN;
2924 else if(strucmp(*p, "LEFT") == 0)
2925 i_cmds[i++] = KEY_LEFT;
2926 else if(strucmp(*p, "RIGHT") == 0)
2927 i_cmds[i++] = KEY_RIGHT;
2929 /* control chars */
2930 else if(strlen(*p) == 2 && **p == '^')
2931 i_cmds[i++] = ctrl(*((*p)+1));
2933 /* function keys */
2934 else if(**p == 'F' || **p == 'f'){
2935 int v;
2937 fkeys++;
2938 v = atoi((*p)+1);
2939 if(v >= 1 && v <= 12)
2940 i_cmds[i++] = PF1 + v - 1;
2941 else
2942 i_cmds[i++] = KEY_JUNK;
2945 /* literal string */
2946 else if(**p == '"' && (*p)[lpm1 = strlen(*p) - 1] == '"'){
2947 if(lpm1 + i - 1 > MAX_INIT_CMDS){
2948 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
2949 "Initial keystroke list too long, truncated at %s\n", *p);
2950 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
2951 break; /* Bail out of this loop! */
2952 } else
2953 for(j = 1; j < lpm1; j++)
2954 i_cmds[i++] = (*p)[j];
2956 else {
2957 snprintf(tmp_20k_buf,SIZEOF_20KBUF,
2958 "Bad initial keystroke \"%.500s\" (missing comma?)", *p);
2959 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
2960 break;
2966 * We don't handle the case where function keys are used to specify the
2967 * commands but some non-function key input is also required. For example,
2968 * you might want to jump to a specific message number and view it
2969 * on start up. To do that, you need to use character commands instead
2970 * of function key commands in the initial-keystroke-list.
2972 if(fkeys && not_fkeys){
2973 init_error(ps, SM_ORDER | SM_DING, 3, 5,
2974 "Mixed characters and function keys in \"initial-keystroke-list\", skipping.");
2975 i = 0;
2978 if(fkeys && !not_fkeys)
2979 F_TURN_ON(F_USE_FK,ps);
2980 if(!fkeys && not_fkeys)
2981 F_TURN_OFF(F_USE_FK,ps);
2983 if(i > 0){
2984 ps->initial_cmds = (int *)fs_get((i+1) * sizeof(int));
2985 ps->free_initial_cmds = ps->initial_cmds;
2986 for(j = 0; j < i; j++)
2987 ps->initial_cmds[j] = i_cmds[j];
2989 ps->initial_cmds[i] = 0;
2990 ps->in_init_seq = ps->save_in_init_seq = 1;
2995 UCS *
2996 user_wordseps(char **list)
2998 char **p;
2999 int i = 0;
3000 int j;
3001 #define MAX_SEPARATORS 500
3003 * This is just a temporary stack array, the real one is allocated below.
3004 * This is supposed to be way large enough.
3006 UCS seps[MAX_SEPARATORS+1];
3007 UCS *u;
3008 UCS *return_array = NULL;
3009 size_t l;
3011 seps[0] = '\0';
3013 if(list){
3014 for(p = list; *p; p++){
3015 if(i >= MAX_SEPARATORS){
3016 q_status_message(SM_ORDER | SM_DING, 3, 3,
3017 "Warning: composer-word-separators list is too long");
3018 break;
3021 u = utf8_to_ucs4_cpystr(*p);
3023 if(u){
3024 if(ucs4_strlen(u) == 1)
3025 seps[i++] = *u;
3026 else if(*u == '"' && u[l = ucs4_strlen(u) - 1] == '"'){
3027 if(l + i - 1 > MAX_SEPARATORS){
3028 q_status_message(SM_ORDER | SM_DING, 3, 3,
3029 "Warning: composer-word-separators list is too long");
3030 break; /* Bail out of this loop! */
3032 else{
3033 for(j = 1; j < l; j++)
3034 seps[i++] = u[j];
3037 else{
3038 l = ucs4_strlen(u);
3039 if(l + i > MAX_SEPARATORS){
3040 q_status_message(SM_ORDER | SM_DING, 3, 3,
3041 "Warning: composer-word-separators list is too long");
3042 break; /* Bail out of this loop! */
3044 else{
3045 for(j = 0; j < l; j++)
3046 seps[i++] = u[j];
3050 fs_give((void **) &u);
3055 seps[i] = '\0';
3057 if(i > 0)
3058 return_array = ucs4_cpystr(seps);
3060 return(return_array);
3065 * Make sure any errors during initialization get queued for display
3067 void
3068 queue_init_errors(struct pine *ps)
3070 int i;
3072 if(ps->init_errs){
3073 for(i = 0; (ps->init_errs)[i].message; i++){
3074 q_status_message((ps->init_errs)[i].flags,
3075 (ps->init_errs)[i].min_time,
3076 (ps->init_errs)[i].max_time,
3077 (ps->init_errs)[i].message);
3078 fs_give((void **)&(ps->init_errs)[i].message);
3081 fs_give((void **)&ps->init_errs);
3086 /*----------------------------------------------------------------------
3087 Quit pine if the user wants to
3089 Args: The usual pine structure
3091 Result: User is asked if she wants to quit, if yes then execute quit.
3093 Q U I T S C R E E N
3095 Not really a full screen. Just count up deletions and ask if we really
3096 want to quit.
3097 ----*/
3098 void
3099 quit_screen(struct pine *pine_state)
3101 int quit = 0;
3103 dprint((1, "\n\n ---- QUIT SCREEN ----\n"));
3105 if(F_ON(F_CHECK_MAIL_ONQUIT,ps_global)
3106 && new_mail(1, VeryBadTime, NM_STATUS_MSG | NM_DEFER_SORT) > 0
3107 && (quit = want_to(_("Quit even though new mail just arrived"), 'y', 0,
3108 NO_HELP, WT_NORM)) != 'y'){
3109 refresh_sort(pine_state->mail_stream, pine_state->msgmap, SRT_VRB);
3110 pine_state->next_screen = pine_state->prev_screen;
3111 return;
3114 if(quit != 'y'
3115 && F_OFF(F_QUIT_WO_CONFIRM,pine_state)
3116 && want_to(_("Really quit Alpine"), 'y', 0, NO_HELP, WT_NORM) != 'y'){
3117 pine_state->next_screen = pine_state->prev_screen;
3118 return;
3121 goodnight_gracey(pine_state, 0);
3125 /*----------------------------------------------------------------------
3126 The nuts and bolts of actually cleaning up and exitting pine
3128 Args: ps -- the usual pine structure,
3129 exit_val -- what to tell our parent
3131 Result: This never returns
3133 ----*/
3134 void
3135 goodnight_gracey(struct pine *pine_state, int exit_val)
3137 int i, cnt_user_streams = 0;
3138 char *final_msg = NULL;
3139 char msg[MAX_SCREEN_COLS+1];
3140 char *pf = _("Alpine finished");
3141 MAILSTREAM *m;
3142 extern KBESC_T *kbesc;
3144 dprint((2, "goodnight_gracey:\n"));
3146 /* We want to do this here before we close up the streams */
3147 trim_remote_adrbks();
3149 for(i = 0; i < ps_global->s_pool.nstream; i++){
3150 m = ps_global->s_pool.streams[i];
3151 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR))
3152 cnt_user_streams++;
3155 /* clean up open streams */
3157 if(pine_state->mail_stream
3158 && sp_flagged(pine_state->mail_stream, SP_LOCKED)
3159 && sp_flagged(pine_state->mail_stream, SP_USERFLDR)){
3160 dprint((5, "goodnight_gracey: close current stream\n"));
3161 expunge_and_close(pine_state->mail_stream,
3162 (cnt_user_streams <= 1) ? &final_msg : NULL, EC_NONE);
3163 cnt_user_streams--;
3166 pine_state->mail_stream = NULL;
3167 pine_state->redrawer = (void (*)(void))NULL;
3169 dprint((5,
3170 "goodnight_gracey: close other stream pool streams\n"));
3171 for(i = 0; i < ps_global->s_pool.nstream; i++){
3172 m = ps_global->s_pool.streams[i];
3174 * fix global for functions that depend(ed) on it sort_folder.
3175 * Hopefully those will get phased out.
3177 ps_global->mail_stream = m;
3178 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
3179 && !sp_flagged(m, SP_INBOX)){
3180 sp_set_expunge_count(m, 0L);
3181 expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
3182 EC_NONE);
3183 cnt_user_streams--;
3187 for(i = 0; i < ps_global->s_pool.nstream; i++){
3188 m = ps_global->s_pool.streams[i];
3190 * fix global for functions that depend(ed) on it (sort_folder).
3191 * Hopefully those will get phased out.
3193 ps_global->mail_stream = m;
3194 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
3195 && sp_flagged(m, SP_INBOX)){
3196 dprint((5,
3197 "goodnight_gracey: close inbox stream stream\n"));
3198 sp_set_expunge_count(m, 0L);
3199 expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
3200 EC_NONE);
3201 cnt_user_streams--;
3205 #ifdef _WINDOWS
3206 if(ps_global->ttyo)
3207 (void)get_windsize(ps_global->ttyo);
3208 #endif
3210 dprint((7, "goodnight_gracey: close config files\n"));
3212 #ifdef SMIME
3213 smime_deinit();
3214 #endif
3216 free_pinerc_strings(&pine_state);
3218 strncpy(msg, pf, sizeof(msg));
3219 msg[sizeof(msg)-1] = '\0';
3220 if(final_msg){
3221 strncat(msg, " -- ", sizeof(msg)-strlen(msg)-1);
3222 msg[sizeof(msg)-1] = '\0';
3223 strncat(msg, final_msg, sizeof(msg)-strlen(msg)-1);
3224 msg[sizeof(msg)-1] = '\0';
3225 fs_give((void **)&final_msg);
3228 dprint((7, "goodnight_gracey: sp_end\n"));
3229 ps_global->noshow_error = 1;
3230 sp_end();
3232 /* after sp_end, which might call a filter */
3233 completely_done_with_adrbks();
3235 dprint((7, "goodnight_gracey: end_screen\n"));
3236 end_screen(msg, exit_val);
3237 dprint((7, "goodnight_gracey: end_titlebar\n"));
3238 end_titlebar();
3239 dprint((7, "goodnight_gracey: end_keymenu\n"));
3240 end_keymenu();
3242 dprint((7, "goodnight_gracey: end_keyboard\n"));
3243 end_keyboard(F_ON(F_USE_FK,pine_state));
3244 dprint((7, "goodnight_gracey: end_ttydriver\n"));
3245 end_tty_driver(pine_state);
3246 #if !defined(DOS) && !defined(OS2)
3247 kbdestroy(kbesc);
3248 #if !defined(LEAVEOUTFIFO)
3249 close_newmailfifo();
3250 #endif
3251 #endif
3252 end_signals(0);
3253 if(filter_data_file(0))
3254 our_unlink(filter_data_file(0));
3256 imap_flush_passwd_cache(TRUE);
3257 free_newsgrp_cache();
3258 mailcap_free();
3259 close_every_pattern();
3260 free_extra_hdrs();
3261 free_contexts(&ps_global->context_list);
3262 free_charsetchecker();
3263 dprint((7, "goodnight_gracey: free more memory\n"));
3264 #ifdef ENABLE_LDAP
3265 free_saved_query_parameters();
3266 #endif
3268 free_pine_struct(&pine_state);
3270 free_histlist();
3272 #ifdef DEBUG
3273 if(debugfile){
3274 if(debug >= 2)
3275 fputs("goodnight_gracey finished\n", debugfile);
3277 fclose(debugfile);
3279 #endif
3281 exit(exit_val);
3285 /*----------------------------------------------------------------------
3286 Call back for c-client to feed us back the progress of network reads
3288 Input:
3290 Result:
3291 ----*/
3292 void
3293 pine_read_progress(GETS_DATA *md, long unsigned int count)
3295 gets_bytes += count; /* update counter */
3299 /*----------------------------------------------------------------------
3300 Function to fish the current byte count from a c-client fetch.
3302 Input: reset -- flag telling us to reset the count
3304 Result: Returns the number of bytes read by the c-client so far
3305 ----*/
3306 unsigned long
3307 pine_gets_bytes(int reset)
3309 if(reset)
3310 gets_bytes = 0L;
3312 return(gets_bytes);
3316 /*----------------------------------------------------------------------
3317 Panic pine - call on detected programmatic errors to exit pine
3319 Args: message -- message to record in debug file and to be printed for user
3321 Result: The various tty modes are restored
3322 If debugging is active a core dump will be generated
3323 Exits Alpine
3325 This is also called from imap routines and fs_get and fs_resize.
3326 ----*/
3327 void
3328 alpine_panic(char *message)
3330 char buf[256];
3332 /* global variable in .../pico/edef.h */
3333 in_panic = 1;
3335 if(ps_global->ttyo){
3336 end_screen(NULL, -1);
3337 end_keyboard(ps_global != NULL ? F_ON(F_USE_FK,ps_global) : 0);
3338 end_tty_driver(ps_global);
3339 end_signals(1);
3341 if(filter_data_file(0))
3342 our_unlink(filter_data_file(0));
3344 dprint((1, "\n===========================================\n\n"));
3345 dprint((1, " Alpine Panic: %s\n\n", message ? message : "?"));
3346 dprint((1, "===========================================\n\n"));
3348 /* intercept c-client "free storage" errors */
3349 if(strstr(message, "free storage"))
3350 snprintf(buf, sizeof(buf), _("No more available memory.\nAlpine Exiting"));
3351 else
3352 snprintf(buf, sizeof(buf), _("Problem detected: \"%s\".\nAlpine Exiting."), message);
3354 buf[sizeof(buf)-1] = '\0';
3356 #ifdef _WINDOWS
3357 /* Put up a message box. */
3358 mswin_messagebox (buf, 1);
3359 #else
3360 fprintf(stderr, "\n\n%s\n", buf);
3361 #endif
3363 #ifdef DEBUG
3364 if(debugfile){
3365 save_debug_on_crash(debugfile, recent_keystroke);
3368 coredump(); /*--- If we're debugging get a core dump --*/
3369 #endif
3371 exit(-1);
3372 fatal("ffo"); /* BUG -- hack to get fatal out of library in right order*/
3377 * panicking - function to test whether or not we're exiting under stress.
3381 panicking(void)
3383 return(in_panic);
3387 /*----------------------------------------------------------------------
3388 exceptional_exit - called to exit under unusual conditions (with no core)
3390 Args: message -- message to record in debug file and to be printed for user
3391 ev -- exit value
3393 ----*/
3394 void
3395 exceptional_exit(char *message, int ev)
3397 fprintf(stderr, "%s\n", message);
3398 exit(ev);
3403 * PicoText Storage Object Support Routines
3406 STORE_S *
3407 pine_pico_get(void)
3409 return((STORE_S *)pico_get());
3413 pine_pico_give(STORE_S **sop)
3415 pico_give((void *)sop);
3416 return(1);
3420 pine_pico_writec(int c, STORE_S *so)
3422 unsigned char ch = (unsigned char) c;
3424 return(pico_writec(so->txt, ch, PICOREADC_NONE));
3428 pine_pico_writec_noucs(int c, STORE_S *so)
3430 unsigned char ch = (unsigned char) c;
3432 return(pico_writec(so->txt, ch, PICOREADC_NOUCS));
3436 pine_pico_readc(unsigned char *c, STORE_S *so)
3438 return(pico_readc(so->txt, c, PICOREADC_NONE));
3442 pine_pico_readc_noucs(unsigned char *c, STORE_S *so)
3444 return(pico_readc(so->txt, c, PICOREADC_NOUCS));
3448 pine_pico_puts(STORE_S *so, char *s)
3450 return(pico_puts(so->txt, s, PICOREADC_NONE));
3454 pine_pico_puts_noucs(STORE_S *so, char *s)
3456 return(pico_puts(so->txt, s, PICOREADC_NOUCS));
3460 pine_pico_seek(STORE_S *so, long pos, int orig)
3462 return(pico_seek((void *)so, pos, orig));
3467 remote_pinerc_failure(void)
3469 #ifdef _WINDOWS
3470 if(ps_global->install_flag) /* just exit silently */
3471 exit(0);
3472 #endif /* _WINDOWS */
3474 if(ps_global->exit_if_no_pinerc){
3475 exceptional_exit("Exiting because -bail option is set and config file not readable.", -1);
3478 if(want_to("Trouble reading remote configuration! Continue anyway ",
3479 'n', 'n', NO_HELP, WT_FLUSH_IN) != 'y'){
3480 return(0);
3483 return(1);
3487 void
3488 dump_supported_options(void)
3490 char **config;
3492 config = get_supported_options();
3493 if(config){
3494 display_args_err(NULL, config, 0);
3495 free_list_array(&config);
3500 /*----------------------------------------------------------------------
3501 Check pruned-folders for validity, making sure they are in the
3502 same context as sent-mail.
3504 ----*/
3506 prune_folders_ok(void)
3508 char **p;
3510 for(p = ps_global->VAR_PRUNED_FOLDERS; p && *p && **p; p++)
3511 if(!context_isambig(*p))
3512 return(0);
3514 return(1);
3518 #ifdef WIN32
3519 char *
3520 pine_user_callback()
3522 if(ps_global->VAR_USER_ID && ps_global->VAR_USER_ID[0]){
3523 return(ps_global->VAR_USER_ID);
3525 else{
3526 /* SHOULD PROMPT HERE! */
3527 return(NULL);
3530 #endif
3532 #ifdef _WINDOWS
3534 * windows callback to get/set function keys mode state
3537 fkey_mode_callback(set, args)
3538 int set;
3539 long args;
3541 return(F_ON(F_USE_FK, ps_global) != 0);
3545 void
3546 imap_telemetry_on()
3548 if(ps_global->mail_stream)
3549 mail_debug(ps_global->mail_stream);
3553 void
3554 imap_telemetry_off()
3556 if(ps_global->mail_stream)
3557 mail_nodebug(ps_global->mail_stream);
3561 char *
3562 pcpine_help_main(title)
3563 char *title;
3565 if(title)
3566 strncpy(title, _("PC-Alpine MAIN MENU Help"), 256);
3568 return(pcpine_help(main_menu_tx));
3573 pcpine_main_cursor(col, row)
3574 int col;
3575 long row;
3577 unsigned ndmi;
3579 if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global)))
3580 ndmi = (row+1 - HEADER_ROWS(ps_global) - (MNSKIP(ps_global)+1))/(MNSKIP(ps_global)+1);
3582 if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global))
3583 && !(MNSKIP(ps_global) && (row+1) & 0x01)
3584 && ndmi <= MAX_MENU_ITEM
3585 && FOOTER_ROWS(ps_global) + (ndmi+1)*(MNSKIP(ps_global)+1)
3586 + MNSKIP(ps_global) + FOOTER_ROWS(ps_global) <= ps_global->ttyo->screen_rows)
3587 return(MSWIN_CURSOR_HAND);
3588 else
3589 return(MSWIN_CURSOR_ARROW);
3591 #endif /* _WINDOWS */