* Patches from MichaƂ Dardas and Mateusz Kocielski from LogicalTrust
[alpine.git] / alpine / imap.c
blob1fc6c76f3877a1d2a7770bc57ee4014ae18bdc3b
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: imap.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2018 Eduardo Chappa
8 * Copyright 2006-2009 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 /*======================================================================
20 imap.c
21 The call back routines for the c-client/imap
22 - handles error messages and other notification
23 - handles prelimirary notification of new mail and expunged mail
24 - prompting for imap server login and password
26 ====*/
28 #include "headers.h"
29 #include "alpine.h"
30 #include "imap.h"
31 #include "status.h"
32 #include "mailview.h"
33 #include "mailcmd.h"
34 #include "radio.h"
35 #include "keymenu.h"
36 #include "signal.h"
37 #include "mailpart.h"
38 #include "mailindx.h"
39 #include "arg.h"
40 #include "busy.h"
41 #include "titlebar.h"
42 #include "../pith/state.h"
43 #include "../pith/conf.h"
44 #include "../pith/msgno.h"
45 #include "../pith/filter.h"
46 #include "../pith/news.h"
47 #include "../pith/util.h"
48 #include "../pith/list.h"
49 #include "../pith/margin.h"
50 #ifdef SMIME
51 #include "../pith/smime.h"
52 #endif /* SMIME */
54 #if (WINCRED > 0)
55 #include <wincred.h>
56 #define TNAME "UWash_Alpine_"
57 #define TNAMESTAR "UWash_Alpine_*"
60 * WinCred Function prototypes
62 typedef BOOL (WINAPI CREDWRITEW) ( __in PCREDENTIALW Credential, __in DWORD Flags );
63 typedef BOOL (WINAPI CREDENUMERATEW) ( __in LPCWSTR Filter, __reserved DWORD Flags,
64 __out DWORD *Count, __deref_out_ecount(*Count) PCREDENTIALW **Credential );
65 typedef BOOL (WINAPI CREDDELETEW) ( __in LPCWSTR TargetName, __in DWORD Type,
66 __reserved DWORD Flags );
67 typedef VOID (WINAPI CREDFREE) ( __in PVOID Buffer );
70 * WinCred functions
72 int g_CredInited = 0; /* 1 for loaded successfully,
73 * -1 for not available.
74 * 0 for not initialized yet.
76 CREDWRITEW *g_CredWriteW;
77 CREDENUMERATEW *g_CredEnumerateW;
78 CREDDELETEW *g_CredDeleteW;
79 CREDFREE *g_CredFree;
81 #endif /* WINCRED */
83 #ifdef APPLEKEYCHAIN
84 #include <Security/SecKeychain.h>
85 #include <Security/SecKeychainItem.h>
86 #include <Security/SecKeychainSearch.h>
87 #define TNAME "UWash_Alpine"
88 #define TNAMEPROMPT "UWash_Alpine_Prompt_For_Password"
90 int macos_store_pass_prompt(void);
91 void macos_set_store_pass_prompt(int);
93 static int storepassprompt = -1;
94 #endif /* APPLEKEYCHAIN */
98 * Internal prototypes
100 void mm_login_alt_cue(NETMBX *);
101 long pine_tcptimeout_noscreen(long, long, char *);
102 int answer_cert_failure(int, MSGNO_S *, SCROLL_S *);
104 #ifdef LOCAL_PASSWD_CACHE
105 int read_passfile(char *, MMLOGIN_S **);
106 void write_passfile(char *, MMLOGIN_S *);
107 int preserve_prompt(char *);
108 void update_passfile_hostlist(char *, char *, STRLIST_S *, int);
109 void free_passfile_cache_work(MMLOGIN_S **);
111 static MMLOGIN_S *passfile_cache = NULL;
112 static int using_passfile = -1;
113 int save_password = 1;
114 #endif /* LOCAL_PASSWD_CACHE */
116 #ifdef PASSFILE
117 char xlate_in(int);
118 char xlate_out(char);
119 int line_get(char *, size_t, char **);
120 #endif /* PASSFILE */
122 #if (WINCRED > 0)
123 void ask_erase_credentials(void);
124 int init_wincred_funcs(void);
125 #endif /* WINCRED */
128 static char *details_cert, *details_host, *details_reason;
131 /*----------------------------------------------------------------------
132 recieve notification from IMAP
134 Args: stream -- Mail stream message is relavant to
135 string -- The message text
136 errflg -- Set if it is a serious error
138 Result: message displayed in status line
140 The facility is for general notices, such as connection to server;
141 server shutting down etc... It is used infrequently.
142 ----------------------------------------------------------------------*/
143 void
144 mm_notify(MAILSTREAM *stream, char *string, long int errflg)
146 time_t now;
147 struct tm *tm_now;
149 now = time((time_t *)0);
150 tm_now = localtime(&now);
152 /* be sure to log the message... */
153 #ifdef DEBUG
154 if(ps_global->debug_imap || ps_global->debugmem)
155 dprint((errflg == TCPDEBUG ? 7 : 2,
156 "IMAP %2.2d:%2.2d:%2.2d %d/%d mm_notify %s: %s: %s\n",
157 tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
158 tm_now->tm_mon+1, tm_now->tm_mday,
159 (!errflg) ? "babble" :
160 (errflg == ERROR) ? "error" :
161 (errflg == WARN) ? "warning" :
162 (errflg == PARSE) ? "parse" :
163 (errflg == TCPDEBUG) ? "tcp" :
164 (errflg == BYE) ? "bye" : "unknown",
165 (stream && stream->mailbox) ? stream->mailbox : "-no folder-",
166 string ? string : "?"));
167 #endif
169 snprintf(ps_global->last_error, sizeof(ps_global->last_error), "%s : %.*s",
170 (stream && stream->mailbox) ? stream->mailbox : "-no folder-",
171 (int) MIN(MAX_SCREEN_COLS, sizeof(ps_global->last_error)-70),
172 string);
173 ps_global->last_error[ps_global->ttyo ? ps_global->ttyo->screen_cols
174 : sizeof(ps_global->last_error)-1] = '\0';
177 * Then either set special bits in the pine struct or
178 * display the message if it's tagged as an "ALERT" or
179 * its errflg > NIL (i.e., WARN, or ERROR)
181 if(errflg == BYE)
183 * We'd like to sp_mark_stream_dead() here but we can't do that because
184 * that might call mail_close and we are already in a c-client callback.
185 * So just set the dead bit and clean it up later.
187 sp_set_dead_stream(stream, 1);
188 else if(!strncmp(string, "[TRYCREATE]", 11))
189 ps_global->try_to_create = 1;
190 else if(!strncmp(string, "[REFERRAL ", 10))
191 ; /* handled in the imap_referral() callback */
192 else if(!strncmp(string, "[ALERT]", 7))
193 q_status_message2(SM_MODAL, 3, 3,
194 _("Alert received while accessing \"%s\": %s"),
195 (stream && stream->mailbox)
196 ? stream->mailbox : "-no folder-",
197 rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000),
198 SIZEOF_20KBUF-10000, string));
199 else if(!strncmp(string, "[UNSEEN ", 8)){
200 char *p;
201 long n = 0;
203 for(p = string + 8; isdigit(*p); p++)
204 n = (n * 10) + (*p - '0');
206 sp_set_first_unseen(stream, n);
208 else if(!strncmp(string, "[READ-ONLY]", 11)
209 && !(stream && stream->mailbox && IS_NEWS(stream)))
210 q_status_message2(SM_ORDER | SM_DING, 3, 3, "%s : %s",
211 (stream && stream->mailbox)
212 ? stream->mailbox : "-no folder-",
213 string + 11);
214 else if((errflg && errflg != BYE && errflg != PARSE)
215 && !ps_global->noshow_error
216 && !(errflg == WARN
217 && (ps_global->noshow_warn || (stream && stream->unhealthy))))
218 q_status_message(SM_ORDER | ((errflg == ERROR) ? SM_DING : 0),
219 3, 6, ps_global->last_error);
223 /*----------------------------------------------------------------------
224 Queue imap log message for display in the message line
226 Args: string -- The message
227 errflg -- flag set to 1 if pertains to an error
229 Result: Message queued for display
231 The c-client/imap reports most of it's status and errors here
232 ---*/
233 void
234 mm_log(char *string, long int errflg)
236 char message[sizeof(ps_global->c_client_error)];
237 char *occurence;
238 int was_capitalized;
239 static char saw_kerberos_init_warning;
240 time_t now;
241 struct tm *tm_now;
243 now = time((time_t *)0);
244 tm_now = localtime(&now);
246 dprint((((errflg == TCPDEBUG) && ps_global->debug_tcp) ? 1 :
247 (errflg == TCPDEBUG) ? 10 : 2,
248 "IMAP %2.2d:%2.2d:%2.2d %d/%d mm_log %s: %s\n",
249 tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
250 tm_now->tm_mon+1, tm_now->tm_mday,
251 (!errflg) ? "babble" :
252 (errflg == ERROR) ? "error" :
253 (errflg == WARN) ? "warning" :
254 (errflg == PARSE) ? "parse" :
255 (errflg == TCPDEBUG) ? "tcp" :
256 (errflg == BYE) ? "bye" : "unknown",
257 string ? string : "?"));
259 if(errflg == ERROR && !strncmp(string, "[TRYCREATE]", 11)){
260 ps_global->try_to_create = 1;
261 return;
263 else if(ps_global->try_to_create
264 || !strncmp(string, "[CLOSED]", 8)
265 || (sp_dead_stream(ps_global->mail_stream) && strstr(string, "No-op")))
267 * Don't display if creating new folder OR
268 * warning about a dead stream ...
270 return;
272 strncpy(message, string, sizeof(message));
273 message[sizeof(message) - 1] = '\0';
275 if(errflg == WARN && srchstr(message, "try running kinit") != NULL){
276 if(saw_kerberos_init_warning)
277 return;
279 saw_kerberos_init_warning = 1;
282 /*---- replace all "mailbox" with "folder" ------*/
283 occurence = srchstr(message, "mailbox");
284 while(occurence) {
285 if(!*(occurence+7)
286 || isspace((unsigned char) *(occurence+7))
287 || *(occurence+7) == ':'){
288 was_capitalized = isupper((unsigned char) *occurence);
289 rplstr(occurence, sizeof(message)-(occurence-message), 7, (errflg == PARSE ? "address" : "folder"));
290 if(was_capitalized)
291 *occurence = (errflg == PARSE ? 'A' : 'F');
293 else
294 occurence += 7;
296 occurence = srchstr(occurence, "mailbox");
299 /*---- replace all "GSSAPI" with "Kerberos" ------*/
300 occurence = srchstr(message, "GSSAPI");
301 while(occurence) {
302 if(!*(occurence+6)
303 || isspace((unsigned char) *(occurence+6))
304 || *(occurence+6) == ':')
305 rplstr(occurence, sizeof(message)-(occurence-message), 6, "Kerberos");
306 else
307 occurence += 6;
309 occurence = srchstr(occurence, "GSSAPI");
312 if(errflg == ERROR)
313 ps_global->mm_log_error = 1;
315 if(errflg == PARSE || (errflg == ERROR && ps_global->noshow_error))
316 strncpy(ps_global->c_client_error, message,
317 sizeof(ps_global->c_client_error));
319 if(ps_global->noshow_error
320 || (ps_global->noshow_warn && errflg == WARN)
321 || !(errflg == ERROR || errflg == WARN))
322 return; /* Only care about errors; don't print when asked not to */
324 /*---- Display the message ------*/
325 q_status_message((errflg == ERROR) ? (SM_ORDER | SM_DING) : SM_ORDER,
326 3, 5, message);
327 strncpy(ps_global->last_error, message, sizeof(ps_global->last_error));
328 ps_global->last_error[sizeof(ps_global->last_error) - 1] = '\0';
332 void
333 mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
334 char *usethisprompt, char *altuserforcache)
336 char prompt[1000], *last;
337 char port[20], non_def_port[20], insecure[20];
338 char defuser[NETMAXUSER];
339 char hostleadin[80], hostname[200], defubuf[200];
340 char logleadin[80], pwleadin[50];
341 char hostlist0[MAILTMPLEN], hostlist1[MAILTMPLEN];
342 /* TRANSLATORS: when logging in, this text is added to the prompt to show
343 that the password will be sent unencrypted over the network. This is
344 just a warning message that gets added parenthetically when the user
345 is asked for a password. */
346 char *insec = _(" (INSECURE)");
347 /* TRANSLATORS: Retrying is shown when the user is being asked for a password
348 after having already failed at least once. */
349 char *retry = _("Retrying - ");
350 /* TRANSLATORS: A label for the hostname that the user is logging in on */
351 char *hostlabel = _("HOST");
352 /* TRANSLATORS: user is logging in as a particular user (a particular
353 login name), this is just labelling that user name. */
354 char *userlabel = _("USER");
355 STRLIST_S hostlist[2];
356 HelpType help ;
357 int len, rc, q_line, flags;
358 int oespace, avail, need, save_dont_use;
359 int save_in_init;
360 struct servent *sv;
361 #if defined(_WINDOWS) || defined(LOCAL_PASSWD_CACHE)
362 int preserve_password = -1;
363 #endif
365 dprint((9, "mm_login_work trial=%ld user=%s service=%s%s%s%s%s\n",
366 trial, mb->user ? mb->user : "(null)",
367 mb->service ? mb->service : "(null)",
368 mb->port ? " port=" : "",
369 mb->port ? comatose(mb->port) : "",
370 altuserforcache ? " altuserforcache =" : "",
371 altuserforcache ? altuserforcache : ""));
372 q_line = -(ps_global->ttyo ? ps_global->ttyo->footer_rows : 3);
374 save_in_init = ps_global->in_init_seq;
375 ps_global->in_init_seq = 0;
376 ps_global->no_newmail_check_from_optionally_enter = 1;
378 /* make sure errors are seen */
379 if(ps_global->ttyo)
380 flush_status_messages(0);
383 * Add port number to hostname if going through a tunnel or something
385 non_def_port[0] = '\0';
386 if(mb->port && mb->service &&
387 (sv = getservbyname(mb->service, "tcp")) &&
388 (mb->port != ntohs(sv->s_port))){
389 snprintf(non_def_port, sizeof(non_def_port), ":%lu", mb->port);
390 non_def_port[sizeof(non_def_port)-1] = '\0';
391 dprint((9, "mm_login: using non-default port=%s\n",
392 non_def_port ? non_def_port : "?"));
396 * set up host list for sybil servers...
398 if(*non_def_port){
399 strncpy(hostlist0, mb->host, sizeof(hostlist0)-1);
400 hostlist0[sizeof(hostlist0)-1] = '\0';
401 strncat(hostlist0, non_def_port, sizeof(hostlist0)-strlen(hostlist0)-1);
402 hostlist0[sizeof(hostlist0)-1] = '\0';
403 hostlist[0].name = hostlist0;
404 if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)){
405 strncpy(hostlist1, mb->orighost, sizeof(hostlist1)-1);
406 hostlist1[sizeof(hostlist1)-1] = '\0';
407 strncat(hostlist1, non_def_port, sizeof(hostlist1)-strlen(hostlist1)-1);
408 hostlist1[sizeof(hostlist1)-1] = '\0';
409 hostlist[0].next = &hostlist[1];
410 hostlist[1].name = hostlist1;
411 hostlist[1].next = NULL;
413 else
414 hostlist[0].next = NULL;
416 else{
417 hostlist[0].name = mb->host;
418 if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)){
419 hostlist[0].next = &hostlist[1];
420 hostlist[1].name = mb->orighost;
421 hostlist[1].next = NULL;
423 else
424 hostlist[0].next = NULL;
427 if(hostlist[0].name){
428 dprint((9, "mm_login: host=%s\n",
429 hostlist[0].name ? hostlist[0].name : "?"));
430 if(hostlist[0].next && hostlist[1].name){
431 dprint((9, "mm_login: orighost=%s\n", hostlist[1].name));
436 * Initialize user name with either
437 * 1) /user= value in the stream being logged into,
438 * or 2) the user name we're running under.
440 * Note that VAR_USER_ID is not yet initialized if this login is
441 * the one to access the remote config file. In that case, the user
442 * can supply the username in the config file name with /user=.
444 if(trial == 0L && !altuserforcache){
445 strncpy(user, (*mb->user) ? mb->user :
446 ps_global->VAR_USER_ID ? ps_global->VAR_USER_ID : "",
447 NETMAXUSER);
448 user[NETMAXUSER-1] = '\0';
450 /* try last working password associated with this host. */
451 if(imap_get_passwd(mm_login_list, pwd, user, hostlist,
452 (mb->sslflag||mb->tlsflag))){
453 dprint((9, "mm_login: found a password to try\n"));
454 ps_global->no_newmail_check_from_optionally_enter = 0;
455 ps_global->in_init_seq = save_in_init;
456 return;
459 #ifdef LOCAL_PASSWD_CACHE
460 /* check to see if there's a password left over from last session */
461 if(get_passfile_passwd(ps_global->pinerc, pwd,
462 user, hostlist, (mb->sslflag||mb->tlsflag))){
463 imap_set_passwd(&mm_login_list, pwd, user,
464 hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
465 update_passfile_hostlist(ps_global->pinerc, user, hostlist,
466 (mb->sslflag||mb->tlsflag));
467 dprint((9, "mm_login: found a password in passfile to try\n"));
468 ps_global->no_newmail_check_from_optionally_enter = 0;
469 ps_global->in_init_seq = save_in_init;
470 return;
472 #endif /* LOCAL_PASSWD_CACHE */
475 * If no explicit user name supplied and we've not logged in
476 * with our local user name, see if we've visited this
477 * host before as someone else.
479 if(!*mb->user &&
480 ((last = imap_get_user(mm_login_list, hostlist))
481 #ifdef LOCAL_PASSWD_CACHE
483 (last = get_passfile_user(ps_global->pinerc, hostlist))
484 #endif /* LOCAL_PASSWD_CACHE */
486 strncpy(user, last, NETMAXUSER);
487 user[NETMAXUSER-1] = '\0';
488 dprint((9, "mm_login: found user=%s\n",
489 user ? user : "?"));
491 /* try last working password associated with this host/user. */
492 if(imap_get_passwd(mm_login_list, pwd, user, hostlist,
493 (mb->sslflag||mb->tlsflag))){
494 dprint((9,
495 "mm_login: found a password for user=%s to try\n",
496 user ? user : "?"));
497 ps_global->no_newmail_check_from_optionally_enter = 0;
498 ps_global->in_init_seq = save_in_init;
499 return;
502 #ifdef LOCAL_PASSWD_CACHE
503 /* check to see if there's a password left over from last session */
504 if(get_passfile_passwd(ps_global->pinerc, pwd,
505 user, hostlist, (mb->sslflag||mb->tlsflag))){
506 imap_set_passwd(&mm_login_list, pwd, user,
507 hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
508 update_passfile_hostlist(ps_global->pinerc, user, hostlist,
509 (mb->sslflag||mb->tlsflag));
510 dprint((9,
511 "mm_login: found a password for user=%s in passfile to try\n",
512 user ? user : "?"));
513 ps_global->no_newmail_check_from_optionally_enter = 0;
514 ps_global->in_init_seq = save_in_init;
515 return;
517 #endif /* LOCAL_PASSWD_CACHE */
520 #if !defined(DOS) && !defined(OS2)
521 if(!*mb->user && !*user &&
522 (last = (ps_global->ui.login && ps_global->ui.login[0])
523 ? ps_global->ui.login : NULL)
525 strncpy(user, last, NETMAXUSER);
526 user[NETMAXUSER-1] = '\0';
527 dprint((9, "mm_login: found user=%s\n",
528 user ? user : "?"));
530 /* try last working password associated with this host. */
531 if(imap_get_passwd(mm_login_list, pwd, user, hostlist,
532 (mb->sslflag||mb->tlsflag))){
533 dprint((9, "mm_login:ui: found a password to try\n"));
534 ps_global->no_newmail_check_from_optionally_enter = 0;
535 ps_global->in_init_seq = save_in_init;
536 return;
539 #ifdef LOCAL_PASSWD_CACHE
540 /* check to see if there's a password left over from last session */
541 if(get_passfile_passwd(ps_global->pinerc, pwd,
542 user, hostlist, (mb->sslflag||mb->tlsflag))){
543 imap_set_passwd(&mm_login_list, pwd, user,
544 hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
545 update_passfile_hostlist(ps_global->pinerc, user, hostlist,
546 (mb->sslflag||mb->tlsflag));
547 dprint((9, "mm_login:ui: found a password in passfile to try\n"));
548 ps_global->no_newmail_check_from_optionally_enter = 0;
549 ps_global->in_init_seq = save_in_init;
550 return;
552 #endif /* LOCAL_PASSWD_CACHE */
554 #endif
557 user[NETMAXUSER-1] = '\0';
559 if(trial == 0)
560 retry = "";
563 * Even if we have a user now, user gets a chance to change it.
565 ps_global->mangled_footer = 1;
566 if(!*mb->user && !altuserforcache){
568 help = NO_HELP;
571 * Instead of offering user with a value that the user can edit,
572 * we offer [user] as a default so that the user can type CR to
573 * use it. Otherwise, the user has to type in whole name.
575 strncpy(defuser, user, sizeof(defuser)-1);
576 defuser[sizeof(defuser)-1] = '\0';
577 user[0] = '\0';
580 * Need space for "Retrying - "
581 * "+ HOST: "
582 * hostname
583 * " (INSECURE)"
584 * ENTER LOGIN NAME
585 * " [defuser] : "
586 * about 15 chars for input
589 snprintf(hostleadin, sizeof(hostleadin), "%s%s: ",
590 (!ps_global->ttyo && (mb->sslflag||mb->tlsflag)) ? "+ " : "", hostlabel);
591 hostleadin[sizeof(hostleadin)-1] = '\0';
593 strncpy(hostname, mb->host, sizeof(hostname)-1);
594 hostname[sizeof(hostname)-1] = '\0';
597 * Add port number to hostname if going through a tunnel or something
599 if(*non_def_port)
600 strncpy(port, non_def_port, sizeof(port));
601 else
602 port[0] = '\0';
604 insecure[0] = '\0';
605 /* if not encrypted and SSL/TLS is supported */
606 if(!(mb->sslflag||mb->tlsflag) &&
607 mail_parameters(NIL, GET_SSLDRIVER, NIL))
608 strncpy(insecure, insec, sizeof(insecure));
610 /* TRANSLATORS: user is being asked to type in their login name */
611 snprintf(logleadin, sizeof(logleadin), " %s", _("ENTER LOGIN NAME"));
613 snprintf(defubuf, sizeof(defubuf), "%s%s%s : ", (*defuser) ? " [" : "",
614 (*defuser) ? defuser : "",
615 (*defuser) ? "]" : "");
616 defubuf[sizeof(defubuf)-1] = '\0';
617 /* space reserved after prompt */
618 oespace = MAX(MIN(15, (ps_global->ttyo ? ps_global->ttyo->screen_cols : 80)/5), 6);
620 avail = ps_global->ttyo ? ps_global->ttyo->screen_cols : 80;
621 need = utf8_width(retry) + utf8_width(hostleadin) + strlen(hostname) + strlen(port) +
622 utf8_width(insecure) + utf8_width(logleadin) + strlen(defubuf) + oespace;
624 /* If we're retrying cut the hostname back to the first word. */
625 if(avail < need && trial > 0){
626 char *p;
628 len = strlen(hostname);
629 if((p = strchr(hostname, '.')) != NULL){
630 *p = '\0';
631 need -= (len - strlen(hostname));
635 if(avail < need){
636 need -= utf8_width(retry);
637 retry = "";
639 if(avail < need){
641 /* reduce length of logleadin */
642 len = utf8_width(logleadin);
643 /* TRANSLATORS: An abbreviated form of ENTER LOGIN NAME because
644 longer version doesn't fit on screen */
645 snprintf(logleadin, sizeof(logleadin), " %s", _("LOGIN"));
646 need -= (len - utf8_width(logleadin));
648 if(avail < need){
649 /* get two spaces from hostleadin */
650 len = utf8_width(hostleadin);
651 snprintf(hostleadin, sizeof(hostleadin), "%s%s:",
652 (!ps_global->ttyo && (mb->sslflag||mb->tlsflag)) ? "+" : "", hostlabel);
653 hostleadin[sizeof(hostleadin)-1] = '\0';
654 need -= (len - utf8_width(hostleadin));
656 /* get rid of port */
657 if(avail < need && strlen(port) > 0){
658 need -= strlen(port);
659 port[0] = '\0';
662 if(avail < need){
663 int reduce_to;
666 * Reduce space for hostname. Best we can do is 6 chars
667 * with hos...
669 reduce_to = (need - avail < strlen(hostname) - 6) ? (strlen(hostname)-(need-avail)) : 6;
670 len = strlen(hostname);
671 strncpy(hostname+reduce_to-3, "...", 4);
672 need -= (len - strlen(hostname));
674 if(avail < need && strlen(insecure) > 0){
675 if(need - avail <= 3 && !strcmp(insecure," (INSECURE)")){
676 need -= 3;
677 insecure[strlen(insecure)-4] = ')';
678 insecure[strlen(insecure)-3] = '\0';
680 else{
681 need -= utf8_width(insecure);
682 insecure[0] = '\0';
686 if(avail < need){
687 if(strlen(defubuf) > 3){
688 len = strlen(defubuf);
689 strncpy(defubuf, " [..] :", 9);
690 need -= (len - strlen(defubuf));
693 if(avail < need)
694 strncpy(defubuf, ":", 2);
697 * If it still doesn't fit, optionally_enter gets
698 * to worry about it.
706 snprintf(prompt, sizeof(prompt), "%s%s%s%s%s%s%s",
707 retry, hostleadin, hostname, port, insecure, logleadin, defubuf);
708 prompt[sizeof(prompt)-1] = '\0';
710 while(1) {
711 if(ps_global->ttyo)
712 mm_login_alt_cue(mb);
714 flags = OE_APPEND_CURRENT;
715 save_dont_use = ps_global->dont_use_init_cmds;
716 ps_global->dont_use_init_cmds = 1;
717 #ifdef _WINDOWS
718 if(!*user && *defuser){
719 strncpy(user, defuser, NETMAXUSER);
720 user[NETMAXUSER-1] = '\0';
723 rc = os_login_dialog(mb, user, NETMAXUSER, pwd, NETMAXPASSWD,
724 #ifdef LOCAL_PASSWD_CACHE
725 is_using_passfile() ? 1 :
726 #endif /* LOCAL_PASSWD_CACHE */
727 0, 0, &preserve_password);
728 ps_global->dont_use_init_cmds = save_dont_use;
729 if(rc == 0 && *user && *pwd)
730 goto nopwpmt;
731 #else /* !_WINDOWS */
732 rc = optionally_enter(user, q_line, 0, NETMAXUSER,
733 prompt, NULL, help, &flags);
734 #endif /* !_WINDOWS */
735 ps_global->dont_use_init_cmds = save_dont_use;
737 if(rc == 3) {
738 help = help == NO_HELP ? h_oe_login : NO_HELP;
739 continue;
742 /* default */
743 if(rc == 0 && !*user){
744 strncpy(user, defuser, NETMAXUSER);
745 user[NETMAXUSER-1] = '\0';
748 if(rc != 4)
749 break;
752 if(rc == 1 || !user[0]) {
753 ps_global->user_says_cancel = (rc == 1);
754 user[0] = '\0';
755 pwd[0] = '\0';
758 else{
759 strncpy(user, mb->user, NETMAXUSER);
760 user[NETMAXUSER-1] = '\0';
763 user[NETMAXUSER-1] = '\0';
764 pwd[NETMAXPASSWD-1] = '\0';
766 if(!(user[0] || altuserforcache)){
767 ps_global->no_newmail_check_from_optionally_enter = 0;
768 ps_global->in_init_seq = save_in_init;
769 return;
773 * Now that we have a user, we can check in the cache again to see
774 * if there is a password there. Try last working password associated
775 * with this host and user.
777 if(trial == 0L && !*mb->user && !altuserforcache){
778 if(imap_get_passwd(mm_login_list, pwd, user, hostlist,
779 (mb->sslflag||mb->tlsflag))){
780 ps_global->no_newmail_check_from_optionally_enter = 0;
781 ps_global->in_init_seq = save_in_init;
782 return;
785 #ifdef LOCAL_PASSWD_CACHE
786 if(get_passfile_passwd(ps_global->pinerc, pwd,
787 user, hostlist, (mb->sslflag||mb->tlsflag))){
788 imap_set_passwd(&mm_login_list, pwd, user,
789 hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
790 ps_global->no_newmail_check_from_optionally_enter = 0;
791 ps_global->in_init_seq = save_in_init;
792 return;
794 #endif /* LOCAL_PASSWD_CACHE */
796 else if(trial == 0 && altuserforcache){
797 if(imap_get_passwd(mm_login_list, pwd, altuserforcache, hostlist,
798 (mb->sslflag||mb->tlsflag))){
799 ps_global->no_newmail_check_from_optionally_enter = 0;
800 ps_global->in_init_seq = save_in_init;
801 return;
804 #ifdef LOCAL_PASSWD_CACHE
805 if(get_passfile_passwd(ps_global->pinerc, pwd,
806 altuserforcache, hostlist, (mb->sslflag||mb->tlsflag))){
807 imap_set_passwd(&mm_login_list, pwd, altuserforcache,
808 hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
809 ps_global->no_newmail_check_from_optionally_enter = 0;
810 ps_global->in_init_seq = save_in_init;
811 return;
813 #endif /* LOCAL_PASSWD_CACHE */
817 * Didn't find password in cache or this isn't the first try. Ask user.
819 help = NO_HELP;
822 * Need space for "Retrying - "
823 * "+ HOST: "
824 * hostname
825 * " (INSECURE) "
826 * " USER: "
827 * user
828 * " ENTER PASSWORD: "
829 * about 15 chars for input
832 snprintf(hostleadin, sizeof(hostleadin), "%s%s: ",
833 (!ps_global->ttyo && (mb->sslflag||mb->tlsflag)) ? "+ " : "", hostlabel);
835 strncpy(hostname, mb->host, sizeof(hostname)-1);
836 hostname[sizeof(hostname)-1] = '\0';
839 * Add port number to hostname if going through a tunnel or something
841 if(*non_def_port)
842 strncpy(port, non_def_port, sizeof(port));
843 else
844 port[0] = '\0';
846 insecure[0] = '\0';
848 /* if not encrypted and SSL/TLS is supported */
849 if(!(mb->sslflag||mb->tlsflag) &&
850 mail_parameters(NIL, GET_SSLDRIVER, NIL))
851 strncpy(insecure, insec, sizeof(insecure));
853 if(usethisprompt){
854 strncpy(logleadin, usethisprompt, sizeof(logleadin));
855 logleadin[sizeof(logleadin)-1] = '\0';
856 defubuf[0] = '\0';
857 user[0] = '\0';
859 else{
860 snprintf(logleadin, sizeof(logleadin), " %s: ", userlabel);
862 strncpy(defubuf, user, sizeof(defubuf)-1);
863 defubuf[sizeof(defubuf)-1] = '\0';
866 /* TRANSLATORS: user is being asked to type in their password */
867 snprintf(pwleadin, sizeof(pwleadin), " %s: ", _("ENTER PASSWORD"));
869 /* space reserved after prompt */
870 oespace = MAX(MIN(15, (ps_global->ttyo ? ps_global->ttyo->screen_cols : 80)/5), 6);
872 avail = ps_global->ttyo ? ps_global->ttyo->screen_cols : 80;
873 need = utf8_width(retry) + utf8_width(hostleadin) + strlen(hostname) + strlen(port) +
874 utf8_width(insecure) + utf8_width(logleadin) + strlen(defubuf) +
875 utf8_width(pwleadin) + oespace;
877 if(avail < need && trial > 0){
878 char *p;
880 len = strlen(hostname);
881 if((p = strchr(hostname, '.')) != NULL){
882 *p = '\0';
883 need -= (len - strlen(hostname));
887 if(avail < need){
888 need -= utf8_width(retry);
889 retry = "";
891 if(avail < need){
893 if(!usethisprompt){
894 snprintf(logleadin, sizeof(logleadin), " %s: ", userlabel);
895 need--;
898 rplstr(pwleadin, sizeof(pwleadin), 1, "");
899 need--;
901 if(avail < need){
902 /* get two spaces from hostleadin */
903 len = utf8_width(hostleadin);
904 snprintf(hostleadin, sizeof(hostleadin), "%s%s:",
905 (!ps_global->ttyo && (mb->sslflag||mb->tlsflag)) ? "+" : "", hostlabel);
906 hostleadin[sizeof(hostleadin)-1] = '\0';
907 need -= (len - utf8_width(hostleadin));
909 /* get rid of port */
910 if(avail < need && strlen(port) > 0){
911 need -= strlen(port);
912 port[0] = '\0';
915 if(avail < need){
916 len = utf8_width(pwleadin);
917 /* TRANSLATORS: An abbreviated form of ENTER PASSWORD */
918 snprintf(pwleadin, sizeof(pwleadin), " %s: ", _("PASSWORD"));
919 need -= (len - utf8_width(pwleadin));
923 if(avail < need){
924 int reduce_to;
927 * Reduce space for hostname. Best we can do is 6 chars
928 * with hos...
930 reduce_to = (need - avail < strlen(hostname) - 6) ? (strlen(hostname)-(need-avail)) : 6;
931 len = strlen(hostname);
932 strncpy(hostname+reduce_to-3, "...", 4);
933 need -= (len - strlen(hostname));
935 if(avail < need && strlen(insecure) > 0){
936 if(need - avail <= 3 && !strcmp(insecure," (INSECURE)")){
937 need -= 3;
938 insecure[strlen(insecure)-4] = ')';
939 insecure[strlen(insecure)-3] = '\0';
941 else{
942 need -= utf8_width(insecure);
943 insecure[0] = '\0';
947 if(avail < need){
948 len = utf8_width(logleadin);
949 strncpy(logleadin, " ", sizeof(logleadin));
950 logleadin[sizeof(logleadin)-1] = '\0';
951 need -= (len - utf8_width(logleadin));
953 if(avail < need){
954 reduce_to = (need - avail < strlen(defubuf) - 6) ? (strlen(defubuf)-(need-avail)) : 0;
955 if(reduce_to)
956 strncpy(defubuf+reduce_to-3, "...", 4);
957 else
958 defubuf[0] = '\0';
965 snprintf(prompt, sizeof(prompt), "%s%s%s%s%s%s%s%s",
966 retry, hostleadin, hostname, port, insecure, logleadin, defubuf, pwleadin);
967 prompt[sizeof(prompt)-1] = '\0';
969 *pwd = '\0';
970 while(1) {
971 if(ps_global->ttyo)
972 mm_login_alt_cue(mb);
974 save_dont_use = ps_global->dont_use_init_cmds;
975 ps_global->dont_use_init_cmds = 1;
976 flags = F_ON(F_QUELL_ASTERISKS, ps_global) ? OE_PASSWD_NOAST : OE_PASSWD;
977 #ifdef _WINDOWS
978 rc = os_login_dialog(mb, user, NETMAXUSER, pwd, NETMAXPASSWD, 0, 1,
979 &preserve_password);
980 #else /* !_WINDOWS */
981 rc = optionally_enter(pwd, q_line, 0, NETMAXPASSWD,
982 prompt, NULL, help, &flags);
983 #endif /* !_WINDOWS */
984 ps_global->dont_use_init_cmds = save_dont_use;
986 if(rc == 3) {
987 help = help == NO_HELP ? h_oe_passwd : NO_HELP;
989 else if(rc == 4){
991 else
992 break;
995 if(rc == 1 || !pwd[0]) {
996 ps_global->user_says_cancel = (rc == 1);
997 user[0] = pwd[0] = '\0';
998 ps_global->no_newmail_check_from_optionally_enter = 0;
999 ps_global->in_init_seq = save_in_init;
1000 return;
1003 #ifdef _WINDOWS
1004 nopwpmt:
1005 #endif
1006 /* remember the password for next time */
1007 if(F_OFF(F_DISABLE_PASSWORD_CACHING,ps_global))
1008 imap_set_passwd(&mm_login_list, pwd,
1009 altuserforcache ? altuserforcache : user, hostlist,
1010 (mb->sslflag||mb->tlsflag), 0, 0);
1011 #ifdef LOCAL_PASSWD_CACHE
1012 /* if requested, remember it on disk for next session */
1013 if(save_password && F_OFF(F_DISABLE_PASSWORD_FILE_SAVING,ps_global))
1014 set_passfile_passwd(ps_global->pinerc, pwd,
1015 altuserforcache ? altuserforcache : user, hostlist,
1016 (mb->sslflag||mb->tlsflag),
1017 (preserve_password == -1 ? 0
1018 : (preserve_password == 0 ? 2 :1)));
1019 #endif /* LOCAL_PASSWD_CACHE */
1021 ps_global->no_newmail_check_from_optionally_enter = 0;
1025 void
1026 mm_login_alt_cue(NETMBX *mb)
1028 if(ps_global->ttyo){
1029 COLOR_PAIR *lastc;
1031 lastc = pico_set_colors(ps_global->VAR_TITLE_FORE_COLOR,
1032 ps_global->VAR_TITLE_BACK_COLOR,
1033 PSC_REV | PSC_RET);
1035 mark_titlebar_dirty();
1036 PutLine0(0, ps_global->ttyo->screen_cols - 1,
1037 (mb->sslflag||mb->tlsflag) ? "+" : " ");
1039 if(lastc){
1040 (void)pico_set_colorp(lastc, PSC_NONE);
1041 free_color_pair(&lastc);
1044 fflush(stdout);
1049 /*----------------------------------------------------------------------
1050 Receive notification of an error writing to disk
1052 Args: stream -- The stream the error occured on
1053 errcode -- The system error code (errno)
1054 serious -- Flag indicating error is serious (mail may be lost)
1056 Result: If error is non serious, the stream is marked as having an error
1057 and deletes are disallowed until error clears
1058 If error is serious this goes modal, allowing the user to retry
1059 or get a shell escape to fix the condition. When the condition is
1060 serious it means that mail existing in the mailbox will be lost
1061 if Pine exits without writing, so we try to induce the user to
1062 fix the error, go get someone that can fix the error, or whatever
1063 and don't provide an easy way out.
1064 ----*/
1065 long
1066 mm_diskerror (MAILSTREAM *stream, long int errcode, long int serious)
1068 int i, j;
1069 char *p, *q, *s;
1070 static ESCKEY_S de_opts[] = {
1071 {'r', 'r', "R", "Retry"},
1072 {'f', 'f', "F", "FileBrowser"},
1073 {'s', 's', "S", "ShellPrompt"},
1074 {-1, 0, NULL, NULL}
1076 #define DE_COLS (ps_global->ttyo->screen_cols)
1077 #define DE_LINE (ps_global->ttyo->screen_rows - 3)
1079 #define DE_FOLDER(X) (((X) && (X)->mailbox) ? (X)->mailbox : "<no folder>")
1080 #define DE_PMT \
1081 "Disk error! Choose Retry, or the File browser or Shell to clean up: "
1082 #define DE_STR1 "SERIOUS DISK ERROR WRITING: \"%s\""
1083 #define DE_STR2 \
1084 "The reported error number is %s. The last reported mail error was:"
1085 static char *de_msg[] = {
1086 "Please try to correct the error preventing Alpine from saving your",
1087 "mail folder. For example if the disk is out of space try removing",
1088 "unneeded files. You might also contact your system administrator.",
1090 "Both Alpine's File Browser and an option to enter the system's",
1091 "command prompt are offered to aid in fixing the problem. When",
1092 "you believe the problem is resolved, choose the \"Retry\" option.",
1093 "Be aware that messages may be lost or this folder left in an",
1094 "inaccessible condition if you exit or kill Alpine before the problem",
1095 "is resolved.",
1096 NULL};
1097 static char *de_shell_msg[] = {
1098 "\n\nPlease attempt to correct the error preventing saving of the",
1099 "mail folder. If you do not know how to correct the problem, contact",
1100 "your system administrator. To return to Alpine, type \"exit\".",
1101 NULL};
1103 dprint((0,
1104 "\n***** DISK ERROR on stream %s. Error code %ld. Error is %sserious\n",
1105 DE_FOLDER(stream), errcode, serious ? "" : "not "));
1106 dprint((0, "***** message: \"%s\"\n\n",
1107 ps_global->last_error ? ps_global->last_error : "?"));
1109 if(!serious) {
1110 sp_set_io_error_on_stream(stream, 1);
1111 return (1) ;
1114 while(1){
1115 /* replace pine's body display with screen full of explanatory text */
1116 ClearLine(2);
1117 PutLine1(2, MAX((DE_COLS - sizeof(DE_STR1)
1118 - strlen(DE_FOLDER(stream)))/2, 0),
1119 DE_STR1, DE_FOLDER(stream));
1120 ClearLine(3);
1121 PutLine1(3, 4, DE_STR2, long2string(errcode));
1123 PutLine0(4, 0, " \"");
1124 removing_leading_white_space(ps_global->last_error);
1125 for(i = 4, p = ps_global->last_error; *p && i < DE_LINE; ){
1126 for(s = NULL, q = p; *q && q - p < DE_COLS - 16; q++)
1127 if(isspace((unsigned char)*q))
1128 s = q;
1130 if(*q && s)
1131 q = s;
1133 while(p < q)
1134 Writechar(*p++, 0);
1136 if(*(p = q)){
1137 ClearLine(++i);
1138 PutLine0(i, 0, " ");
1139 while(*p && isspace((unsigned char)*p))
1140 p++;
1142 else{
1143 Writechar('\"', 0);
1144 CleartoEOLN();
1145 break;
1149 ClearLine(++i);
1150 for(j = ++i; i < DE_LINE && de_msg[i-j]; i++){
1151 ClearLine(i);
1152 PutLine0(i, 0, " ");
1153 Write_to_screen(de_msg[i-j]);
1156 while(i < DE_LINE)
1157 ClearLine(i++);
1159 switch(radio_buttons(DE_PMT, -FOOTER_ROWS(ps_global), de_opts,
1160 'r', 0, NO_HELP, RB_FLUSH_IN | RB_NO_NEWMAIL)){
1161 case 'r' : /* Retry! */
1162 ps_global->mangled_screen = 1;
1163 return(0L);
1165 case 'f' : /* File Browser */
1167 char full_filename[MAXPATH+1], filename[MAXPATH+1];
1169 filename[0] = '\0';
1170 build_path(full_filename, ps_global->home_dir, filename,
1171 sizeof(full_filename));
1172 file_lister("DISK ERROR", full_filename, sizeof(full_filename),
1173 filename, sizeof(filename), FALSE, FB_SAVE);
1176 break;
1178 case 's' :
1179 EndInverse();
1180 end_keyboard(ps_global ? F_ON(F_USE_FK,ps_global) : 0);
1181 end_tty_driver(ps_global);
1182 for(i = 0; de_shell_msg[i]; i++)
1183 puts(de_shell_msg[i]);
1186 * Don't use our piping mechanism to spawn a subshell here
1187 * since it will the server (thus reentering c-client).
1188 * Bad thing to do.
1190 #ifdef _WINDOWS
1191 #else
1192 system("csh");
1193 #endif
1194 init_tty_driver(ps_global);
1195 init_keyboard(F_ON(F_USE_FK,ps_global));
1196 break;
1199 if(ps_global->redrawer)
1200 (*ps_global->redrawer)();
1205 long
1206 pine_tcptimeout_noscreen(long int elapsed, long int sincelast, char *host)
1208 long rv = 1L;
1209 char pmt[128];
1211 #ifdef _WINDOWS
1212 mswin_killsplash();
1213 #endif
1215 if(elapsed >= (long)ps_global->tcp_query_timeout){
1216 snprintf(pmt, sizeof(pmt),
1217 _("No reply in %s seconds from server %s. Break connection"),
1218 long2string(elapsed), host);
1219 pmt[sizeof(pmt)-1] = '\0';
1220 if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
1221 ps_global->user_says_cancel = 1;
1222 return(0L);
1226 ps_global->tcptimeout = 0;
1227 return(rv);
1232 * -------------------------------------------------------------
1233 * These are declared in pith/imap.h as mandatory to implement.
1234 * -------------------------------------------------------------
1239 * pine_tcptimeout - C-client callback to handle tcp-related timeouts.
1241 long
1242 pine_tcptimeout(long int elapsed, long int sincelast, char *host)
1244 long rv = 1L; /* keep trying by default */
1245 unsigned long ch;
1247 ps_global->tcptimeout = 1;
1248 #ifdef DEBUG
1249 dprint((1, "tcptimeout: waited %s seconds, server: %s\n",
1250 long2string(elapsed), host));
1251 if(debugfile)
1252 fflush(debugfile);
1253 #endif
1255 #ifdef _WINDOWS
1256 mswin_killsplash();
1257 #endif
1259 if(ps_global->noshow_timeout)
1260 return(rv);
1262 if(ps_global->can_interrupt
1263 && ps_global->close_connection_timeout > 0L
1264 && elapsed >= (long)ps_global->tcp_query_timeout
1265 && elapsed >= (long)ps_global->close_connection_timeout){
1266 ps_global->can_interrupt = 0; /* do not return here */
1267 ps_global->read_bail = 0;
1268 ps_global->user_says_cancel = 1;
1269 return 0;
1272 if(!ps_global->ttyo)
1273 return(pine_tcptimeout_noscreen(elapsed, sincelast, host));
1275 suspend_busy_cue();
1278 * Prompt after a minute (since by then things are probably really bad)
1279 * A prompt timeout means "keep trying"...
1281 if(elapsed >= (long)ps_global->tcp_query_timeout){
1282 int clear_inverse;
1284 ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
1285 if((clear_inverse = !InverseState()) != 0)
1286 StartInverse();
1288 Writechar(BELL, 0);
1290 PutLine2(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global), 0,
1291 _("No reply in %s seconds from server %s. Break connection?"),
1292 long2string(elapsed), host);
1293 CleartoEOLN();
1294 fflush(stdout);
1295 flush_input();
1296 ch = read_char(7);
1297 if(ps_global->read_bail || ch == 'y' || ch == 'Y'){
1298 ps_global->read_bail = 0;
1299 ps_global->user_says_cancel = 1;
1300 rv = 0L;
1303 if(clear_inverse)
1304 EndInverse();
1306 ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
1309 if(rv == 1L){ /* just warn 'em something's up */
1310 q_status_message2(SM_ORDER, 0, 0,
1311 _("No reply in %s seconds from server %s. Still Waiting..."),
1312 long2string(elapsed), host);
1313 flush_status_messages(0); /* make sure it's seen */
1316 mark_status_dirty(); /* make sure it get's cleared */
1318 resume_busy_cue((rv == 1) ? 3 : 0);
1319 ps_global->tcptimeout = 0;
1321 return(rv);
1324 QUOTALIST *pine_quotalist_copy (QUOTALIST *pquota)
1326 QUOTALIST *cquota = NULL;
1328 if(pquota){
1329 cquota = mail_newquotalist();
1330 if (pquota->name && *pquota->name)
1331 cquota->name = cpystr(pquota->name);
1332 cquota->usage = pquota->usage;
1333 cquota->limit = pquota->limit;
1334 if (pquota->next)
1335 cquota->next = pine_quotalist_copy(pquota->next);
1337 return cquota;
1341 /* c-client callback to handle quota */
1343 void
1344 pine_parse_quota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
1346 ps_global->quota = pine_quotalist_copy (pquota);
1350 * C-client callback to handle SSL/TLS certificate validation failures
1352 * Returning 0 means error becomes fatal
1353 * Non-zero means certificate problem is ignored and SSL session is
1354 * established
1356 * We remember the answer and won't re-ask for subsequent open attempts to
1357 * the same hostname.
1359 long
1360 pine_sslcertquery(char *reason, char *host, char *cert)
1362 char tmp[500];
1363 char *unknown = "<unknown>";
1364 long rv = 0L;
1365 STRLIST_S hostlist;
1366 int ok_novalidate = 0, warned = 0;
1368 dprint((1, "sslcertificatequery: host=%s reason=%s cert=%s\n",
1369 host ? host : "?", reason ? reason : "?",
1370 cert ? cert : "?"));
1372 hostlist.name = host ? host : "";
1373 hostlist.next = NULL;
1376 * See if we've been asked about this host before.
1378 if(imap_get_ssl(cert_failure_list, &hostlist, &ok_novalidate, &warned)){
1379 /* we were asked before, did we say Yes? */
1380 if(ok_novalidate)
1381 rv++;
1383 if(rv){
1384 dprint((5,
1385 "sslcertificatequery: approved automatically\n"));
1386 return(rv);
1389 dprint((1, "sslcertificatequery: we were asked before and said No, so ask again\n"));
1392 if(ps_global->ttyo){
1393 SCROLL_S sargs;
1394 STORE_S *in_store, *out_store;
1395 gf_io_t pc, gc;
1396 HANDLE_S *handles = NULL;
1397 int the_answer = 'n';
1399 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS)) ||
1400 !(out_store = so_get(CharStar, NULL, EDIT_ACCESS)))
1401 goto try_wantto;
1403 so_puts(in_store, "<HTML><P>");
1404 so_puts(in_store, _("There was a failure validating the SSL/TLS certificate for the server"));
1406 so_puts(in_store, "<P><CENTER>");
1407 so_puts(in_store, host ? host : unknown);
1408 so_puts(in_store, "</CENTER>");
1410 so_puts(in_store, "<P>");
1411 so_puts(in_store, _("The reason for the failure was"));
1413 /* squirrel away details */
1414 if(details_host)
1415 fs_give((void **)&details_host);
1416 if(details_reason)
1417 fs_give((void **)&details_reason);
1418 if(details_cert)
1419 fs_give((void **)&details_cert);
1421 details_host = cpystr(host ? host : unknown);
1422 details_reason = cpystr(reason ? reason : unknown);
1423 details_cert = cpystr(cert ? cert : unknown);
1425 so_puts(in_store, "<P><CENTER>");
1426 snprintf(tmp, sizeof(tmp), "%s (<A HREF=\"X-Alpine-Cert:\">details</A>)",
1427 reason ? reason : unknown);
1428 tmp[sizeof(tmp)-1] = '\0';
1430 so_puts(in_store, tmp);
1431 so_puts(in_store, "</CENTER>");
1433 so_puts(in_store, "<P>");
1434 so_puts(in_store, _("We have not verified the identity of your server. If you ignore this certificate validation problem and continue, you could end up connecting to an imposter server."));
1436 so_puts(in_store, "<P>");
1437 so_puts(in_store, _("If the certificate validation failure was expected and permanent you may avoid seeing this warning message in the future by adding the option"));
1439 so_puts(in_store, "<P><CENTER>");
1440 so_puts(in_store, "/novalidate-cert");
1441 so_puts(in_store, "</CENTER>");
1443 so_puts(in_store, "<P>");
1444 so_puts(in_store, _("to the name of the folder you attempted to access. In other words, wherever you see the characters"));
1446 so_puts(in_store, "<P><CENTER>");
1447 so_puts(in_store, host ? host : unknown);
1448 so_puts(in_store, "</CENTER>");
1450 so_puts(in_store, "<P>");
1451 so_puts(in_store, _("in your configuration, replace those characters with"));
1453 so_puts(in_store, "<P><CENTER>");
1454 so_puts(in_store, host ? host : unknown);
1455 so_puts(in_store, "/novalidate-cert");
1456 so_puts(in_store, "</CENTER>");
1458 so_puts(in_store, "<P>");
1459 so_puts(in_store, _("Answer \"Yes\" to ignore the warning and continue, \"No\" to cancel the open of this folder."));
1461 so_seek(in_store, 0L, 0);
1462 init_handles(&handles);
1463 gf_filter_init();
1464 gf_link_filter(gf_html2plain,
1465 gf_html2plain_opt(NULL,
1466 ps_global->ttyo->screen_cols, non_messageview_margin(),
1467 &handles, NULL, GFHP_LOCAL_HANDLES));
1468 gf_set_so_readc(&gc, in_store);
1469 gf_set_so_writec(&pc, out_store);
1470 gf_pipe(gc, pc);
1471 gf_clear_so_writec(out_store);
1472 gf_clear_so_readc(in_store);
1474 memset(&sargs, 0, sizeof(SCROLL_S));
1475 sargs.text.handles = handles;
1476 sargs.text.text = so_text(out_store);
1477 sargs.text.src = CharStar;
1478 sargs.text.desc = _("help text");
1479 sargs.bar.title = _("SSL/TLS CERTIFICATE VALIDATION FAILURE");
1480 sargs.proc.tool = answer_cert_failure;
1481 sargs.proc.data.p = (void *)&the_answer;
1482 sargs.keys.menu = &ans_certquery_keymenu;
1483 /* don't want to re-enter c-client */
1484 sargs.quell_newmail = 1;
1485 setbitmap(sargs.keys.bitmap);
1486 sargs.help.text = h_tls_validation_failure;
1487 sargs.help.title = _("HELP FOR CERT VALIDATION FAILURE");
1489 scrolltool(&sargs);
1491 if(the_answer == 'y')
1492 rv++;
1494 ps_global->mangled_screen = 1;
1495 ps_global->painted_body_on_startup = 0;
1496 ps_global->painted_footer_on_startup = 0;
1497 so_give(&in_store);
1498 so_give(&out_store);
1499 free_handles(&handles);
1500 if(details_host)
1501 fs_give((void **)&details_host);
1502 if(details_reason)
1503 fs_give((void **)&details_reason);
1504 if(details_cert)
1505 fs_give((void **)&details_cert);
1507 else{
1509 * If screen hasn't been initialized yet, use want_to.
1511 try_wantto:
1512 memset((void *)tmp, 0, sizeof(tmp));
1513 strncpy(tmp,
1514 reason ? reason : _("SSL/TLS certificate validation failure"),
1515 sizeof(tmp));
1516 tmp[sizeof(tmp)-1] = '\0';
1517 strncat(tmp, _(": Continue anyway "), sizeof(tmp)-strlen(tmp)-1);
1519 if(want_to(tmp, 'n', 'x', NO_HELP, WT_NORM) == 'y')
1520 rv++;
1523 if(rv == 0)
1524 q_status_message1(SM_ORDER, 1, 3, _("Open of %s cancelled"),
1525 host ? host : unknown);
1527 imap_set_passwd(&cert_failure_list, "", "", &hostlist, 0, rv ? 1 : 0, 0);
1529 dprint((5, "sslcertificatequery: %s\n",
1530 rv ? "approved" : "rejected"));
1532 return(rv);
1536 char *
1537 pine_newsrcquery(MAILSTREAM *stream, char *mulname, char *name)
1539 char buf[MAILTMPLEN];
1541 if((can_access(mulname, ACCESS_EXISTS) == 0)
1542 || !(can_access(name, ACCESS_EXISTS) == 0))
1543 return(mulname);
1545 snprintf(buf, sizeof(buf),
1546 _("Rename newsrc \"%s%s\" for use as new host-specific newsrc"),
1547 last_cmpnt(name),
1548 strlen(last_cmpnt(name)) > 15 ? "..." : "");
1549 buf[sizeof(buf)-1] = '\0';
1550 if(want_to(buf, 'n', 'n', NO_HELP, WT_NORM) == 'y')
1551 rename_file(name, mulname);
1552 return(mulname);
1557 url_local_certdetails(char *url)
1559 if(!struncmp(url, "x-alpine-cert:", 14)){
1560 STORE_S *store;
1561 SCROLL_S sargs;
1562 char *folded;
1564 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
1565 q_status_message(SM_ORDER | SM_DING, 7, 10,
1566 _("Error allocating space for details."));
1567 return(0);
1570 so_puts(store, _("Host given by user:\n\n "));
1571 so_puts(store, details_host);
1572 so_puts(store, _("\n\nReason for failure:\n\n "));
1573 so_puts(store, details_reason);
1574 so_puts(store, _("\n\nCertificate being verified:\n\n"));
1575 folded = fold(details_cert, ps_global->ttyo->screen_cols, ps_global->ttyo->screen_cols, " ", " ", FLD_NONE);
1576 so_puts(store, folded);
1577 fs_give((void **)&folded);
1578 so_puts(store, "\n");
1580 memset(&sargs, 0, sizeof(SCROLL_S));
1581 sargs.text.text = so_text(store);
1582 sargs.text.src = CharStar;
1583 sargs.text.desc = _("Details");
1584 sargs.bar.title = _("CERT VALIDATION DETAILS");
1585 sargs.help.text = NO_HELP;
1586 sargs.help.title = NULL;
1587 sargs.quell_newmail = 1;
1588 sargs.help.text = h_tls_failure_details;
1589 sargs.help.title = _("HELP FOR CERT VALIDATION DETAILS");
1591 scrolltool(&sargs);
1593 so_give(&store); /* free resources associated with store */
1594 ps_global->mangled_screen = 1;
1595 return(1);
1598 return(0);
1603 * C-client callback to handle SSL/TLS certificate validation failures
1605 void
1606 pine_sslfailure(char *host, char *reason, long unsigned int flags)
1608 SCROLL_S sargs;
1609 STORE_S *store;
1610 int the_answer = 'n', indent, len, cols;
1611 char buf[500], buf2[500];
1612 char *folded;
1613 char *hst = host ? host : "<unknown>";
1614 char *rsn = reason ? reason : "<unknown>";
1615 char *notls = "/notls";
1616 STRLIST_S hostlist;
1617 int ok_novalidate = 0, warned = 0;
1620 dprint((1, "sslfailure: host=%s reason=%s\n",
1621 hst ? hst : "?",
1622 rsn ? rsn : "?"));
1624 if(flags & NET_SILENT)
1625 return;
1627 hostlist.name = host ? host : "";
1628 hostlist.next = NULL;
1631 * See if we've been told about this host before.
1633 if(imap_get_ssl(cert_failure_list, &hostlist, &ok_novalidate, &warned)){
1634 /* we were told already */
1635 if(warned){
1636 snprintf(buf, sizeof(buf), _("SSL/TLS failure for %s: %s"), hst, rsn);
1637 buf[sizeof(buf)-1] = '\0';
1638 mm_log(buf, ERROR);
1639 return;
1643 cols = ps_global->ttyo ? ps_global->ttyo->screen_cols : 80;
1644 cols--;
1646 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS)))
1647 return;
1649 strncpy(buf, _("There was an SSL/TLS failure for the server"), sizeof(buf));
1650 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1651 so_puts(store, folded);
1652 fs_give((void **)&folded);
1653 so_puts(store, "\n");
1655 if((len=strlen(hst)) <= cols){
1656 if((indent=((cols-len)/2)) > 0)
1657 so_puts(store, repeat_char(indent, SPACE));
1659 so_puts(store, hst);
1660 so_puts(store, "\n");
1662 else{
1663 strncpy(buf, hst, sizeof(buf));
1664 buf[sizeof(buf)-1] = '\0';
1665 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1666 so_puts(store, folded);
1667 fs_give((void **)&folded);
1670 so_puts(store, "\n");
1672 strncpy(buf, _("The reason for the failure was"), sizeof(buf));
1673 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1674 so_puts(store, folded);
1675 fs_give((void **)&folded);
1676 so_puts(store, "\n");
1678 if((len=strlen(rsn)) <= cols){
1679 if((indent=((cols-len)/2)) > 0)
1680 so_puts(store, repeat_char(indent, SPACE));
1682 so_puts(store, rsn);
1683 so_puts(store, "\n");
1685 else{
1686 strncpy(buf, rsn, sizeof(buf));
1687 buf[sizeof(buf)-1] = '\0';
1688 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1689 so_puts(store, folded);
1690 fs_give((void **)&folded);
1693 so_puts(store, "\n");
1695 strncpy(buf, _("This is just an informational message. With the current setup, SSL/TLS will not work. If this error re-occurs every time you run Alpine, your current setup is not compatible with the configuration of your mail server. You may want to add the option"), sizeof(buf));
1696 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1697 so_puts(store, folded);
1698 fs_give((void **)&folded);
1699 so_puts(store, "\n");
1701 if((len=strlen(notls)) <= cols){
1702 if((indent=((cols-len)/2)) > 0)
1703 so_puts(store, repeat_char(indent, SPACE));
1705 so_puts(store, notls);
1706 so_puts(store, "\n");
1708 else{
1709 strncpy(buf, notls, sizeof(buf));
1710 buf[sizeof(buf)-1] = '\0';
1711 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1712 so_puts(store, folded);
1713 fs_give((void **)&folded);
1716 so_puts(store, "\n");
1718 strncpy(buf, _("to the name of the mail server you are attempting to access. In other words, wherever you see the characters"),
1719 sizeof(buf));
1720 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1721 so_puts(store, folded);
1722 fs_give((void **)&folded);
1723 so_puts(store, "\n");
1725 if((len=strlen(hst)) <= cols){
1726 if((indent=((cols-len)/2)) > 0)
1727 so_puts(store, repeat_char(indent, SPACE));
1729 so_puts(store, hst);
1730 so_puts(store, "\n");
1732 else{
1733 strncpy(buf, hst, sizeof(buf));
1734 buf[sizeof(buf)-1] = '\0';
1735 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1736 so_puts(store, folded);
1737 fs_give((void **)&folded);
1740 so_puts(store, "\n");
1742 strncpy(buf, _("in your configuration, replace those characters with"),
1743 sizeof(buf));
1744 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1745 so_puts(store, folded);
1746 fs_give((void **)&folded);
1747 so_puts(store, "\n");
1749 snprintf(buf2, sizeof(buf2), "%s%s", hst, notls);
1750 buf2[sizeof(buf2)-1] = '\0';
1751 if((len=strlen(buf2)) <= cols){
1752 if((indent=((cols-len)/2)) > 0)
1753 so_puts(store, repeat_char(indent, SPACE));
1755 so_puts(store, buf2);
1756 so_puts(store, "\n");
1758 else{
1759 strncpy(buf, buf2, sizeof(buf));
1760 buf[sizeof(buf)-1] = '\0';
1761 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1762 so_puts(store, folded);
1763 fs_give((void **)&folded);
1766 so_puts(store, "\n");
1768 if(ps_global->ttyo){
1769 strncpy(buf, _("Type RETURN to continue."), sizeof(buf));
1770 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1771 so_puts(store, folded);
1772 fs_give((void **)&folded);
1775 memset(&sargs, 0, sizeof(SCROLL_S));
1776 sargs.text.text = so_text(store);
1777 sargs.text.src = CharStar;
1778 sargs.text.desc = _("help text");
1779 sargs.bar.title = _("SSL/TLS FAILURE");
1780 sargs.proc.tool = answer_cert_failure;
1781 sargs.proc.data.p = (void *)&the_answer;
1782 sargs.keys.menu = &ans_certfail_keymenu;
1783 setbitmap(sargs.keys.bitmap);
1784 /* don't want to re-enter c-client */
1785 sargs.quell_newmail = 1;
1786 sargs.help.text = h_tls_failure;
1787 sargs.help.title = _("HELP FOR TLS/SSL FAILURE");
1789 if(ps_global->ttyo)
1790 scrolltool(&sargs);
1791 else{
1792 char **q, **qp;
1793 char *p;
1794 unsigned char c;
1795 int cnt = 0;
1798 * The screen isn't initialized yet, which should mean that this
1799 * is the result of a -p argument. Display_args_err knows how to deal
1800 * with the uninitialized screen, so we mess with the data to get it
1801 * in shape for display_args_err. This is pretty hacky.
1804 so_seek(store, 0L, 0); /* rewind */
1805 /* count the lines */
1806 while(so_readc(&c, store))
1807 if(c == '\n')
1808 cnt++;
1810 qp = q = (char **)fs_get((cnt+1) * sizeof(char *));
1811 memset(q, 0, (cnt+1) * sizeof(char *));
1813 so_seek(store, 0L, 0); /* rewind */
1814 p = buf;
1815 while(so_readc(&c, store)){
1816 if(c == '\n'){
1817 *p = '\0';
1818 *qp++ = cpystr(buf);
1819 p = buf;
1821 else
1822 *p++ = c;
1825 display_args_err(NULL, q, 0);
1826 free_list_array(&q);
1829 ps_global->mangled_screen = 1;
1830 ps_global->painted_body_on_startup = 0;
1831 ps_global->painted_footer_on_startup = 0;
1832 so_give(&store);
1834 imap_set_passwd(&cert_failure_list, "", "", &hostlist, 0, ok_novalidate, 1);
1839 answer_cert_failure(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
1841 int rv = 1;
1843 ps_global->next_screen = SCREEN_FUN_NULL;
1845 switch(cmd){
1846 case MC_YES :
1847 *(int *)(sparms->proc.data.p) = 'y';
1848 break;
1850 case MC_NO :
1851 *(int *)(sparms->proc.data.p) = 'n';
1852 break;
1854 default:
1855 alpine_panic("Unexpected command in answer_cert_failure");
1856 break;
1859 return(rv);
1863 /*----------------------------------------------------------------------
1864 This can be used to prevent the flickering of the check_cue char
1865 caused by numerous (5000+) fetches by c-client. Right now, the only
1866 practical use found is newsgroup subsciption.
1868 check_cue_display will check if this global is set, and won't clear
1869 the check_cue_char if set.
1870 ----*/
1871 void
1872 set_read_predicted(int i)
1874 ps_global->read_predicted = i==1;
1875 #ifdef _WINDOWS
1876 if(!i && F_ON(F_SHOW_DELAY_CUE, ps_global))
1877 check_cue_display(" ");
1878 #endif
1882 /*----------------------------------------------------------------------
1883 Exported method to retrieve logged in user name associated with stream
1885 Args: host -- host to find associated login name with.
1887 Result:
1888 ----*/
1889 void *
1890 pine_block_notify(int reason, void *data)
1892 switch(reason){
1893 case BLOCK_SENSITIVE: /* sensitive code, disallow alarms */
1894 break;
1896 case BLOCK_NONSENSITIVE: /* non-sensitive code, allow alarms */
1897 break;
1899 case BLOCK_TCPWRITE: /* blocked on TCP write */
1900 case BLOCK_FILELOCK: /* blocked on file locking */
1901 #ifdef _WINDOWS
1902 if(F_ON(F_SHOW_DELAY_CUE, ps_global))
1903 check_cue_display(">");
1905 mswin_setcursor(MSWIN_CURSOR_BUSY);
1906 #endif
1907 break;
1909 case BLOCK_DNSLOOKUP: /* blocked on DNS lookup */
1910 case BLOCK_TCPOPEN: /* blocked on TCP open */
1911 case BLOCK_TCPREAD: /* blocked on TCP read */
1912 case BLOCK_TCPCLOSE: /* blocked on TCP close */
1913 #ifdef _WINDOWS
1914 if(F_ON(F_SHOW_DELAY_CUE, ps_global))
1915 check_cue_display("<");
1917 mswin_setcursor(MSWIN_CURSOR_BUSY);
1918 #endif
1919 break;
1921 default :
1922 case BLOCK_NONE: /* not blocked */
1923 #ifdef _WINDOWS
1924 if(F_ON(F_SHOW_DELAY_CUE, ps_global))
1925 check_cue_display(" ");
1926 #endif
1927 break;
1931 return(NULL);
1935 void
1936 mm_expunged_current(long unsigned int rawno)
1938 /* expunged something we're viewing? */
1939 if(!ps_global->expunge_in_progress
1940 && (mn_is_cur(ps_global->msgmap, mn_raw2m(ps_global->msgmap, (long) rawno))
1941 && (ps_global->prev_screen == mail_view_screen
1942 || ps_global->prev_screen == attachment_screen))){
1943 ps_global->next_screen = mail_index_screen;
1944 q_status_message(SM_ORDER | SM_DING , 3, 3,
1945 "Message you were viewing is gone!");
1950 #ifdef PASSFILE
1953 * Specific functions to support caching username/passwd/host
1954 * triples on disk for use from one session to the next...
1957 #define FIRSTCH 0x20
1958 #define LASTCH 0x7e
1959 #define TABSZ (LASTCH - FIRSTCH + 1)
1961 static int xlate_key;
1965 * xlate_in() - xlate_in the given character
1967 char
1968 xlate_in(int c)
1970 register int eti;
1972 eti = xlate_key;
1973 if((c >= FIRSTCH) && (c <= LASTCH)){
1974 eti += (c - FIRSTCH);
1975 eti -= (eti >= 2*TABSZ) ? 2*TABSZ : (eti >= TABSZ) ? TABSZ : 0;
1976 return((xlate_key = eti) + FIRSTCH);
1978 else
1979 return(c);
1984 * xlate_out() - xlate_out the given character
1986 char
1987 xlate_out(char c)
1989 register int dti;
1990 register int xch;
1992 if((c >= FIRSTCH) && (c <= LASTCH)){
1993 xch = c - (dti = xlate_key);
1994 xch += (xch < FIRSTCH-TABSZ) ? 2*TABSZ : (xch < FIRSTCH) ? TABSZ : 0;
1995 dti = (xch - FIRSTCH) + dti;
1996 dti -= (dti >= 2*TABSZ) ? 2*TABSZ : (dti >= TABSZ) ? TABSZ : 0;
1997 xlate_key = dti;
1998 return(xch);
2000 else
2001 return(c);
2003 #endif /* PASSFILE */
2006 #ifdef LOCAL_PASSWD_CACHE
2009 int
2010 line_get(char *tmp, size_t len, char **textp)
2012 char *s;
2014 tmp[0] = '\0';
2015 if (*textp == NULL
2016 || (s = strchr(*textp, '\n')) == NULL
2017 || (s - *textp) > len - 1)
2018 return 0;
2020 *s = '\0';
2021 if(*(s-1) == '\r')
2022 *(s-1) = '\0';
2024 snprintf(tmp, len, "%s\n", *textp);
2025 tmp[len-1] = '\0';
2026 *textp = s+1;
2028 return 1;
2031 * For UNIX:
2032 * Passfile lines are
2034 * passwd TAB user TAB hostname TAB flags [ TAB orig_hostname ] \n
2036 * In pine4.40 and before there was no orig_hostname, and there still isn't
2037 * if it is the same as hostname.
2039 * else for WINDOWS:
2040 * Use Windows credentials. The TargetName of the credential is
2041 * UWash_Alpine_<hostname:port>\tuser\taltflag
2042 * and the blob consists of
2043 * passwd\torighost (if different from host)
2045 * We don't use anything fancy we just copy out all the credentials which
2046 * begin with TNAME and put them into our cache, so we don't lookup based
2047 * on the TargetName or anything like that. That was so we could re-use
2048 * the existing code and so that orighost data could be easily used.
2051 read_passfile(pinerc, l)
2052 char *pinerc;
2053 MMLOGIN_S **l;
2055 #ifdef WINCRED
2056 # if (WINCRED > 0)
2057 LPCTSTR lfilter = TEXT(TNAMESTAR);
2058 DWORD count, k;
2059 PCREDENTIAL *pcred;
2060 char *tmp, *blob, *target = NULL;
2061 char *host, *user, *sflags, *passwd, *orighost;
2062 char *ui[5];
2063 int i, j;
2065 if(using_passfile == 0)
2066 return(using_passfile);
2068 if(!g_CredInited){
2069 if(init_wincred_funcs() != 1){
2070 using_passfile = 0;
2071 return(using_passfile);
2075 dprint((9, "read_passfile\n"));
2077 using_passfile = 1;
2079 if(g_CredEnumerateW(lfilter, 0, &count, &pcred)){
2080 if(pcred){
2081 for(k = 0; k < count; k++){
2083 host = user = sflags = passwd = orighost = NULL;
2084 ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
2086 target = lptstr_to_utf8(pcred[k]->TargetName);
2087 tmp = srchstr(target, TNAME);
2089 if(tmp){
2090 tmp += strlen(TNAME);
2091 for(i = 0, j = 0; tmp[i] && j < 3; j++){
2092 for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
2093 ; /* find end of data */
2095 if(tmp[i])
2096 tmp[i++] = '\0'; /* tie off data */
2099 host = ui[0];
2100 user = ui[1];
2101 sflags = ui[2];
2104 blob = (char *) pcred[k]->CredentialBlob;
2105 if(blob){
2106 for(i = 0, j = 3; blob[i] && j < 5; j++){
2107 for(ui[j] = &blob[i]; blob[i] && blob[i] != '\t'; i++)
2108 ; /* find end of data */
2110 if(blob[i])
2111 blob[i++] = '\0'; /* tie off data */
2114 passwd = ui[3];
2115 orighost = ui[4];
2118 if(passwd && host && user){ /* valid field? */
2119 STRLIST_S hostlist[2];
2120 int flags = sflags ? atoi(sflags) : 0;
2122 hostlist[0].name = host;
2123 if(orighost){
2124 hostlist[0].next = &hostlist[1];
2125 hostlist[1].name = orighost;
2126 hostlist[1].next = NULL;
2128 else{
2129 hostlist[0].next = NULL;
2132 imap_set_passwd(l, passwd, user, hostlist, flags & 0x01, 0, 0);
2135 if(target)
2136 fs_give((void **) &target);
2139 g_CredFree((PVOID) pcred);
2143 return(1);
2145 # else /* old windows */
2146 using_passfile = 0;
2147 return(0);
2148 # endif
2150 #elif APPLEKEYCHAIN
2152 char target[MAILTMPLEN];
2153 char *tmp, *host, *user, *sflags, *passwd, *orighost;
2154 char *ui[5];
2155 int i, j, k, rc;
2156 SecKeychainAttributeList attrList;
2157 SecKeychainSearchRef searchRef = NULL;
2158 SecKeychainAttribute attrs[] = {
2159 { kSecAccountItemAttr, strlen(TNAME), TNAME }
2162 if(using_passfile == 0)
2163 return(using_passfile);
2165 dprint((9, "read_passfile\n"));
2168 /* search for only our items in the keychain */
2169 attrList.count = 1;
2170 attrList.attr = attrs;
2172 using_passfile = 1;
2173 if(!(rc=SecKeychainSearchCreateFromAttributes(NULL,
2174 kSecGenericPasswordItemClass,
2175 &attrList,
2176 &searchRef))){
2177 dprint((10, "read_passfile: SecKeychainSearchCreate succeeded\n"));
2178 if(searchRef){
2179 SecKeychainItemRef itemRef = NULL;
2180 SecKeychainAttributeInfo info;
2181 SecKeychainAttributeList *attrList = NULL;
2182 UInt32 blength = 0;
2183 char *blob = NULL;
2184 char *blobcopy = NULL; /* NULL terminated copy */
2186 UInt32 tags[] = {kSecAccountItemAttr,
2187 kSecServiceItemAttr};
2188 UInt32 formats[] = {0,0};
2190 dprint((10, "read_passfile: searchRef not NULL\n"));
2191 info.count = 2;
2192 info.tag = tags;
2193 info.format = formats;
2196 * Go through each item we found and put it
2197 * into our list.
2199 while(!(rc=SecKeychainSearchCopyNext(searchRef, &itemRef)) && itemRef){
2200 dprint((10, "read_passfile: SecKeychainSearchCopyNext got one\n"));
2201 rc = SecKeychainItemCopyAttributesAndData(itemRef,
2202 &info, NULL,
2203 &attrList,
2204 &blength,
2205 (void **) &blob);
2206 if(rc == 0 && attrList){
2207 dprint((10, "read_passfile: SecKeychainItemCopyAttributesAndData succeeded, count=%d\n", attrList->count));
2209 blobcopy = (char *) fs_get((blength + 1) * sizeof(char));
2210 strncpy(blobcopy, (char *) blob, blength);
2211 blobcopy[blength] = '\0';
2214 * I'm not real clear on how this works. It seems to be
2215 * necessary to combine the attributes from two passes
2216 * (attrList->count == 2) in order to get the full set
2217 * of attributes we inserted into the keychain in the
2218 * first place. So, we reset host...orighost outside of
2219 * the following for loop, not inside.
2221 host = user = sflags = passwd = orighost = NULL;
2222 ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
2224 for(k = 0; k < attrList->count; k++){
2226 if(attrList->attr[k].length){
2227 strncpy(target,
2228 (char *) attrList->attr[k].data,
2229 MIN(attrList->attr[k].length,sizeof(target)));
2230 target[MIN(attrList->attr[k].length,sizeof(target)-1)] = '\0';
2233 tmp = target;
2234 for(i = 0, j = 0; tmp[i] && j < 3; j++){
2235 for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
2236 ; /* find end of data */
2238 if(tmp[i])
2239 tmp[i++] = '\0'; /* tie off data */
2242 if(ui[0])
2243 host = ui[0];
2245 if(ui[1])
2246 user = ui[1];
2248 if(ui[2])
2249 sflags = ui[2];
2251 for(i = 0, j = 3; blobcopy[i] && j < 5; j++){
2252 for(ui[j] = &blobcopy[i]; blobcopy[i] && blobcopy[i] != '\t'; i++)
2253 ; /* find end of data */
2255 if(blobcopy[i])
2256 blobcopy[i++] = '\0'; /* tie off data */
2259 if(ui[3])
2260 passwd = ui[3];
2262 if(ui[4])
2263 orighost = ui[4];
2265 dprint((10, "read_passfile: host=%s user=%s sflags=%s passwd=%s orighost=%s\n", host?host:"", user?user:"", sflags?sflags:"", passwd?passwd:"", orighost?orighost:""));
2268 if(passwd && host && user){ /* valid field? */
2269 STRLIST_S hostlist[2];
2270 int flags = sflags ? atoi(sflags) : 0;
2272 hostlist[0].name = host;
2273 if(orighost){
2274 hostlist[0].next = &hostlist[1];
2275 hostlist[1].name = orighost;
2276 hostlist[1].next = NULL;
2278 else{
2279 hostlist[0].next = NULL;
2282 imap_set_passwd(l, passwd, user, hostlist, flags & 0x01, 0, 0);
2285 if(blobcopy)
2286 fs_give((void **) & blobcopy);
2288 SecKeychainItemFreeAttributesAndData(attrList, (void *) blob);
2290 else{
2291 using_passfile = 0;
2292 dprint((10, "read_passfile: SecKeychainItemCopyAttributesAndData failed, rc=%d\n", rc));
2295 CFRelease(itemRef);
2296 itemRef = NULL;
2299 CFRelease(searchRef);
2301 else{
2302 using_passfile = 0;
2303 dprint((10, "read_passfile: searchRef NULL\n"));
2306 else{
2307 using_passfile = 0;
2308 dprint((10, "read_passfile: SecKeychainSearchCreateFromAttributes failed, rc=%d\n", rc));
2311 return(using_passfile);
2313 #else /* PASSFILE */
2315 char tmp[MAILTMPLEN], *ui[5];
2316 int i, j, n, rv = 0;
2317 #ifdef SMIME
2318 char tmp2[MAILTMPLEN];
2319 char *text = NULL, *text2 = NULL;
2320 int encrypted = 0;
2321 #endif /* SMIME */
2322 FILE *fp;
2324 if(using_passfile == 0)
2325 return(using_passfile);
2327 dprint((9, "read_passfile\n"));
2329 /* if there's no password to read, bag it!! */
2330 if(!passfile_name(pinerc, tmp, sizeof(tmp)) || !(fp = our_fopen(tmp, "rb"))){
2331 using_passfile = 0;
2332 return(using_passfile);
2335 #ifdef SMIME
2336 /* the next call initializes the key/certificate pair used to
2337 * encrypt and decrypt a password file. The details of how this is
2338 * done is in the file pith/smime.c. During this setup we might call
2339 * smime_init(), but no matter what happens we must call smime_deinit()
2340 * there. The reason why this is so is because we can not assume that
2341 * the .pinerc file has been read by this time, so this code might not
2342 * know about the ps_global->smime structure or any of its components,
2343 * and it shouldn't because it only needs ps_global->pwdcert, so
2344 * do not init smime here, because the .pinerc might not have been
2345 * read and we do not really know where the keys and certificates really
2346 * are.
2347 * Remark: setupwdcert will produce a null ps_global->pwdcert only when
2348 * it is called for the first time and there are certificates at all,
2349 * or when it is called after the first time and the user refuses to
2350 * create a self-signed certificate. In this situation we will just
2351 * let the user live in an insecure world, but no more passwords will
2352 * be saved in the password file, and only those found there will be used.
2354 tmp2[0] = '\0';
2355 fgets(tmp2, sizeof(tmp2), fp);
2356 fclose(fp);
2357 if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){
2358 /* there is an already existing password file, that is not encrypted
2359 * and there is no key to encrypt it yet, go again through setup_pwdcert
2360 * and encrypt it now.
2362 if(tmp2[0]){ /* not empty, UNencrypted password file */
2363 if(ps_global->pwdcert == NULL)
2364 rv = setup_pwdcert(&ps_global->pwdcert);
2365 if((rv == 0 || rv == -5) && ps_global->pwdcert == NULL)
2366 ps_global->pwdcert = (void *) ALPINE_self_signed_certificate(NULL, 0, ps_global->pwdcertdir, MASTERNAME);
2367 if(ps_global->pwdcert == NULL){
2368 q_status_message(SM_ORDER, 3, 3,
2369 " Failed to create private key. Using UNencrypted Password file. ");
2370 save_password = 0;
2372 else{
2373 if(rv == 1){
2374 q_status_message(SM_ORDER, 3, 3,
2375 " Failed to unlock private key. Using UNencrypted Password file. ");
2376 save_password = 0; /* do not save more passwords */
2379 if(ps_global->pwdcert != NULL
2380 && encrypt_file((char *)tmp, NULL, (PERSONAL_CERT *)ps_global->pwdcert))
2381 encrypted++;
2384 else {
2385 if(ps_global->pwdcert == NULL)
2386 rv = setup_pwdcert(&ps_global->pwdcert);
2387 encrypted++;
2391 * if password file is encrypted we attemtp to decrypt. We ask the
2392 * user for the password to unlock the password file. If the user
2393 * enters the password and it unlocks the file, use it and keep saving
2394 * passwords in it. If the user enters the wrong passwords and does
2395 * not unlock it, we will not see that here, but in decrypt_file, so
2396 * the only other possibility is that the user cancels. In that case
2397 * we will see i == -1. In that case, we will let the user attempt
2398 * manual login to the server they want to login, but passwords will
2399 * not be saved so that the password file will not be saved
2400 * unencrypted and rewritten again.
2402 if(encrypted){
2403 text = text2 = decrypt_file((char *)tmp, &i, (PERSONAL_CERT *)ps_global->pwdcert);
2404 switch(i){
2405 case -2: using_passfile = 0;
2406 break;
2408 case 1 : save_password = 1;
2409 using_passfile = 1;
2410 break;
2412 case -1: save_password = 0;
2413 using_passfile = 1;
2414 break;
2416 default: break;
2419 else
2420 fp = our_fopen(tmp, "rb"); /* reopen to read data */
2421 #endif /* SMIME */
2423 if(using_passfile == 0){
2424 #ifdef SMIME
2425 if(text) fs_give((void **)&text);
2426 #endif /* SMIME */
2427 return using_passfile;
2430 #ifdef SMIME
2431 for(n = 0; encrypted ? line_get(tmp, sizeof(tmp), &text2)
2432 : (fgets(tmp, sizeof(tmp), fp) != NULL); n++){
2433 #else /* SMIME */
2434 for(n = 0; fgets(tmp, sizeof(tmp), fp); n++){
2435 #endif /* SMIME */
2436 /*** do any necessary DEcryption here ***/
2437 xlate_key = n;
2438 for(i = 0; tmp[i]; i++)
2439 tmp[i] = xlate_out(tmp[i]);
2441 if(i && tmp[i-1] == '\n')
2442 tmp[i-1] = '\0'; /* blast '\n' */
2444 dprint((10, "read_passfile: %s\n", tmp ? tmp : "?"));
2445 ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
2446 for(i = 0, j = 0; tmp[i] && j < 5; j++){
2447 for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
2448 ; /* find end of data */
2450 if(tmp[i])
2451 tmp[i++] = '\0'; /* tie off data */
2454 dprint((10, "read_passfile: calling imap_set_passwd\n"));
2455 if(ui[0] && ui[1] && ui[2]){ /* valid field? */
2456 STRLIST_S hostlist[2];
2457 int flags = ui[3] ? atoi(ui[3]) : 0;
2459 hostlist[0].name = ui[2];
2460 if(ui[4]){
2461 hostlist[0].next = &hostlist[1];
2462 hostlist[1].name = ui[4];
2463 hostlist[1].next = NULL;
2465 else{
2466 hostlist[0].next = NULL;
2469 imap_set_passwd(l, ui[0], ui[1], hostlist, flags & 0x01, 0, 0);
2473 #ifdef SMIME
2474 if (text) fs_give((void **)&text);
2475 #else /* SMIME */
2476 fclose(fp);
2477 #endif /* SMIME */
2478 return(1);
2479 #endif /* PASSFILE */
2484 void
2485 write_passfile(pinerc, l)
2486 char *pinerc;
2487 MMLOGIN_S *l;
2489 #ifdef WINCRED
2490 # if (WINCRED > 0)
2491 char target[MAILTMPLEN];
2492 char blob[MAILTMPLEN];
2493 CREDENTIAL cred;
2494 LPTSTR ltarget = 0;
2496 if(using_passfile == 0)
2497 return;
2499 dprint((9, "write_passfile\n"));
2501 for(; l; l = l->next){
2502 snprintf(target, sizeof(target), "%s%s\t%s\t%d",
2503 TNAME,
2504 (l->hosts && l->hosts->name) ? l->hosts->name : "",
2505 l->user ? l->user : "",
2506 l->altflag);
2507 ltarget = utf8_to_lptstr((LPSTR) target);
2509 if(ltarget){
2510 snprintf(blob, sizeof(blob), "%s%s%s",
2511 l->passwd ? l->passwd : "",
2512 (l->hosts && l->hosts->next && l->hosts->next->name)
2513 ? "\t" : "",
2514 (l->hosts && l->hosts->next && l->hosts->next->name)
2515 ? l->hosts->next->name : "");
2516 memset((void *) &cred, 0, sizeof(cred));
2517 cred.Flags = 0;
2518 cred.Type = CRED_TYPE_GENERIC;
2519 cred.TargetName = ltarget;
2520 cred.CredentialBlobSize = strlen(blob)+1;
2521 cred.CredentialBlob = (LPBYTE) &blob;
2522 cred.Persist = CRED_PERSIST_ENTERPRISE;
2523 g_CredWriteW(&cred, 0);
2525 fs_give((void **) &ltarget);
2528 #endif /* WINCRED > 0 */
2530 #elif APPLEKEYCHAIN
2532 int rc;
2533 char target[MAILTMPLEN];
2534 char blob[MAILTMPLEN];
2535 SecKeychainItemRef itemRef = NULL;
2537 if(using_passfile == 0)
2538 return;
2540 dprint((9, "write_passfile\n"));
2542 for(; l; l = l->next){
2543 snprintf(target, sizeof(target), "%s\t%s\t%d",
2544 (l->hosts && l->hosts->name) ? l->hosts->name : "",
2545 l->user ? l->user : "",
2546 l->altflag);
2548 snprintf(blob, sizeof(blob), "%s%s%s",
2549 l->passwd ? l->passwd : "",
2550 (l->hosts && l->hosts->next && l->hosts->next->name)
2551 ? "\t" : "",
2552 (l->hosts && l->hosts->next && l->hosts->next->name)
2553 ? l->hosts->next->name : "");
2555 dprint((10, "write_passfile: SecKeychainAddGenericPassword(NULL, %d, %s, %d, %s, %d, %s, NULL)\n", strlen(target), target, strlen(TNAME), TNAME, strlen(blob), blob));
2557 rc = SecKeychainAddGenericPassword(NULL,
2558 strlen(target), target,
2559 strlen(TNAME), TNAME,
2560 strlen(blob), blob,
2561 NULL);
2562 if(rc==0){
2563 dprint((10, "write_passfile: SecKeychainAddGenericPassword succeeded\n"));
2565 else{
2566 dprint((10, "write_passfile: SecKeychainAddGenericPassword returned rc=%d\n", rc));
2569 if(rc == errSecDuplicateItem){
2570 /* fix existing entry */
2571 dprint((10, "write_passfile: SecKeychainAddGenericPassword found existing entry\n"));
2572 itemRef = NULL;
2573 if(!(rc=SecKeychainFindGenericPassword(NULL,
2574 strlen(target), target,
2575 strlen(TNAME), TNAME,
2576 NULL, NULL,
2577 &itemRef)) && itemRef){
2579 rc = SecKeychainItemModifyAttributesAndData(itemRef, NULL, strlen(blob), blob);
2580 if(!rc){
2581 dprint((10, "write_passfile: SecKeychainItemModifyAttributesAndData returned rc=%d\n", rc));
2584 else{
2585 dprint((10, "write_passfile: SecKeychainFindGenericPassword returned rc=%d\n", rc));
2590 #else /* PASSFILE */
2592 char tmp[MAILTMPLEN];
2593 int i, n;
2594 FILE *fp;
2595 #ifdef SMIME
2596 char *text = NULL, tmp2[MAILTMPLEN];
2597 int len = 0;
2598 #endif
2600 if(using_passfile == 0)
2601 return;
2603 dprint((9, "write_passfile\n"));
2605 /* if there's no passfile to read, bag it!! */
2606 if(!passfile_name(pinerc, tmp, sizeof(tmp)) || !(fp = our_fopen(tmp, "wb"))){
2607 using_passfile = 0;
2608 return;
2611 #ifdef SMIME
2612 strncpy(tmp2, tmp, sizeof(tmp2));
2613 tmp2[sizeof(tmp2)-1] = '\0';
2614 #endif /* SMIME */
2616 for(n = 0; l; l = l->next, n++){
2617 /*** do any necessary ENcryption here ***/
2618 snprintf(tmp, sizeof(tmp), "%s\t%s\t%s\t%d%s%s\n", l->passwd, l->user,
2619 l->hosts->name, l->altflag,
2620 (l->hosts->next && l->hosts->next->name) ? "\t" : "",
2621 (l->hosts->next && l->hosts->next->name) ? l->hosts->next->name
2622 : "");
2623 dprint((10, "write_passfile: %s", tmp ? tmp : "?"));
2624 xlate_key = n;
2625 for(i = 0; tmp[i]; i++)
2626 tmp[i] = xlate_in(tmp[i]);
2628 #ifdef SMIME
2629 if(len == 0){
2630 len = strlen(tmp) + 1;
2631 text = fs_get(len*sizeof(char));
2632 *text = '\0';
2634 if(strlen(text) + strlen(tmp) > len){
2635 len = strlen(text) + strlen(tmp) + 1;
2636 fs_resize((void **)&text, len*sizeof(char));
2638 strncat(text, tmp, strlen(tmp));
2639 #else /* SMIME */
2640 fputs(tmp, fp);
2641 #endif /* SMIME */
2644 fclose(fp);
2645 #ifdef SMIME
2646 if(text != NULL){
2647 if(ps_global->pwdcert == NULL){
2648 q_status_message(SM_ORDER, 3, 3, "Attempting to encrypt password file");
2649 i = setup_pwdcert(&ps_global->pwdcert);
2650 if((i == 0 || i == -5) && ps_global->pwdcert == NULL)
2651 ps_global->pwdcert = (void *) ALPINE_self_signed_certificate(NULL, 0, ps_global->pwdcertdir, MASTERNAME);
2653 if(ps_global->pwdcert == NULL){ /* we tried but failed */
2654 if(i == -1)
2655 q_status_message1(SM_ORDER, 3, 3, "Error: no directory %s to save certificates", ps_global->pwdcertdir);
2656 else
2657 q_status_message(SM_ORDER, 3, 3, "Refusing to write non-encrypted password file");
2659 else if(encrypt_file((char *)tmp2, text, ps_global->pwdcert) == 0)
2660 q_status_message(SM_ORDER, 3, 3, "Failed to encrypt password file");
2661 fs_give((void **)&text); /* do not save this text */
2663 #endif /* SMIME */
2664 #endif /* PASSFILE */
2667 #endif /* LOCAL_PASSWD_CACHE */
2670 #if (WINCRED > 0)
2671 void
2672 erase_windows_credentials(void)
2674 LPCTSTR lfilter = TEXT(TNAMESTAR);
2675 DWORD count, k;
2676 PCREDENTIAL *pcred;
2678 if(!g_CredInited){
2679 if(init_wincred_funcs() != 1)
2680 return;
2683 if(g_CredEnumerateW(lfilter, 0, &count, &pcred)){
2684 if(pcred){
2685 for(k = 0; k < count; k++)
2686 g_CredDeleteW(pcred[k]->TargetName, CRED_TYPE_GENERIC, 0);
2688 g_CredFree((PVOID) pcred);
2693 void
2694 ask_erase_credentials(void)
2696 if(want_to(_("Erase previously preserved passwords"), 'y', 'x', NO_HELP, WT_NORM) == 'y'){
2697 erase_windows_credentials();
2698 q_status_message(SM_ORDER, 3, 3, "All preserved passwords have been erased");
2700 else
2701 q_status_message(SM_ORDER, 3, 3, "Previously preserved passwords will not be erased");
2704 #endif /* WINCRED */
2707 #ifdef LOCAL_PASSWD_CACHE
2710 * get_passfile_passwd - return the password contained in the special passord
2711 * cache. The file is assumed to be in the same directory
2712 * as the pinerc with the name defined above.
2715 get_passfile_passwd(pinerc, passwd, user, hostlist, altflag)
2716 char *pinerc, *passwd, *user;
2717 STRLIST_S *hostlist;
2718 int altflag;
2720 dprint((10, "get_passfile_passwd\n"));
2721 return((passfile_cache || read_passfile(pinerc, &passfile_cache))
2722 ? imap_get_passwd(passfile_cache, passwd,
2723 user, hostlist, altflag)
2724 : 0);
2727 void
2728 free_passfile_cache_work(MMLOGIN_S **pwdcache)
2730 if(pwdcache == NULL || *pwdcache == NULL)
2731 return;
2733 if((*pwdcache)->user) fs_give((void **)&(*pwdcache)->user);
2734 // if((*pwdcache)->passwd) fs_give((void **)&(*pwdcache)->passwd);
2735 if((*pwdcache)->hosts) free_strlist(&(*pwdcache)->hosts);
2736 free_passfile_cache_work(&(*pwdcache)->next);
2737 fs_give((void **)pwdcache);
2741 void
2742 free_passfile_cache(void)
2744 if(passfile_cache)
2745 free_passfile_cache_work(&passfile_cache);
2749 is_using_passfile(void)
2751 return(using_passfile == 1);
2755 * Just trying to guess the username the user might want to use on this
2756 * host, the user will confirm.
2758 char *
2759 get_passfile_user(pinerc, hostlist)
2760 char *pinerc;
2761 STRLIST_S *hostlist;
2763 return((passfile_cache || read_passfile(pinerc, &passfile_cache))
2764 ? imap_get_user(passfile_cache, hostlist)
2765 : NULL);
2770 preserve_prompt(char *pinerc)
2772 #ifdef WINCRED
2773 # if (WINCRED > 0)
2775 * This prompt was going to be able to be turned on and off via a registry
2776 * setting controlled from the config menu. We decided to always use the
2777 * dialog for login, and there the prompt is unobtrusive enough to always
2778 * be in there. As a result, windows should never reach this, but now
2779 * OS X somewhat uses the behavior just described.
2781 if(mswin_store_pass_prompt()
2782 && (want_to(_("Preserve password for next login"),
2783 'y', 'x', NO_HELP, WT_NORM)
2784 == 'y'))
2785 return(1);
2786 else
2787 return(0);
2788 # else
2789 return(0);
2790 # endif
2792 #elif APPLEKEYCHAIN
2794 int rc;
2795 if((rc = macos_store_pass_prompt()) != 0){
2796 if(want_to(_("Preserve password for next login"),
2797 'y', 'x', NO_HELP, WT_NORM)
2798 == 'y'){
2799 if(rc == -1){
2800 macos_set_store_pass_prompt(1);
2801 q_status_message(SM_ORDER, 4, 4,
2802 _("Stop \"Preserve passwords?\" prompts by deleting Alpine Keychain entry"));
2804 return(1);
2806 else if(rc == -1){
2807 macos_set_store_pass_prompt(0);
2808 q_status_message(SM_ORDER, 4, 4,
2809 _("Restart \"Preserve passwords?\" prompts by deleting Alpine Keychain entry"));
2811 return(0);
2813 return(0);
2814 #else /* PASSFILE */
2815 char tmp[MAILTMPLEN];
2816 struct stat sbuf;
2818 if(!passfile_name(pinerc, tmp, sizeof(tmp)) || our_stat(tmp, &sbuf) < 0)
2819 return 0;
2821 if(F_OFF(F_DISABLE_PASSWORD_FILE_SAVING,ps_global))
2822 return(want_to(_("Preserve password on DISK for next login"),
2823 'y', 'x', NO_HELP, WT_NORM)
2824 == 'y');
2825 return(0);
2826 #endif /* PASSFILE */
2829 #endif /* LOCAL_PASSWD_CACHE */
2832 #ifdef APPLEKEYCHAIN
2835 * Returns:
2836 * 1 if store pass prompt is set in the "registry" to on
2837 * 0 if set to off
2838 * -1 if not set to anything
2841 macos_store_pass_prompt(void)
2843 char *data = NULL;
2844 UInt32 len = 0;
2845 int rc = -1;
2846 int val;
2848 if(storepassprompt == -1){
2849 if(!(rc=SecKeychainFindGenericPassword(NULL, 0, NULL,
2850 strlen(TNAMEPROMPT),
2851 TNAMEPROMPT, &len,
2852 (void **) &data, NULL))){
2853 val = (len == 1 && data && data[0] == '1');
2857 if(storepassprompt == -1 && !rc){
2858 if(val)
2859 storepassprompt = 1;
2860 else
2861 storepassprompt = 0;
2864 return(storepassprompt);
2868 void
2869 macos_set_store_pass_prompt(int val)
2871 storepassprompt = val ? 1 : 0;
2873 SecKeychainAddGenericPassword(NULL, 0, NULL, strlen(TNAMEPROMPT),
2874 TNAMEPROMPT, 1, val ? "1" : "0", NULL);
2878 void
2879 macos_erase_keychain(void)
2881 SecKeychainAttributeList attrList;
2882 SecKeychainSearchRef searchRef = NULL;
2883 SecKeychainAttribute attrs1[] = {
2884 { kSecAccountItemAttr, strlen(TNAME), TNAME }
2886 SecKeychainAttribute attrs2[] = {
2887 { kSecAccountItemAttr, strlen(TNAMEPROMPT), TNAMEPROMPT }
2890 dprint((9, "macos_erase_keychain\n"));
2893 * Seems like we ought to be able to combine attrs1 and attrs2
2894 * into a single array, but I couldn't get it to work.
2897 /* search for only our items in the keychain */
2898 attrList.count = 1;
2899 attrList.attr = attrs1;
2901 if(!SecKeychainSearchCreateFromAttributes(NULL,
2902 kSecGenericPasswordItemClass,
2903 &attrList,
2904 &searchRef)){
2905 if(searchRef){
2906 SecKeychainItemRef itemRef = NULL;
2909 * Go through each item we found and put it
2910 * into our list.
2912 while(!SecKeychainSearchCopyNext(searchRef, &itemRef) && itemRef){
2913 dprint((10, "read_passfile: SecKeychainSearchCopyNext got one\n"));
2914 SecKeychainItemDelete(itemRef);
2915 CFRelease(itemRef);
2918 CFRelease(searchRef);
2922 attrList.count = 1;
2923 attrList.attr = attrs2;
2925 if(!SecKeychainSearchCreateFromAttributes(NULL,
2926 kSecGenericPasswordItemClass,
2927 &attrList,
2928 &searchRef)){
2929 if(searchRef){
2930 SecKeychainItemRef itemRef = NULL;
2933 * Go through each item we found and put it
2934 * into our list.
2936 while(!SecKeychainSearchCopyNext(searchRef, &itemRef) && itemRef){
2937 SecKeychainItemDelete(itemRef);
2938 CFRelease(itemRef);
2941 CFRelease(searchRef);
2946 #endif /* APPLEKEYCHAIN */
2948 #ifdef LOCAL_PASSWD_CACHE
2951 * set_passfile_passwd - set the password file entry associated with
2952 * cache. The file is assumed to be in the same directory
2953 * as the pinerc with the name defined above.
2954 * already_prompted: 0 not prompted
2955 * 1 prompted, answered yes
2956 * 2 prompted, answered no
2958 void
2959 set_passfile_passwd(pinerc, passwd, user, hostlist, altflag, already_prompted)
2960 char *pinerc, *passwd, *user;
2961 STRLIST_S *hostlist;
2962 int altflag, already_prompted;
2964 dprint((10, "set_passfile_passwd\n"));
2965 if(((already_prompted == 0 && preserve_prompt(pinerc))
2966 || already_prompted == 1)
2967 && !ps_global->nowrite_password_cache
2968 && (passfile_cache || read_passfile(pinerc, &passfile_cache))){
2969 imap_set_passwd(&passfile_cache, passwd, user, hostlist, altflag, 0, 0);
2970 write_passfile(pinerc, passfile_cache);
2976 * Passfile lines are
2978 * passwd TAB user TAB hostname TAB flags [ TAB orig_hostname ] \n
2980 * In pine4.40 and before there was no orig_hostname.
2981 * This routine attempts to repair that.
2983 void
2984 update_passfile_hostlist(pinerc, user, hostlist, altflag)
2985 char *pinerc;
2986 char *user;
2987 STRLIST_S *hostlist;
2988 int altflag;
2990 #ifdef WINCRED
2991 return;
2992 #else /* !WINCRED */
2993 MMLOGIN_S *l;
2995 for(l = passfile_cache; l; l = l->next)
2996 if(imap_same_host(l->hosts, hostlist)
2997 && *user
2998 && !strcmp(user, l->user)
2999 && l->altflag == altflag){
3000 break;
3003 if(l && l->hosts && hostlist && !l->hosts->next && hostlist->next
3004 && hostlist->next->name
3005 && !ps_global->nowrite_password_cache){
3006 l->hosts->next = new_strlist(hostlist->next->name);
3007 write_passfile(pinerc, passfile_cache);
3009 #endif /* !WINCRED */
3012 #endif /* LOCAL_PASSWD_CACHE */
3015 #if (WINCRED > 0)
3017 * Load and init the WinCred structure.
3018 * This gives us a way to skip the WinCred code
3019 * if the dll doesn't exist.
3022 init_wincred_funcs(void)
3024 if(!g_CredInited)
3026 HMODULE hmod;
3028 /* Assume the worst. */
3029 g_CredInited = -1;
3031 hmod = LoadLibrary(TEXT("advapi32.dll"));
3032 if(hmod)
3034 FARPROC fpCredWriteW;
3035 FARPROC fpCredEnumerateW;
3036 FARPROC fpCredDeleteW;
3037 FARPROC fpCredFree;
3039 fpCredWriteW = GetProcAddress(hmod, "CredWriteW");
3040 fpCredEnumerateW = GetProcAddress(hmod, "CredEnumerateW");
3041 fpCredDeleteW = GetProcAddress(hmod, "CredDeleteW");
3042 fpCredFree = GetProcAddress(hmod, "CredFree");
3044 if(fpCredWriteW && fpCredEnumerateW && fpCredDeleteW && fpCredFree)
3046 g_CredWriteW = (CREDWRITEW *)fpCredWriteW;
3047 g_CredEnumerateW = (CREDENUMERATEW *)fpCredEnumerateW;
3048 g_CredDeleteW = (CREDDELETEW *)fpCredDeleteW;
3049 g_CredFree = (CREDFREE *)fpCredFree;
3051 g_CredInited = 1;
3055 mswin_set_erasecreds_callback(ask_erase_credentials);
3058 return g_CredInited;
3061 #endif /* WINCRED */