* Alpine failed to read an encrypted password file if too many
[alpine.git] / alpine / imap.c
blobd5c97eee3e5203b84d609aa5ccffe153efa84512
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-2016 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);
110 static MMLOGIN_S *passfile_cache = NULL;
111 static int using_passfile = -1;
112 int save_password = 1;
113 #endif /* LOCAL_PASSWD_CACHE */
115 #ifdef PASSFILE
116 char xlate_in(int);
117 char xlate_out(char);
118 int line_get(char *, size_t, char **);
119 #endif /* PASSFILE */
121 #if (WINCRED > 0)
122 void ask_erase_credentials(void);
123 int init_wincred_funcs(void);
124 #endif /* WINCRED */
127 static char *details_cert, *details_host, *details_reason;
130 /*----------------------------------------------------------------------
131 recieve notification from IMAP
133 Args: stream -- Mail stream message is relavant to
134 string -- The message text
135 errflg -- Set if it is a serious error
137 Result: message displayed in status line
139 The facility is for general notices, such as connection to server;
140 server shutting down etc... It is used infrequently.
141 ----------------------------------------------------------------------*/
142 void
143 mm_notify(MAILSTREAM *stream, char *string, long int errflg)
145 time_t now;
146 struct tm *tm_now;
148 now = time((time_t *)0);
149 tm_now = localtime(&now);
151 /* be sure to log the message... */
152 #ifdef DEBUG
153 if(ps_global->debug_imap || ps_global->debugmem)
154 dprint((errflg == TCPDEBUG ? 7 : 2,
155 "IMAP %2.2d:%2.2d:%2.2d %d/%d mm_notify %s: %s: %s\n",
156 tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
157 tm_now->tm_mon+1, tm_now->tm_mday,
158 (!errflg) ? "babble" :
159 (errflg == ERROR) ? "error" :
160 (errflg == WARN) ? "warning" :
161 (errflg == PARSE) ? "parse" :
162 (errflg == TCPDEBUG) ? "tcp" :
163 (errflg == BYE) ? "bye" : "unknown",
164 (stream && stream->mailbox) ? stream->mailbox : "-no folder-",
165 string ? string : "?"));
166 #endif
168 snprintf(ps_global->last_error, sizeof(ps_global->last_error), "%s : %.*s",
169 (stream && stream->mailbox) ? stream->mailbox : "-no folder-",
170 (int) MIN(MAX_SCREEN_COLS, sizeof(ps_global->last_error)-70),
171 string);
172 ps_global->last_error[ps_global->ttyo ? ps_global->ttyo->screen_cols
173 : sizeof(ps_global->last_error)-1] = '\0';
176 * Then either set special bits in the pine struct or
177 * display the message if it's tagged as an "ALERT" or
178 * its errflg > NIL (i.e., WARN, or ERROR)
180 if(errflg == BYE)
182 * We'd like to sp_mark_stream_dead() here but we can't do that because
183 * that might call mail_close and we are already in a c-client callback.
184 * So just set the dead bit and clean it up later.
186 sp_set_dead_stream(stream, 1);
187 else if(!strncmp(string, "[TRYCREATE]", 11))
188 ps_global->try_to_create = 1;
189 else if(!strncmp(string, "[REFERRAL ", 10))
190 ; /* handled in the imap_referral() callback */
191 else if(!strncmp(string, "[ALERT]", 7))
192 q_status_message2(SM_MODAL, 3, 3,
193 _("Alert received while accessing \"%s\": %s"),
194 (stream && stream->mailbox)
195 ? stream->mailbox : "-no folder-",
196 rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000),
197 SIZEOF_20KBUF-10000, string));
198 else if(!strncmp(string, "[UNSEEN ", 8)){
199 char *p;
200 long n = 0;
202 for(p = string + 8; isdigit(*p); p++)
203 n = (n * 10) + (*p - '0');
205 sp_set_first_unseen(stream, n);
207 else if(!strncmp(string, "[READ-ONLY]", 11)
208 && !(stream && stream->mailbox && IS_NEWS(stream)))
209 q_status_message2(SM_ORDER | SM_DING, 3, 3, "%s : %s",
210 (stream && stream->mailbox)
211 ? stream->mailbox : "-no folder-",
212 string + 11);
213 else if((errflg && errflg != BYE && errflg != PARSE)
214 && !ps_global->noshow_error
215 && !(errflg == WARN
216 && (ps_global->noshow_warn || (stream && stream->unhealthy))))
217 q_status_message(SM_ORDER | ((errflg == ERROR) ? SM_DING : 0),
218 3, 6, ps_global->last_error);
222 /*----------------------------------------------------------------------
223 Queue imap log message for display in the message line
225 Args: string -- The message
226 errflg -- flag set to 1 if pertains to an error
228 Result: Message queued for display
230 The c-client/imap reports most of it's status and errors here
231 ---*/
232 void
233 mm_log(char *string, long int errflg)
235 char message[sizeof(ps_global->c_client_error)];
236 char *occurence;
237 int was_capitalized;
238 static char saw_kerberos_init_warning;
239 time_t now;
240 struct tm *tm_now;
242 now = time((time_t *)0);
243 tm_now = localtime(&now);
245 dprint((((errflg == TCPDEBUG) && ps_global->debug_tcp) ? 1 :
246 (errflg == TCPDEBUG) ? 10 : 2,
247 "IMAP %2.2d:%2.2d:%2.2d %d/%d mm_log %s: %s\n",
248 tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
249 tm_now->tm_mon+1, tm_now->tm_mday,
250 (!errflg) ? "babble" :
251 (errflg == ERROR) ? "error" :
252 (errflg == WARN) ? "warning" :
253 (errflg == PARSE) ? "parse" :
254 (errflg == TCPDEBUG) ? "tcp" :
255 (errflg == BYE) ? "bye" : "unknown",
256 string ? string : "?"));
258 if(errflg == ERROR && !strncmp(string, "[TRYCREATE]", 11)){
259 ps_global->try_to_create = 1;
260 return;
262 else if(ps_global->try_to_create
263 || !strncmp(string, "[CLOSED]", 8)
264 || (sp_dead_stream(ps_global->mail_stream) && strstr(string, "No-op")))
266 * Don't display if creating new folder OR
267 * warning about a dead stream ...
269 return;
271 strncpy(message, string, sizeof(message));
272 message[sizeof(message) - 1] = '\0';
274 if(errflg == WARN && srchstr(message, "try running kinit") != NULL){
275 if(saw_kerberos_init_warning)
276 return;
278 saw_kerberos_init_warning = 1;
281 /*---- replace all "mailbox" with "folder" ------*/
282 occurence = srchstr(message, "mailbox");
283 while(occurence) {
284 if(!*(occurence+7)
285 || isspace((unsigned char) *(occurence+7))
286 || *(occurence+7) == ':'){
287 was_capitalized = isupper((unsigned char) *occurence);
288 rplstr(occurence, sizeof(message)-(occurence-message), 7, (errflg == PARSE ? "address" : "folder"));
289 if(was_capitalized)
290 *occurence = (errflg == PARSE ? 'A' : 'F');
292 else
293 occurence += 7;
295 occurence = srchstr(occurence, "mailbox");
298 /*---- replace all "GSSAPI" with "Kerberos" ------*/
299 occurence = srchstr(message, "GSSAPI");
300 while(occurence) {
301 if(!*(occurence+6)
302 || isspace((unsigned char) *(occurence+6))
303 || *(occurence+6) == ':')
304 rplstr(occurence, sizeof(message)-(occurence-message), 6, "Kerberos");
305 else
306 occurence += 6;
308 occurence = srchstr(occurence, "GSSAPI");
311 if(errflg == ERROR)
312 ps_global->mm_log_error = 1;
314 if(errflg == PARSE || (errflg == ERROR && ps_global->noshow_error))
315 strncpy(ps_global->c_client_error, message,
316 sizeof(ps_global->c_client_error));
318 if(ps_global->noshow_error
319 || (ps_global->noshow_warn && errflg == WARN)
320 || !(errflg == ERROR || errflg == WARN))
321 return; /* Only care about errors; don't print when asked not to */
323 /*---- Display the message ------*/
324 q_status_message((errflg == ERROR) ? (SM_ORDER | SM_DING) : SM_ORDER,
325 3, 5, message);
326 strncpy(ps_global->last_error, message, sizeof(ps_global->last_error));
327 ps_global->last_error[sizeof(ps_global->last_error) - 1] = '\0';
331 void
332 mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
333 char *usethisprompt, char *altuserforcache)
335 char prompt[1000], *last;
336 char port[20], non_def_port[20], insecure[20];
337 char defuser[NETMAXUSER];
338 char hostleadin[80], hostname[200], defubuf[200];
339 char logleadin[80], pwleadin[50];
340 char hostlist0[MAILTMPLEN], hostlist1[MAILTMPLEN];
341 /* TRANSLATORS: when logging in, this text is added to the prompt to show
342 that the password will be sent unencrypted over the network. This is
343 just a warning message that gets added parenthetically when the user
344 is asked for a password. */
345 char *insec = _(" (INSECURE)");
346 /* TRANSLATORS: Retrying is shown when the user is being asked for a password
347 after having already failed at least once. */
348 char *retry = _("Retrying - ");
349 /* TRANSLATORS: A label for the hostname that the user is logging in on */
350 char *hostlabel = _("HOST");
351 /* TRANSLATORS: user is logging in as a particular user (a particular
352 login name), this is just labelling that user name. */
353 char *userlabel = _("USER");
354 STRLIST_S hostlist[2];
355 HelpType help ;
356 int len, rc, q_line, flags;
357 int oespace, avail, need, save_dont_use;
358 int save_in_init;
359 struct servent *sv;
360 #if defined(_WINDOWS) || defined(LOCAL_PASSWD_CACHE)
361 int preserve_password = -1;
362 #endif
364 dprint((9, "mm_login_work trial=%ld user=%s service=%s%s%s%s%s\n",
365 trial, mb->user ? mb->user : "(null)",
366 mb->service ? mb->service : "(null)",
367 mb->port ? " port=" : "",
368 mb->port ? comatose(mb->port) : "",
369 altuserforcache ? " altuserforcache =" : "",
370 altuserforcache ? altuserforcache : ""));
371 q_line = -(ps_global->ttyo ? ps_global->ttyo->footer_rows : 3);
373 save_in_init = ps_global->in_init_seq;
374 ps_global->in_init_seq = 0;
375 ps_global->no_newmail_check_from_optionally_enter = 1;
377 /* make sure errors are seen */
378 if(ps_global->ttyo)
379 flush_status_messages(0);
382 * Add port number to hostname if going through a tunnel or something
384 non_def_port[0] = '\0';
385 if(mb->port && mb->service &&
386 (sv = getservbyname(mb->service, "tcp")) &&
387 (mb->port != ntohs(sv->s_port))){
388 snprintf(non_def_port, sizeof(non_def_port), ":%lu", mb->port);
389 non_def_port[sizeof(non_def_port)-1] = '\0';
390 dprint((9, "mm_login: using non-default port=%s\n",
391 non_def_port ? non_def_port : "?"));
395 * set up host list for sybil servers...
397 if(*non_def_port){
398 strncpy(hostlist0, mb->host, sizeof(hostlist0)-1);
399 hostlist0[sizeof(hostlist0)-1] = '\0';
400 strncat(hostlist0, non_def_port, sizeof(hostlist0)-strlen(hostlist0)-1);
401 hostlist0[sizeof(hostlist0)-1] = '\0';
402 hostlist[0].name = hostlist0;
403 if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)){
404 strncpy(hostlist1, mb->orighost, sizeof(hostlist1)-1);
405 hostlist1[sizeof(hostlist1)-1] = '\0';
406 strncat(hostlist1, non_def_port, sizeof(hostlist1)-strlen(hostlist1)-1);
407 hostlist1[sizeof(hostlist1)-1] = '\0';
408 hostlist[0].next = &hostlist[1];
409 hostlist[1].name = hostlist1;
410 hostlist[1].next = NULL;
412 else
413 hostlist[0].next = NULL;
415 else{
416 hostlist[0].name = mb->host;
417 if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)){
418 hostlist[0].next = &hostlist[1];
419 hostlist[1].name = mb->orighost;
420 hostlist[1].next = NULL;
422 else
423 hostlist[0].next = NULL;
426 if(hostlist[0].name){
427 dprint((9, "mm_login: host=%s\n",
428 hostlist[0].name ? hostlist[0].name : "?"));
429 if(hostlist[0].next && hostlist[1].name){
430 dprint((9, "mm_login: orighost=%s\n", hostlist[1].name));
435 * Initialize user name with either
436 * 1) /user= value in the stream being logged into,
437 * or 2) the user name we're running under.
439 * Note that VAR_USER_ID is not yet initialized if this login is
440 * the one to access the remote config file. In that case, the user
441 * can supply the username in the config file name with /user=.
443 if(trial == 0L && !altuserforcache){
444 strncpy(user, (*mb->user) ? mb->user :
445 ps_global->VAR_USER_ID ? ps_global->VAR_USER_ID : "",
446 NETMAXUSER);
447 user[NETMAXUSER-1] = '\0';
449 /* try last working password associated with this host. */
450 if(imap_get_passwd(mm_login_list, pwd, user, hostlist,
451 (mb->sslflag||mb->tlsflag))){
452 dprint((9, "mm_login: found a password to try\n"));
453 ps_global->no_newmail_check_from_optionally_enter = 0;
454 ps_global->in_init_seq = save_in_init;
455 return;
458 #ifdef LOCAL_PASSWD_CACHE
459 /* check to see if there's a password left over from last session */
460 if(get_passfile_passwd(ps_global->pinerc, pwd,
461 user, hostlist, (mb->sslflag||mb->tlsflag))){
462 imap_set_passwd(&mm_login_list, pwd, user,
463 hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
464 update_passfile_hostlist(ps_global->pinerc, user, hostlist,
465 (mb->sslflag||mb->tlsflag));
466 dprint((9, "mm_login: found a password in passfile to try\n"));
467 ps_global->no_newmail_check_from_optionally_enter = 0;
468 ps_global->in_init_seq = save_in_init;
469 return;
471 #endif /* LOCAL_PASSWD_CACHE */
474 * If no explicit user name supplied and we've not logged in
475 * with our local user name, see if we've visited this
476 * host before as someone else.
478 if(!*mb->user &&
479 ((last = imap_get_user(mm_login_list, hostlist))
480 #ifdef LOCAL_PASSWD_CACHE
482 (last = get_passfile_user(ps_global->pinerc, hostlist))
483 #endif /* LOCAL_PASSWD_CACHE */
485 strncpy(user, last, NETMAXUSER);
486 user[NETMAXUSER-1] = '\0';
487 dprint((9, "mm_login: found user=%s\n",
488 user ? user : "?"));
490 /* try last working password associated with this host/user. */
491 if(imap_get_passwd(mm_login_list, pwd, user, hostlist,
492 (mb->sslflag||mb->tlsflag))){
493 dprint((9,
494 "mm_login: found a password for user=%s to try\n",
495 user ? user : "?"));
496 ps_global->no_newmail_check_from_optionally_enter = 0;
497 ps_global->in_init_seq = save_in_init;
498 return;
501 #ifdef LOCAL_PASSWD_CACHE
502 /* check to see if there's a password left over from last session */
503 if(get_passfile_passwd(ps_global->pinerc, pwd,
504 user, hostlist, (mb->sslflag||mb->tlsflag))){
505 imap_set_passwd(&mm_login_list, pwd, user,
506 hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
507 update_passfile_hostlist(ps_global->pinerc, user, hostlist,
508 (mb->sslflag||mb->tlsflag));
509 dprint((9,
510 "mm_login: found a password for user=%s in passfile to try\n",
511 user ? user : "?"));
512 ps_global->no_newmail_check_from_optionally_enter = 0;
513 ps_global->in_init_seq = save_in_init;
514 return;
516 #endif /* LOCAL_PASSWD_CACHE */
519 #if !defined(DOS) && !defined(OS2)
520 if(!*mb->user && !*user &&
521 (last = (ps_global->ui.login && ps_global->ui.login[0])
522 ? ps_global->ui.login : NULL)
524 strncpy(user, last, NETMAXUSER);
525 user[NETMAXUSER-1] = '\0';
526 dprint((9, "mm_login: found user=%s\n",
527 user ? user : "?"));
529 /* try last working password associated with this host. */
530 if(imap_get_passwd(mm_login_list, pwd, user, hostlist,
531 (mb->sslflag||mb->tlsflag))){
532 dprint((9, "mm_login:ui: found a password to try\n"));
533 ps_global->no_newmail_check_from_optionally_enter = 0;
534 ps_global->in_init_seq = save_in_init;
535 return;
538 #ifdef LOCAL_PASSWD_CACHE
539 /* check to see if there's a password left over from last session */
540 if(get_passfile_passwd(ps_global->pinerc, pwd,
541 user, hostlist, (mb->sslflag||mb->tlsflag))){
542 imap_set_passwd(&mm_login_list, pwd, user,
543 hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
544 update_passfile_hostlist(ps_global->pinerc, user, hostlist,
545 (mb->sslflag||mb->tlsflag));
546 dprint((9, "mm_login:ui: found a password in passfile to try\n"));
547 ps_global->no_newmail_check_from_optionally_enter = 0;
548 ps_global->in_init_seq = save_in_init;
549 return;
551 #endif /* LOCAL_PASSWD_CACHE */
553 #endif
556 user[NETMAXUSER-1] = '\0';
558 if(trial == 0)
559 retry = "";
562 * Even if we have a user now, user gets a chance to change it.
564 ps_global->mangled_footer = 1;
565 if(!*mb->user && !altuserforcache){
567 help = NO_HELP;
570 * Instead of offering user with a value that the user can edit,
571 * we offer [user] as a default so that the user can type CR to
572 * use it. Otherwise, the user has to type in whole name.
574 strncpy(defuser, user, sizeof(defuser)-1);
575 defuser[sizeof(defuser)-1] = '\0';
576 user[0] = '\0';
579 * Need space for "Retrying - "
580 * "+ HOST: "
581 * hostname
582 * " (INSECURE)"
583 * ENTER LOGIN NAME
584 * " [defuser] : "
585 * about 15 chars for input
588 snprintf(hostleadin, sizeof(hostleadin), "%s%s: ",
589 (!ps_global->ttyo && (mb->sslflag||mb->tlsflag)) ? "+ " : "", hostlabel);
590 hostleadin[sizeof(hostleadin)-1] = '\0';
592 strncpy(hostname, mb->host, sizeof(hostname)-1);
593 hostname[sizeof(hostname)-1] = '\0';
596 * Add port number to hostname if going through a tunnel or something
598 if(*non_def_port)
599 strncpy(port, non_def_port, sizeof(port));
600 else
601 port[0] = '\0';
603 insecure[0] = '\0';
604 /* if not encrypted and SSL/TLS is supported */
605 if(!(mb->sslflag||mb->tlsflag) &&
606 mail_parameters(NIL, GET_SSLDRIVER, NIL))
607 strncpy(insecure, insec, sizeof(insecure));
609 /* TRANSLATORS: user is being asked to type in their login name */
610 snprintf(logleadin, sizeof(logleadin), " %s", _("ENTER LOGIN NAME"));
612 snprintf(defubuf, sizeof(defubuf), "%s%s%s : ", (*defuser) ? " [" : "",
613 (*defuser) ? defuser : "",
614 (*defuser) ? "]" : "");
615 defubuf[sizeof(defubuf)-1] = '\0';
616 /* space reserved after prompt */
617 oespace = MAX(MIN(15, (ps_global->ttyo ? ps_global->ttyo->screen_cols : 80)/5), 6);
619 avail = ps_global->ttyo ? ps_global->ttyo->screen_cols : 80;
620 need = utf8_width(retry) + utf8_width(hostleadin) + strlen(hostname) + strlen(port) +
621 utf8_width(insecure) + utf8_width(logleadin) + strlen(defubuf) + oespace;
623 /* If we're retrying cut the hostname back to the first word. */
624 if(avail < need && trial > 0){
625 char *p;
627 len = strlen(hostname);
628 if((p = strchr(hostname, '.')) != NULL){
629 *p = '\0';
630 need -= (len - strlen(hostname));
634 if(avail < need){
635 need -= utf8_width(retry);
636 retry = "";
638 if(avail < need){
640 /* reduce length of logleadin */
641 len = utf8_width(logleadin);
642 /* TRANSLATORS: An abbreviated form of ENTER LOGIN NAME because
643 longer version doesn't fit on screen */
644 snprintf(logleadin, sizeof(logleadin), " %s", _("LOGIN"));
645 need -= (len - utf8_width(logleadin));
647 if(avail < need){
648 /* get two spaces from hostleadin */
649 len = utf8_width(hostleadin);
650 snprintf(hostleadin, sizeof(hostleadin), "%s%s:",
651 (!ps_global->ttyo && (mb->sslflag||mb->tlsflag)) ? "+" : "", hostlabel);
652 hostleadin[sizeof(hostleadin)-1] = '\0';
653 need -= (len - utf8_width(hostleadin));
655 /* get rid of port */
656 if(avail < need && strlen(port) > 0){
657 need -= strlen(port);
658 port[0] = '\0';
661 if(avail < need){
662 int reduce_to;
665 * Reduce space for hostname. Best we can do is 6 chars
666 * with hos...
668 reduce_to = (need - avail < strlen(hostname) - 6) ? (strlen(hostname)-(need-avail)) : 6;
669 len = strlen(hostname);
670 strncpy(hostname+reduce_to-3, "...", 4);
671 need -= (len - strlen(hostname));
673 if(avail < need && strlen(insecure) > 0){
674 if(need - avail <= 3 && !strcmp(insecure," (INSECURE)")){
675 need -= 3;
676 insecure[strlen(insecure)-4] = ')';
677 insecure[strlen(insecure)-3] = '\0';
679 else{
680 need -= utf8_width(insecure);
681 insecure[0] = '\0';
685 if(avail < need){
686 if(strlen(defubuf) > 3){
687 len = strlen(defubuf);
688 strncpy(defubuf, " [..] :", 9);
689 need -= (len - strlen(defubuf));
692 if(avail < need)
693 strncpy(defubuf, ":", 2);
696 * If it still doesn't fit, optionally_enter gets
697 * to worry about it.
705 snprintf(prompt, sizeof(prompt), "%s%s%s%s%s%s%s",
706 retry, hostleadin, hostname, port, insecure, logleadin, defubuf);
707 prompt[sizeof(prompt)-1] = '\0';
709 while(1) {
710 if(ps_global->ttyo)
711 mm_login_alt_cue(mb);
713 flags = OE_APPEND_CURRENT;
714 save_dont_use = ps_global->dont_use_init_cmds;
715 ps_global->dont_use_init_cmds = 1;
716 #ifdef _WINDOWS
717 if(!*user && *defuser){
718 strncpy(user, defuser, NETMAXUSER);
719 user[NETMAXUSER-1] = '\0';
722 rc = os_login_dialog(mb, user, NETMAXUSER, pwd, NETMAXPASSWD,
723 #ifdef LOCAL_PASSWD_CACHE
724 is_using_passfile() ? 1 :
725 #endif /* LOCAL_PASSWD_CACHE */
726 0, 0, &preserve_password);
727 ps_global->dont_use_init_cmds = save_dont_use;
728 if(rc == 0 && *user && *pwd)
729 goto nopwpmt;
730 #else /* !_WINDOWS */
731 rc = optionally_enter(user, q_line, 0, NETMAXUSER,
732 prompt, NULL, help, &flags);
733 #endif /* !_WINDOWS */
734 ps_global->dont_use_init_cmds = save_dont_use;
736 if(rc == 3) {
737 help = help == NO_HELP ? h_oe_login : NO_HELP;
738 continue;
741 /* default */
742 if(rc == 0 && !*user){
743 strncpy(user, defuser, NETMAXUSER);
744 user[NETMAXUSER-1] = '\0';
747 if(rc != 4)
748 break;
751 if(rc == 1 || !user[0]) {
752 ps_global->user_says_cancel = (rc == 1);
753 user[0] = '\0';
754 pwd[0] = '\0';
757 else{
758 strncpy(user, mb->user, NETMAXUSER);
759 user[NETMAXUSER-1] = '\0';
762 user[NETMAXUSER-1] = '\0';
763 pwd[NETMAXPASSWD-1] = '\0';
765 if(!(user[0] || altuserforcache)){
766 ps_global->no_newmail_check_from_optionally_enter = 0;
767 ps_global->in_init_seq = save_in_init;
768 return;
772 * Now that we have a user, we can check in the cache again to see
773 * if there is a password there. Try last working password associated
774 * with this host and user.
776 if(trial == 0L && !*mb->user && !altuserforcache){
777 if(imap_get_passwd(mm_login_list, pwd, user, hostlist,
778 (mb->sslflag||mb->tlsflag))){
779 ps_global->no_newmail_check_from_optionally_enter = 0;
780 ps_global->in_init_seq = save_in_init;
781 return;
784 #ifdef LOCAL_PASSWD_CACHE
785 if(get_passfile_passwd(ps_global->pinerc, pwd,
786 user, hostlist, (mb->sslflag||mb->tlsflag))){
787 imap_set_passwd(&mm_login_list, pwd, user,
788 hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
789 ps_global->no_newmail_check_from_optionally_enter = 0;
790 ps_global->in_init_seq = save_in_init;
791 return;
793 #endif /* LOCAL_PASSWD_CACHE */
795 else if(trial == 0 && altuserforcache){
796 if(imap_get_passwd(mm_login_list, pwd, altuserforcache, hostlist,
797 (mb->sslflag||mb->tlsflag))){
798 ps_global->no_newmail_check_from_optionally_enter = 0;
799 ps_global->in_init_seq = save_in_init;
800 return;
803 #ifdef LOCAL_PASSWD_CACHE
804 if(get_passfile_passwd(ps_global->pinerc, pwd,
805 altuserforcache, hostlist, (mb->sslflag||mb->tlsflag))){
806 imap_set_passwd(&mm_login_list, pwd, altuserforcache,
807 hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
808 ps_global->no_newmail_check_from_optionally_enter = 0;
809 ps_global->in_init_seq = save_in_init;
810 return;
812 #endif /* LOCAL_PASSWD_CACHE */
816 * Didn't find password in cache or this isn't the first try. Ask user.
818 help = NO_HELP;
821 * Need space for "Retrying - "
822 * "+ HOST: "
823 * hostname
824 * " (INSECURE) "
825 * " USER: "
826 * user
827 * " ENTER PASSWORD: "
828 * about 15 chars for input
831 snprintf(hostleadin, sizeof(hostleadin), "%s%s: ",
832 (!ps_global->ttyo && (mb->sslflag||mb->tlsflag)) ? "+ " : "", hostlabel);
834 strncpy(hostname, mb->host, sizeof(hostname)-1);
835 hostname[sizeof(hostname)-1] = '\0';
838 * Add port number to hostname if going through a tunnel or something
840 if(*non_def_port)
841 strncpy(port, non_def_port, sizeof(port));
842 else
843 port[0] = '\0';
845 insecure[0] = '\0';
847 /* if not encrypted and SSL/TLS is supported */
848 if(!(mb->sslflag||mb->tlsflag) &&
849 mail_parameters(NIL, GET_SSLDRIVER, NIL))
850 strncpy(insecure, insec, sizeof(insecure));
852 if(usethisprompt){
853 strncpy(logleadin, usethisprompt, sizeof(logleadin));
854 logleadin[sizeof(logleadin)-1] = '\0';
855 defubuf[0] = '\0';
856 user[0] = '\0';
858 else{
859 snprintf(logleadin, sizeof(logleadin), " %s: ", userlabel);
861 strncpy(defubuf, user, sizeof(defubuf)-1);
862 defubuf[sizeof(defubuf)-1] = '\0';
865 /* TRANSLATORS: user is being asked to type in their password */
866 snprintf(pwleadin, sizeof(pwleadin), " %s: ", _("ENTER PASSWORD"));
868 /* space reserved after prompt */
869 oespace = MAX(MIN(15, (ps_global->ttyo ? ps_global->ttyo->screen_cols : 80)/5), 6);
871 avail = ps_global->ttyo ? ps_global->ttyo->screen_cols : 80;
872 need = utf8_width(retry) + utf8_width(hostleadin) + strlen(hostname) + strlen(port) +
873 utf8_width(insecure) + utf8_width(logleadin) + strlen(defubuf) +
874 utf8_width(pwleadin) + oespace;
876 if(avail < need && trial > 0){
877 char *p;
879 len = strlen(hostname);
880 if((p = strchr(hostname, '.')) != NULL){
881 *p = '\0';
882 need -= (len - strlen(hostname));
886 if(avail < need){
887 need -= utf8_width(retry);
888 retry = "";
890 if(avail < need){
892 if(!usethisprompt){
893 snprintf(logleadin, sizeof(logleadin), " %s: ", userlabel);
894 need--;
897 rplstr(pwleadin, sizeof(pwleadin), 1, "");
898 need--;
900 if(avail < need){
901 /* get two spaces from hostleadin */
902 len = utf8_width(hostleadin);
903 snprintf(hostleadin, sizeof(hostleadin), "%s%s:",
904 (!ps_global->ttyo && (mb->sslflag||mb->tlsflag)) ? "+" : "", hostlabel);
905 hostleadin[sizeof(hostleadin)-1] = '\0';
906 need -= (len - utf8_width(hostleadin));
908 /* get rid of port */
909 if(avail < need && strlen(port) > 0){
910 need -= strlen(port);
911 port[0] = '\0';
914 if(avail < need){
915 len = utf8_width(pwleadin);
916 /* TRANSLATORS: An abbreviated form of ENTER PASSWORD */
917 snprintf(pwleadin, sizeof(pwleadin), " %s: ", _("PASSWORD"));
918 need -= (len - utf8_width(pwleadin));
922 if(avail < need){
923 int reduce_to;
926 * Reduce space for hostname. Best we can do is 6 chars
927 * with hos...
929 reduce_to = (need - avail < strlen(hostname) - 6) ? (strlen(hostname)-(need-avail)) : 6;
930 len = strlen(hostname);
931 strncpy(hostname+reduce_to-3, "...", 4);
932 need -= (len - strlen(hostname));
934 if(avail < need && strlen(insecure) > 0){
935 if(need - avail <= 3 && !strcmp(insecure," (INSECURE)")){
936 need -= 3;
937 insecure[strlen(insecure)-4] = ')';
938 insecure[strlen(insecure)-3] = '\0';
940 else{
941 need -= utf8_width(insecure);
942 insecure[0] = '\0';
946 if(avail < need){
947 len = utf8_width(logleadin);
948 strncpy(logleadin, " ", sizeof(logleadin));
949 logleadin[sizeof(logleadin)-1] = '\0';
950 need -= (len - utf8_width(logleadin));
952 if(avail < need){
953 reduce_to = (need - avail < strlen(defubuf) - 6) ? (strlen(defubuf)-(need-avail)) : 0;
954 if(reduce_to)
955 strncpy(defubuf+reduce_to-3, "...", 4);
956 else
957 defubuf[0] = '\0';
964 snprintf(prompt, sizeof(prompt), "%s%s%s%s%s%s%s%s",
965 retry, hostleadin, hostname, port, insecure, logleadin, defubuf, pwleadin);
966 prompt[sizeof(prompt)-1] = '\0';
968 *pwd = '\0';
969 while(1) {
970 if(ps_global->ttyo)
971 mm_login_alt_cue(mb);
973 save_dont_use = ps_global->dont_use_init_cmds;
974 ps_global->dont_use_init_cmds = 1;
975 flags = F_ON(F_QUELL_ASTERISKS, ps_global) ? OE_PASSWD_NOAST : OE_PASSWD;
976 #ifdef _WINDOWS
977 rc = os_login_dialog(mb, user, NETMAXUSER, pwd, NETMAXPASSWD, 0, 1,
978 &preserve_password);
979 #else /* !_WINDOWS */
980 rc = optionally_enter(pwd, q_line, 0, NETMAXPASSWD,
981 prompt, NULL, help, &flags);
982 #endif /* !_WINDOWS */
983 ps_global->dont_use_init_cmds = save_dont_use;
985 if(rc == 3) {
986 help = help == NO_HELP ? h_oe_passwd : NO_HELP;
988 else if(rc == 4){
990 else
991 break;
994 if(rc == 1 || !pwd[0]) {
995 ps_global->user_says_cancel = (rc == 1);
996 user[0] = pwd[0] = '\0';
997 ps_global->no_newmail_check_from_optionally_enter = 0;
998 ps_global->in_init_seq = save_in_init;
999 return;
1002 #ifdef _WINDOWS
1003 nopwpmt:
1004 #endif
1005 /* remember the password for next time */
1006 if(F_OFF(F_DISABLE_PASSWORD_CACHING,ps_global))
1007 imap_set_passwd(&mm_login_list, pwd,
1008 altuserforcache ? altuserforcache : user, hostlist,
1009 (mb->sslflag||mb->tlsflag), 0, 0);
1010 #ifdef LOCAL_PASSWD_CACHE
1011 /* if requested, remember it on disk for next session */
1012 if(save_password && F_OFF(F_DISABLE_PASSWORD_FILE_SAVING,ps_global))
1013 set_passfile_passwd(ps_global->pinerc, pwd,
1014 altuserforcache ? altuserforcache : user, hostlist,
1015 (mb->sslflag||mb->tlsflag),
1016 (preserve_password == -1 ? 0
1017 : (preserve_password == 0 ? 2 :1)));
1018 #endif /* LOCAL_PASSWD_CACHE */
1020 ps_global->no_newmail_check_from_optionally_enter = 0;
1024 void
1025 mm_login_alt_cue(NETMBX *mb)
1027 if(ps_global->ttyo){
1028 COLOR_PAIR *lastc;
1030 lastc = pico_set_colors(ps_global->VAR_TITLE_FORE_COLOR,
1031 ps_global->VAR_TITLE_BACK_COLOR,
1032 PSC_REV | PSC_RET);
1034 mark_titlebar_dirty();
1035 PutLine0(0, ps_global->ttyo->screen_cols - 1,
1036 (mb->sslflag||mb->tlsflag) ? "+" : " ");
1038 if(lastc){
1039 (void)pico_set_colorp(lastc, PSC_NONE);
1040 free_color_pair(&lastc);
1043 fflush(stdout);
1048 /*----------------------------------------------------------------------
1049 Receive notification of an error writing to disk
1051 Args: stream -- The stream the error occured on
1052 errcode -- The system error code (errno)
1053 serious -- Flag indicating error is serious (mail may be lost)
1055 Result: If error is non serious, the stream is marked as having an error
1056 and deletes are disallowed until error clears
1057 If error is serious this goes modal, allowing the user to retry
1058 or get a shell escape to fix the condition. When the condition is
1059 serious it means that mail existing in the mailbox will be lost
1060 if Pine exits without writing, so we try to induce the user to
1061 fix the error, go get someone that can fix the error, or whatever
1062 and don't provide an easy way out.
1063 ----*/
1064 long
1065 mm_diskerror (MAILSTREAM *stream, long int errcode, long int serious)
1067 int i, j;
1068 char *p, *q, *s;
1069 static ESCKEY_S de_opts[] = {
1070 {'r', 'r', "R", "Retry"},
1071 {'f', 'f', "F", "FileBrowser"},
1072 {'s', 's', "S", "ShellPrompt"},
1073 {-1, 0, NULL, NULL}
1075 #define DE_COLS (ps_global->ttyo->screen_cols)
1076 #define DE_LINE (ps_global->ttyo->screen_rows - 3)
1078 #define DE_FOLDER(X) (((X) && (X)->mailbox) ? (X)->mailbox : "<no folder>")
1079 #define DE_PMT \
1080 "Disk error! Choose Retry, or the File browser or Shell to clean up: "
1081 #define DE_STR1 "SERIOUS DISK ERROR WRITING: \"%s\""
1082 #define DE_STR2 \
1083 "The reported error number is %s. The last reported mail error was:"
1084 static char *de_msg[] = {
1085 "Please try to correct the error preventing Alpine from saving your",
1086 "mail folder. For example if the disk is out of space try removing",
1087 "unneeded files. You might also contact your system administrator.",
1089 "Both Alpine's File Browser and an option to enter the system's",
1090 "command prompt are offered to aid in fixing the problem. When",
1091 "you believe the problem is resolved, choose the \"Retry\" option.",
1092 "Be aware that messages may be lost or this folder left in an",
1093 "inaccessible condition if you exit or kill Alpine before the problem",
1094 "is resolved.",
1095 NULL};
1096 static char *de_shell_msg[] = {
1097 "\n\nPlease attempt to correct the error preventing saving of the",
1098 "mail folder. If you do not know how to correct the problem, contact",
1099 "your system administrator. To return to Alpine, type \"exit\".",
1100 NULL};
1102 dprint((0,
1103 "\n***** DISK ERROR on stream %s. Error code %ld. Error is %sserious\n",
1104 DE_FOLDER(stream), errcode, serious ? "" : "not "));
1105 dprint((0, "***** message: \"%s\"\n\n",
1106 ps_global->last_error ? ps_global->last_error : "?"));
1108 if(!serious) {
1109 sp_set_io_error_on_stream(stream, 1);
1110 return (1) ;
1113 while(1){
1114 /* replace pine's body display with screen full of explanatory text */
1115 ClearLine(2);
1116 PutLine1(2, MAX((DE_COLS - sizeof(DE_STR1)
1117 - strlen(DE_FOLDER(stream)))/2, 0),
1118 DE_STR1, DE_FOLDER(stream));
1119 ClearLine(3);
1120 PutLine1(3, 4, DE_STR2, long2string(errcode));
1122 PutLine0(4, 0, " \"");
1123 removing_leading_white_space(ps_global->last_error);
1124 for(i = 4, p = ps_global->last_error; *p && i < DE_LINE; ){
1125 for(s = NULL, q = p; *q && q - p < DE_COLS - 16; q++)
1126 if(isspace((unsigned char)*q))
1127 s = q;
1129 if(*q && s)
1130 q = s;
1132 while(p < q)
1133 Writechar(*p++, 0);
1135 if(*(p = q)){
1136 ClearLine(++i);
1137 PutLine0(i, 0, " ");
1138 while(*p && isspace((unsigned char)*p))
1139 p++;
1141 else{
1142 Writechar('\"', 0);
1143 CleartoEOLN();
1144 break;
1148 ClearLine(++i);
1149 for(j = ++i; i < DE_LINE && de_msg[i-j]; i++){
1150 ClearLine(i);
1151 PutLine0(i, 0, " ");
1152 Write_to_screen(de_msg[i-j]);
1155 while(i < DE_LINE)
1156 ClearLine(i++);
1158 switch(radio_buttons(DE_PMT, -FOOTER_ROWS(ps_global), de_opts,
1159 'r', 0, NO_HELP, RB_FLUSH_IN | RB_NO_NEWMAIL)){
1160 case 'r' : /* Retry! */
1161 ps_global->mangled_screen = 1;
1162 return(0L);
1164 case 'f' : /* File Browser */
1166 char full_filename[MAXPATH+1], filename[MAXPATH+1];
1168 filename[0] = '\0';
1169 build_path(full_filename, ps_global->home_dir, filename,
1170 sizeof(full_filename));
1171 file_lister("DISK ERROR", full_filename, sizeof(full_filename),
1172 filename, sizeof(filename), FALSE, FB_SAVE);
1175 break;
1177 case 's' :
1178 EndInverse();
1179 end_keyboard(ps_global ? F_ON(F_USE_FK,ps_global) : 0);
1180 end_tty_driver(ps_global);
1181 for(i = 0; de_shell_msg[i]; i++)
1182 puts(de_shell_msg[i]);
1185 * Don't use our piping mechanism to spawn a subshell here
1186 * since it will the server (thus reentering c-client).
1187 * Bad thing to do.
1189 #ifdef _WINDOWS
1190 #else
1191 system("csh");
1192 #endif
1193 init_tty_driver(ps_global);
1194 init_keyboard(F_ON(F_USE_FK,ps_global));
1195 break;
1198 if(ps_global->redrawer)
1199 (*ps_global->redrawer)();
1204 long
1205 pine_tcptimeout_noscreen(long int elapsed, long int sincelast, char *host)
1207 long rv = 1L;
1208 char pmt[128];
1210 #ifdef _WINDOWS
1211 mswin_killsplash();
1212 #endif
1214 if(elapsed >= (long)ps_global->tcp_query_timeout){
1215 snprintf(pmt, sizeof(pmt),
1216 _("No reply in %s seconds from server %s. Break connection"),
1217 long2string(elapsed), host);
1218 pmt[sizeof(pmt)-1] = '\0';
1219 if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
1220 ps_global->user_says_cancel = 1;
1221 return(0L);
1225 ps_global->tcptimeout = 0;
1226 return(rv);
1231 * -------------------------------------------------------------
1232 * These are declared in pith/imap.h as mandatory to implement.
1233 * -------------------------------------------------------------
1238 * pine_tcptimeout - C-client callback to handle tcp-related timeouts.
1240 long
1241 pine_tcptimeout(long int elapsed, long int sincelast, char *host)
1243 long rv = 1L; /* keep trying by default */
1244 unsigned long ch;
1246 ps_global->tcptimeout = 1;
1247 #ifdef DEBUG
1248 dprint((1, "tcptimeout: waited %s seconds, server: %s\n",
1249 long2string(elapsed), host));
1250 if(debugfile)
1251 fflush(debugfile);
1252 #endif
1254 #ifdef _WINDOWS
1255 mswin_killsplash();
1256 #endif
1258 if(ps_global->noshow_timeout)
1259 return(rv);
1261 if(!ps_global->ttyo)
1262 return(pine_tcptimeout_noscreen(elapsed, sincelast, host));
1264 suspend_busy_cue();
1267 * Prompt after a minute (since by then things are probably really bad)
1268 * A prompt timeout means "keep trying"...
1270 if(elapsed >= (long)ps_global->tcp_query_timeout){
1271 int clear_inverse;
1273 ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
1274 if((clear_inverse = !InverseState()) != 0)
1275 StartInverse();
1277 Writechar(BELL, 0);
1279 PutLine2(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global), 0,
1280 _("No reply in %s seconds from server %s. Break connection?"),
1281 long2string(elapsed), host);
1282 CleartoEOLN();
1283 fflush(stdout);
1284 flush_input();
1285 ch = read_char(7);
1286 if(ps_global->read_bail || ch == 'y' || ch == 'Y'){
1287 ps_global->read_bail = 0;
1288 ps_global->user_says_cancel = 1;
1289 rv = 0L;
1292 if(clear_inverse)
1293 EndInverse();
1295 ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
1298 if(rv == 1L){ /* just warn 'em something's up */
1299 q_status_message2(SM_ORDER, 0, 0,
1300 _("No reply in %s seconds from server %s. Still Waiting..."),
1301 long2string(elapsed), host);
1302 flush_status_messages(0); /* make sure it's seen */
1305 mark_status_dirty(); /* make sure it get's cleared */
1307 resume_busy_cue((rv == 1) ? 3 : 0);
1308 ps_global->tcptimeout = 0;
1310 return(rv);
1313 QUOTALIST *pine_quotalist_copy (QUOTALIST *pquota)
1315 QUOTALIST *cquota = NULL;
1317 if(pquota){
1318 cquota = mail_newquotalist();
1319 if (pquota->name && *pquota->name)
1320 cquota->name = cpystr(pquota->name);
1321 cquota->usage = pquota->usage;
1322 cquota->limit = pquota->limit;
1323 if (pquota->next)
1324 cquota->next = pine_quotalist_copy(pquota->next);
1326 return cquota;
1330 /* c-client callback to handle quota */
1332 void
1333 pine_parse_quota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
1335 ps_global->quota = pine_quotalist_copy (pquota);
1339 * C-client callback to handle SSL/TLS certificate validation failures
1341 * Returning 0 means error becomes fatal
1342 * Non-zero means certificate problem is ignored and SSL session is
1343 * established
1345 * We remember the answer and won't re-ask for subsequent open attempts to
1346 * the same hostname.
1348 long
1349 pine_sslcertquery(char *reason, char *host, char *cert)
1351 char tmp[500];
1352 char *unknown = "<unknown>";
1353 long rv = 0L;
1354 STRLIST_S hostlist;
1355 int ok_novalidate = 0, warned = 0;
1357 dprint((1, "sslcertificatequery: host=%s reason=%s cert=%s\n",
1358 host ? host : "?", reason ? reason : "?",
1359 cert ? cert : "?"));
1361 hostlist.name = host ? host : "";
1362 hostlist.next = NULL;
1365 * See if we've been asked about this host before.
1367 if(imap_get_ssl(cert_failure_list, &hostlist, &ok_novalidate, &warned)){
1368 /* we were asked before, did we say Yes? */
1369 if(ok_novalidate)
1370 rv++;
1372 if(rv){
1373 dprint((5,
1374 "sslcertificatequery: approved automatically\n"));
1375 return(rv);
1378 dprint((1, "sslcertificatequery: we were asked before and said No, so ask again\n"));
1381 if(ps_global->ttyo){
1382 SCROLL_S sargs;
1383 STORE_S *in_store, *out_store;
1384 gf_io_t pc, gc;
1385 HANDLE_S *handles = NULL;
1386 int the_answer = 'n';
1388 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS)) ||
1389 !(out_store = so_get(CharStar, NULL, EDIT_ACCESS)))
1390 goto try_wantto;
1392 so_puts(in_store, "<HTML><P>");
1393 so_puts(in_store, _("There was a failure validating the SSL/TLS certificate for the server"));
1395 so_puts(in_store, "<P><CENTER>");
1396 so_puts(in_store, host ? host : unknown);
1397 so_puts(in_store, "</CENTER>");
1399 so_puts(in_store, "<P>");
1400 so_puts(in_store, _("The reason for the failure was"));
1402 /* squirrel away details */
1403 if(details_host)
1404 fs_give((void **)&details_host);
1405 if(details_reason)
1406 fs_give((void **)&details_reason);
1407 if(details_cert)
1408 fs_give((void **)&details_cert);
1410 details_host = cpystr(host ? host : unknown);
1411 details_reason = cpystr(reason ? reason : unknown);
1412 details_cert = cpystr(cert ? cert : unknown);
1414 so_puts(in_store, "<P><CENTER>");
1415 snprintf(tmp, sizeof(tmp), "%s (<A HREF=\"X-Alpine-Cert:\">details</A>)",
1416 reason ? reason : unknown);
1417 tmp[sizeof(tmp)-1] = '\0';
1419 so_puts(in_store, tmp);
1420 so_puts(in_store, "</CENTER>");
1422 so_puts(in_store, "<P>");
1423 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."));
1425 so_puts(in_store, "<P>");
1426 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"));
1428 so_puts(in_store, "<P><CENTER>");
1429 so_puts(in_store, "/novalidate-cert");
1430 so_puts(in_store, "</CENTER>");
1432 so_puts(in_store, "<P>");
1433 so_puts(in_store, _("to the name of the folder you attempted to access. In other words, wherever you see the characters"));
1435 so_puts(in_store, "<P><CENTER>");
1436 so_puts(in_store, host ? host : unknown);
1437 so_puts(in_store, "</CENTER>");
1439 so_puts(in_store, "<P>");
1440 so_puts(in_store, _("in your configuration, replace those characters with"));
1442 so_puts(in_store, "<P><CENTER>");
1443 so_puts(in_store, host ? host : unknown);
1444 so_puts(in_store, "/novalidate-cert");
1445 so_puts(in_store, "</CENTER>");
1447 so_puts(in_store, "<P>");
1448 so_puts(in_store, _("Answer \"Yes\" to ignore the warning and continue, \"No\" to cancel the open of this folder."));
1450 so_seek(in_store, 0L, 0);
1451 init_handles(&handles);
1452 gf_filter_init();
1453 gf_link_filter(gf_html2plain,
1454 gf_html2plain_opt(NULL,
1455 ps_global->ttyo->screen_cols, non_messageview_margin(),
1456 &handles, NULL, GFHP_LOCAL_HANDLES));
1457 gf_set_so_readc(&gc, in_store);
1458 gf_set_so_writec(&pc, out_store);
1459 gf_pipe(gc, pc);
1460 gf_clear_so_writec(out_store);
1461 gf_clear_so_readc(in_store);
1463 memset(&sargs, 0, sizeof(SCROLL_S));
1464 sargs.text.handles = handles;
1465 sargs.text.text = so_text(out_store);
1466 sargs.text.src = CharStar;
1467 sargs.text.desc = _("help text");
1468 sargs.bar.title = _("SSL/TLS CERTIFICATE VALIDATION FAILURE");
1469 sargs.proc.tool = answer_cert_failure;
1470 sargs.proc.data.p = (void *)&the_answer;
1471 sargs.keys.menu = &ans_certquery_keymenu;
1472 /* don't want to re-enter c-client */
1473 sargs.quell_newmail = 1;
1474 setbitmap(sargs.keys.bitmap);
1475 sargs.help.text = h_tls_validation_failure;
1476 sargs.help.title = _("HELP FOR CERT VALIDATION FAILURE");
1478 scrolltool(&sargs);
1480 if(the_answer == 'y')
1481 rv++;
1483 ps_global->mangled_screen = 1;
1484 ps_global->painted_body_on_startup = 0;
1485 ps_global->painted_footer_on_startup = 0;
1486 so_give(&in_store);
1487 so_give(&out_store);
1488 free_handles(&handles);
1489 if(details_host)
1490 fs_give((void **)&details_host);
1491 if(details_reason)
1492 fs_give((void **)&details_reason);
1493 if(details_cert)
1494 fs_give((void **)&details_cert);
1496 else{
1498 * If screen hasn't been initialized yet, use want_to.
1500 try_wantto:
1501 memset((void *)tmp, 0, sizeof(tmp));
1502 strncpy(tmp,
1503 reason ? reason : _("SSL/TLS certificate validation failure"),
1504 sizeof(tmp));
1505 tmp[sizeof(tmp)-1] = '\0';
1506 strncat(tmp, _(": Continue anyway "), sizeof(tmp)-strlen(tmp)-1);
1508 if(want_to(tmp, 'n', 'x', NO_HELP, WT_NORM) == 'y')
1509 rv++;
1512 if(rv == 0)
1513 q_status_message1(SM_ORDER, 1, 3, _("Open of %s cancelled"),
1514 host ? host : unknown);
1516 imap_set_passwd(&cert_failure_list, "", "", &hostlist, 0, rv ? 1 : 0, 0);
1518 dprint((5, "sslcertificatequery: %s\n",
1519 rv ? "approved" : "rejected"));
1521 return(rv);
1525 char *
1526 pine_newsrcquery(MAILSTREAM *stream, char *mulname, char *name)
1528 char buf[MAILTMPLEN];
1530 if((can_access(mulname, ACCESS_EXISTS) == 0)
1531 || !(can_access(name, ACCESS_EXISTS) == 0))
1532 return(mulname);
1534 snprintf(buf, sizeof(buf),
1535 _("Rename newsrc \"%s%s\" for use as new host-specific newsrc"),
1536 last_cmpnt(name),
1537 strlen(last_cmpnt(name)) > 15 ? "..." : "");
1538 buf[sizeof(buf)-1] = '\0';
1539 if(want_to(buf, 'n', 'n', NO_HELP, WT_NORM) == 'y')
1540 rename_file(name, mulname);
1541 return(mulname);
1546 url_local_certdetails(char *url)
1548 if(!struncmp(url, "x-alpine-cert:", 14)){
1549 STORE_S *store;
1550 SCROLL_S sargs;
1551 char *folded;
1553 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
1554 q_status_message(SM_ORDER | SM_DING, 7, 10,
1555 _("Error allocating space for details."));
1556 return(0);
1559 so_puts(store, _("Host given by user:\n\n "));
1560 so_puts(store, details_host);
1561 so_puts(store, _("\n\nReason for failure:\n\n "));
1562 so_puts(store, details_reason);
1563 so_puts(store, _("\n\nCertificate being verified:\n\n"));
1564 folded = fold(details_cert, ps_global->ttyo->screen_cols, ps_global->ttyo->screen_cols, " ", " ", FLD_NONE);
1565 so_puts(store, folded);
1566 fs_give((void **)&folded);
1567 so_puts(store, "\n");
1569 memset(&sargs, 0, sizeof(SCROLL_S));
1570 sargs.text.text = so_text(store);
1571 sargs.text.src = CharStar;
1572 sargs.text.desc = _("Details");
1573 sargs.bar.title = _("CERT VALIDATION DETAILS");
1574 sargs.help.text = NO_HELP;
1575 sargs.help.title = NULL;
1576 sargs.quell_newmail = 1;
1577 sargs.help.text = h_tls_failure_details;
1578 sargs.help.title = _("HELP FOR CERT VALIDATION DETAILS");
1580 scrolltool(&sargs);
1582 so_give(&store); /* free resources associated with store */
1583 ps_global->mangled_screen = 1;
1584 return(1);
1587 return(0);
1592 * C-client callback to handle SSL/TLS certificate validation failures
1594 void
1595 pine_sslfailure(char *host, char *reason, long unsigned int flags)
1597 SCROLL_S sargs;
1598 STORE_S *store;
1599 int the_answer = 'n', indent, len, cols;
1600 char buf[500], buf2[500];
1601 char *folded;
1602 char *hst = host ? host : "<unknown>";
1603 char *rsn = reason ? reason : "<unknown>";
1604 char *notls = "/notls";
1605 STRLIST_S hostlist;
1606 int ok_novalidate = 0, warned = 0;
1609 dprint((1, "sslfailure: host=%s reason=%s\n",
1610 hst ? hst : "?",
1611 rsn ? rsn : "?"));
1613 if(flags & NET_SILENT)
1614 return;
1616 hostlist.name = host ? host : "";
1617 hostlist.next = NULL;
1620 * See if we've been told about this host before.
1622 if(imap_get_ssl(cert_failure_list, &hostlist, &ok_novalidate, &warned)){
1623 /* we were told already */
1624 if(warned){
1625 snprintf(buf, sizeof(buf), _("SSL/TLS failure for %s: %s"), hst, rsn);
1626 buf[sizeof(buf)-1] = '\0';
1627 mm_log(buf, ERROR);
1628 return;
1632 cols = ps_global->ttyo ? ps_global->ttyo->screen_cols : 80;
1633 cols--;
1635 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS)))
1636 return;
1638 strncpy(buf, _("There was an SSL/TLS failure for the server"), sizeof(buf));
1639 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1640 so_puts(store, folded);
1641 fs_give((void **)&folded);
1642 so_puts(store, "\n");
1644 if((len=strlen(hst)) <= cols){
1645 if((indent=((cols-len)/2)) > 0)
1646 so_puts(store, repeat_char(indent, SPACE));
1648 so_puts(store, hst);
1649 so_puts(store, "\n");
1651 else{
1652 strncpy(buf, hst, sizeof(buf));
1653 buf[sizeof(buf)-1] = '\0';
1654 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1655 so_puts(store, folded);
1656 fs_give((void **)&folded);
1659 so_puts(store, "\n");
1661 strncpy(buf, _("The reason for the failure was"), sizeof(buf));
1662 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1663 so_puts(store, folded);
1664 fs_give((void **)&folded);
1665 so_puts(store, "\n");
1667 if((len=strlen(rsn)) <= cols){
1668 if((indent=((cols-len)/2)) > 0)
1669 so_puts(store, repeat_char(indent, SPACE));
1671 so_puts(store, rsn);
1672 so_puts(store, "\n");
1674 else{
1675 strncpy(buf, rsn, sizeof(buf));
1676 buf[sizeof(buf)-1] = '\0';
1677 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1678 so_puts(store, folded);
1679 fs_give((void **)&folded);
1682 so_puts(store, "\n");
1684 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));
1685 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1686 so_puts(store, folded);
1687 fs_give((void **)&folded);
1688 so_puts(store, "\n");
1690 if((len=strlen(notls)) <= cols){
1691 if((indent=((cols-len)/2)) > 0)
1692 so_puts(store, repeat_char(indent, SPACE));
1694 so_puts(store, notls);
1695 so_puts(store, "\n");
1697 else{
1698 strncpy(buf, notls, sizeof(buf));
1699 buf[sizeof(buf)-1] = '\0';
1700 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1701 so_puts(store, folded);
1702 fs_give((void **)&folded);
1705 so_puts(store, "\n");
1707 strncpy(buf, _("to the name of the mail server you are attempting to access. In other words, wherever you see the characters"),
1708 sizeof(buf));
1709 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1710 so_puts(store, folded);
1711 fs_give((void **)&folded);
1712 so_puts(store, "\n");
1714 if((len=strlen(hst)) <= cols){
1715 if((indent=((cols-len)/2)) > 0)
1716 so_puts(store, repeat_char(indent, SPACE));
1718 so_puts(store, hst);
1719 so_puts(store, "\n");
1721 else{
1722 strncpy(buf, hst, sizeof(buf));
1723 buf[sizeof(buf)-1] = '\0';
1724 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1725 so_puts(store, folded);
1726 fs_give((void **)&folded);
1729 so_puts(store, "\n");
1731 strncpy(buf, _("in your configuration, replace those characters with"),
1732 sizeof(buf));
1733 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1734 so_puts(store, folded);
1735 fs_give((void **)&folded);
1736 so_puts(store, "\n");
1738 snprintf(buf2, sizeof(buf2), "%s%s", hst, notls);
1739 buf2[sizeof(buf2)-1] = '\0';
1740 if((len=strlen(buf2)) <= cols){
1741 if((indent=((cols-len)/2)) > 0)
1742 so_puts(store, repeat_char(indent, SPACE));
1744 so_puts(store, buf2);
1745 so_puts(store, "\n");
1747 else{
1748 strncpy(buf, buf2, sizeof(buf));
1749 buf[sizeof(buf)-1] = '\0';
1750 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1751 so_puts(store, folded);
1752 fs_give((void **)&folded);
1755 so_puts(store, "\n");
1757 if(ps_global->ttyo){
1758 strncpy(buf, _("Type RETURN to continue."), sizeof(buf));
1759 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1760 so_puts(store, folded);
1761 fs_give((void **)&folded);
1764 memset(&sargs, 0, sizeof(SCROLL_S));
1765 sargs.text.text = so_text(store);
1766 sargs.text.src = CharStar;
1767 sargs.text.desc = _("help text");
1768 sargs.bar.title = _("SSL/TLS FAILURE");
1769 sargs.proc.tool = answer_cert_failure;
1770 sargs.proc.data.p = (void *)&the_answer;
1771 sargs.keys.menu = &ans_certfail_keymenu;
1772 setbitmap(sargs.keys.bitmap);
1773 /* don't want to re-enter c-client */
1774 sargs.quell_newmail = 1;
1775 sargs.help.text = h_tls_failure;
1776 sargs.help.title = _("HELP FOR TLS/SSL FAILURE");
1778 if(ps_global->ttyo)
1779 scrolltool(&sargs);
1780 else{
1781 char **q, **qp;
1782 char *p;
1783 unsigned char c;
1784 int cnt = 0;
1787 * The screen isn't initialized yet, which should mean that this
1788 * is the result of a -p argument. Display_args_err knows how to deal
1789 * with the uninitialized screen, so we mess with the data to get it
1790 * in shape for display_args_err. This is pretty hacky.
1793 so_seek(store, 0L, 0); /* rewind */
1794 /* count the lines */
1795 while(so_readc(&c, store))
1796 if(c == '\n')
1797 cnt++;
1799 qp = q = (char **)fs_get((cnt+1) * sizeof(char *));
1800 memset(q, 0, (cnt+1) * sizeof(char *));
1802 so_seek(store, 0L, 0); /* rewind */
1803 p = buf;
1804 while(so_readc(&c, store)){
1805 if(c == '\n'){
1806 *p = '\0';
1807 *qp++ = cpystr(buf);
1808 p = buf;
1810 else
1811 *p++ = c;
1814 display_args_err(NULL, q, 0);
1815 free_list_array(&q);
1818 ps_global->mangled_screen = 1;
1819 ps_global->painted_body_on_startup = 0;
1820 ps_global->painted_footer_on_startup = 0;
1821 so_give(&store);
1823 imap_set_passwd(&cert_failure_list, "", "", &hostlist, 0, ok_novalidate, 1);
1828 answer_cert_failure(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
1830 int rv = 1;
1832 ps_global->next_screen = SCREEN_FUN_NULL;
1834 switch(cmd){
1835 case MC_YES :
1836 *(int *)(sparms->proc.data.p) = 'y';
1837 break;
1839 case MC_NO :
1840 *(int *)(sparms->proc.data.p) = 'n';
1841 break;
1843 default:
1844 alpine_panic("Unexpected command in answer_cert_failure");
1845 break;
1848 return(rv);
1852 /*----------------------------------------------------------------------
1853 This can be used to prevent the flickering of the check_cue char
1854 caused by numerous (5000+) fetches by c-client. Right now, the only
1855 practical use found is newsgroup subsciption.
1857 check_cue_display will check if this global is set, and won't clear
1858 the check_cue_char if set.
1859 ----*/
1860 void
1861 set_read_predicted(int i)
1863 ps_global->read_predicted = i==1;
1864 #ifdef _WINDOWS
1865 if(!i && F_ON(F_SHOW_DELAY_CUE, ps_global))
1866 check_cue_display(" ");
1867 #endif
1871 /*----------------------------------------------------------------------
1872 Exported method to retrieve logged in user name associated with stream
1874 Args: host -- host to find associated login name with.
1876 Result:
1877 ----*/
1878 void *
1879 pine_block_notify(int reason, void *data)
1881 switch(reason){
1882 case BLOCK_SENSITIVE: /* sensitive code, disallow alarms */
1883 break;
1885 case BLOCK_NONSENSITIVE: /* non-sensitive code, allow alarms */
1886 break;
1888 case BLOCK_TCPWRITE: /* blocked on TCP write */
1889 case BLOCK_FILELOCK: /* blocked on file locking */
1890 #ifdef _WINDOWS
1891 if(F_ON(F_SHOW_DELAY_CUE, ps_global))
1892 check_cue_display(">");
1894 mswin_setcursor(MSWIN_CURSOR_BUSY);
1895 #endif
1896 break;
1898 case BLOCK_DNSLOOKUP: /* blocked on DNS lookup */
1899 case BLOCK_TCPOPEN: /* blocked on TCP open */
1900 case BLOCK_TCPREAD: /* blocked on TCP read */
1901 case BLOCK_TCPCLOSE: /* blocked on TCP close */
1902 #ifdef _WINDOWS
1903 if(F_ON(F_SHOW_DELAY_CUE, ps_global))
1904 check_cue_display("<");
1906 mswin_setcursor(MSWIN_CURSOR_BUSY);
1907 #endif
1908 break;
1910 default :
1911 case BLOCK_NONE: /* not blocked */
1912 #ifdef _WINDOWS
1913 if(F_ON(F_SHOW_DELAY_CUE, ps_global))
1914 check_cue_display(" ");
1915 #endif
1916 break;
1920 return(NULL);
1924 void
1925 mm_expunged_current(long unsigned int rawno)
1927 /* expunged something we're viewing? */
1928 if(!ps_global->expunge_in_progress
1929 && (mn_is_cur(ps_global->msgmap, mn_raw2m(ps_global->msgmap, (long) rawno))
1930 && (ps_global->prev_screen == mail_view_screen
1931 || ps_global->prev_screen == attachment_screen))){
1932 ps_global->next_screen = mail_index_screen;
1933 q_status_message(SM_ORDER | SM_DING , 3, 3,
1934 "Message you were viewing is gone!");
1939 #ifdef PASSFILE
1942 * Specific functions to support caching username/passwd/host
1943 * triples on disk for use from one session to the next...
1946 #define FIRSTCH 0x20
1947 #define LASTCH 0x7e
1948 #define TABSZ (LASTCH - FIRSTCH + 1)
1950 static int xlate_key;
1954 * xlate_in() - xlate_in the given character
1956 char
1957 xlate_in(int c)
1959 register int eti;
1961 eti = xlate_key;
1962 if((c >= FIRSTCH) && (c <= LASTCH)){
1963 eti += (c - FIRSTCH);
1964 eti -= (eti >= 2*TABSZ) ? 2*TABSZ : (eti >= TABSZ) ? TABSZ : 0;
1965 return((xlate_key = eti) + FIRSTCH);
1967 else
1968 return(c);
1973 * xlate_out() - xlate_out the given character
1975 char
1976 xlate_out(char c)
1978 register int dti;
1979 register int xch;
1981 if((c >= FIRSTCH) && (c <= LASTCH)){
1982 xch = c - (dti = xlate_key);
1983 xch += (xch < FIRSTCH-TABSZ) ? 2*TABSZ : (xch < FIRSTCH) ? TABSZ : 0;
1984 dti = (xch - FIRSTCH) + dti;
1985 dti -= (dti >= 2*TABSZ) ? 2*TABSZ : (dti >= TABSZ) ? TABSZ : 0;
1986 xlate_key = dti;
1987 return(xch);
1989 else
1990 return(c);
1992 #endif /* PASSFILE */
1995 #ifdef LOCAL_PASSWD_CACHE
1998 int
1999 line_get(char *tmp, size_t len, char **textp)
2001 char *s;
2003 tmp[0] = '\0';
2004 if (*textp == NULL
2005 || (s = strchr(*textp, '\n')) == NULL
2006 || (s - *textp) > len - 1)
2007 return 0;
2009 *s = '\0';
2010 if(*(s-1) == '\r')
2011 *(s-1) = '\0';
2013 snprintf(tmp, len, "%s\n", *textp);
2014 tmp[len-1] = '\0';
2015 *textp = s+1;
2017 return 1;
2020 * For UNIX:
2021 * Passfile lines are
2023 * passwd TAB user TAB hostname TAB flags [ TAB orig_hostname ] \n
2025 * In pine4.40 and before there was no orig_hostname, and there still isn't
2026 * if it is the same as hostname.
2028 * else for WINDOWS:
2029 * Use Windows credentials. The TargetName of the credential is
2030 * UWash_Alpine_<hostname:port>\tuser\taltflag
2031 * and the blob consists of
2032 * passwd\torighost (if different from host)
2034 * We don't use anything fancy we just copy out all the credentials which
2035 * begin with TNAME and put them into our cache, so we don't lookup based
2036 * on the TargetName or anything like that. That was so we could re-use
2037 * the existing code and so that orighost data could be easily used.
2040 read_passfile(pinerc, l)
2041 char *pinerc;
2042 MMLOGIN_S **l;
2044 #ifdef WINCRED
2045 # if (WINCRED > 0)
2046 LPCTSTR lfilter = TEXT(TNAMESTAR);
2047 DWORD count, k;
2048 PCREDENTIAL *pcred;
2049 char *tmp, *blob, *target = NULL;
2050 char *host, *user, *sflags, *passwd, *orighost;
2051 char *ui[5];
2052 int i, j;
2054 if(using_passfile == 0)
2055 return(using_passfile);
2057 if(!g_CredInited){
2058 if(init_wincred_funcs() != 1){
2059 using_passfile = 0;
2060 return(using_passfile);
2064 dprint((9, "read_passfile\n"));
2066 using_passfile = 1;
2068 if(g_CredEnumerateW(lfilter, 0, &count, &pcred)){
2069 if(pcred){
2070 for(k = 0; k < count; k++){
2072 host = user = sflags = passwd = orighost = NULL;
2073 ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
2075 target = lptstr_to_utf8(pcred[k]->TargetName);
2076 tmp = srchstr(target, TNAME);
2078 if(tmp){
2079 tmp += strlen(TNAME);
2080 for(i = 0, j = 0; tmp[i] && j < 3; j++){
2081 for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
2082 ; /* find end of data */
2084 if(tmp[i])
2085 tmp[i++] = '\0'; /* tie off data */
2088 host = ui[0];
2089 user = ui[1];
2090 sflags = ui[2];
2093 blob = (char *) pcred[k]->CredentialBlob;
2094 if(blob){
2095 for(i = 0, j = 3; blob[i] && j < 5; j++){
2096 for(ui[j] = &blob[i]; blob[i] && blob[i] != '\t'; i++)
2097 ; /* find end of data */
2099 if(blob[i])
2100 blob[i++] = '\0'; /* tie off data */
2103 passwd = ui[3];
2104 orighost = ui[4];
2107 if(passwd && host && user){ /* valid field? */
2108 STRLIST_S hostlist[2];
2109 int flags = sflags ? atoi(sflags) : 0;
2111 hostlist[0].name = host;
2112 if(orighost){
2113 hostlist[0].next = &hostlist[1];
2114 hostlist[1].name = orighost;
2115 hostlist[1].next = NULL;
2117 else{
2118 hostlist[0].next = NULL;
2121 imap_set_passwd(l, passwd, user, hostlist, flags & 0x01, 0, 0);
2124 if(target)
2125 fs_give((void **) &target);
2128 g_CredFree((PVOID) pcred);
2132 return(1);
2134 # else /* old windows */
2135 using_passfile = 0;
2136 return(0);
2137 # endif
2139 #elif APPLEKEYCHAIN
2141 char target[MAILTMPLEN];
2142 char *tmp, *host, *user, *sflags, *passwd, *orighost;
2143 char *ui[5];
2144 int i, j, k, rc;
2145 SecKeychainAttributeList attrList;
2146 SecKeychainSearchRef searchRef = NULL;
2147 SecKeychainAttribute attrs[] = {
2148 { kSecAccountItemAttr, strlen(TNAME), TNAME }
2151 if(using_passfile == 0)
2152 return(using_passfile);
2154 dprint((9, "read_passfile\n"));
2157 /* search for only our items in the keychain */
2158 attrList.count = 1;
2159 attrList.attr = attrs;
2161 using_passfile = 1;
2162 if(!(rc=SecKeychainSearchCreateFromAttributes(NULL,
2163 kSecGenericPasswordItemClass,
2164 &attrList,
2165 &searchRef))){
2166 dprint((10, "read_passfile: SecKeychainSearchCreate succeeded\n"));
2167 if(searchRef){
2168 SecKeychainItemRef itemRef = NULL;
2169 SecKeychainAttributeInfo info;
2170 SecKeychainAttributeList *attrList = NULL;
2171 UInt32 blength = 0;
2172 char *blob = NULL;
2173 char *blobcopy = NULL; /* NULL terminated copy */
2175 UInt32 tags[] = {kSecAccountItemAttr,
2176 kSecServiceItemAttr};
2177 UInt32 formats[] = {0,0};
2179 dprint((10, "read_passfile: searchRef not NULL\n"));
2180 info.count = 2;
2181 info.tag = tags;
2182 info.format = formats;
2185 * Go through each item we found and put it
2186 * into our list.
2188 while(!(rc=SecKeychainSearchCopyNext(searchRef, &itemRef)) && itemRef){
2189 dprint((10, "read_passfile: SecKeychainSearchCopyNext got one\n"));
2190 rc = SecKeychainItemCopyAttributesAndData(itemRef,
2191 &info, NULL,
2192 &attrList,
2193 &blength,
2194 (void **) &blob);
2195 if(rc == 0 && attrList){
2196 dprint((10, "read_passfile: SecKeychainItemCopyAttributesAndData succeeded, count=%d\n", attrList->count));
2198 blobcopy = (char *) fs_get((blength + 1) * sizeof(char));
2199 strncpy(blobcopy, (char *) blob, blength);
2200 blobcopy[blength] = '\0';
2203 * I'm not real clear on how this works. It seems to be
2204 * necessary to combine the attributes from two passes
2205 * (attrList->count == 2) in order to get the full set
2206 * of attributes we inserted into the keychain in the
2207 * first place. So, we reset host...orighost outside of
2208 * the following for loop, not inside.
2210 host = user = sflags = passwd = orighost = NULL;
2211 ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
2213 for(k = 0; k < attrList->count; k++){
2215 if(attrList->attr[k].length){
2216 strncpy(target,
2217 (char *) attrList->attr[k].data,
2218 MIN(attrList->attr[k].length,sizeof(target)));
2219 target[MIN(attrList->attr[k].length,sizeof(target)-1)] = '\0';
2222 tmp = target;
2223 for(i = 0, j = 0; tmp[i] && j < 3; j++){
2224 for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
2225 ; /* find end of data */
2227 if(tmp[i])
2228 tmp[i++] = '\0'; /* tie off data */
2231 if(ui[0])
2232 host = ui[0];
2234 if(ui[1])
2235 user = ui[1];
2237 if(ui[2])
2238 sflags = ui[2];
2240 for(i = 0, j = 3; blobcopy[i] && j < 5; j++){
2241 for(ui[j] = &blobcopy[i]; blobcopy[i] && blobcopy[i] != '\t'; i++)
2242 ; /* find end of data */
2244 if(blobcopy[i])
2245 blobcopy[i++] = '\0'; /* tie off data */
2248 if(ui[3])
2249 passwd = ui[3];
2251 if(ui[4])
2252 orighost = ui[4];
2254 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:""));
2257 if(passwd && host && user){ /* valid field? */
2258 STRLIST_S hostlist[2];
2259 int flags = sflags ? atoi(sflags) : 0;
2261 hostlist[0].name = host;
2262 if(orighost){
2263 hostlist[0].next = &hostlist[1];
2264 hostlist[1].name = orighost;
2265 hostlist[1].next = NULL;
2267 else{
2268 hostlist[0].next = NULL;
2271 imap_set_passwd(l, passwd, user, hostlist, flags & 0x01, 0, 0);
2274 if(blobcopy)
2275 fs_give((void **) & blobcopy);
2277 SecKeychainItemFreeAttributesAndData(attrList, (void *) blob);
2279 else{
2280 using_passfile = 0;
2281 dprint((10, "read_passfile: SecKeychainItemCopyAttributesAndData failed, rc=%d\n", rc));
2284 CFRelease(itemRef);
2285 itemRef = NULL;
2288 CFRelease(searchRef);
2290 else{
2291 using_passfile = 0;
2292 dprint((10, "read_passfile: searchRef NULL\n"));
2295 else{
2296 using_passfile = 0;
2297 dprint((10, "read_passfile: SecKeychainSearchCreateFromAttributes failed, rc=%d\n", rc));
2300 return(using_passfile);
2302 #else /* PASSFILE */
2304 char tmp[MAILTMPLEN], *ui[5];
2305 int i, j, n;
2306 #ifdef SMIME
2307 char tmp2[MAILTMPLEN];
2308 char *text = NULL, *text2 = NULL;
2309 int encrypted = 0;
2310 #endif /* SMIME */
2311 FILE *fp;
2313 if(using_passfile == 0)
2314 return(using_passfile);
2316 dprint((9, "read_passfile\n"));
2318 /* if there's no password to read, bag it!! */
2319 if(!passfile_name(pinerc, tmp, sizeof(tmp)) || !(fp = our_fopen(tmp, "rb"))){
2320 using_passfile = 0;
2321 return(using_passfile);
2324 #ifdef SMIME
2325 /* the next call initializes the key/certificate pair used to
2326 * encrypt and decrypt a password file. The details of how this is
2327 * done is in the file pith/smime.c. During this setup we might call
2328 * smime_init(), but no matter what happens we must call smime_deinit()
2329 * there. The reason why this is so is because we can not assume that
2330 * the .pinerc file has been read by this time, so this code might not
2331 * know about the ps_global->smime structure or any of its components,
2332 * and it shouldn't because it only needs ps_global->pwdcert, so
2333 * do not init smime here, because the .pinerc might not have been
2334 * read and we do not really know where the keys and certificates really
2335 * are.
2337 if(ps_global->pwdcert == NULL)
2338 setup_pwdcert(&ps_global->pwdcert);
2339 tmp2[0] = '\0';
2340 fgets(tmp2, sizeof(tmp2), fp);
2341 fclose(fp);
2342 if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){
2343 if(encrypt_file((char *)tmp, NULL, (PERSONAL_CERT *)ps_global->pwdcert))
2344 encrypted++;
2346 else
2347 encrypted++;
2350 * if password file is encrypted we attemtp to decrypt. We ask the
2351 * user for the password to unlock the password file. If the user
2352 * enters the password and it unlocks the file, use it and keep saving
2353 * passwords in it. If the user enters the wrong passwords and does
2354 * not unlock it, we will not see that here, but in decrypt_file, so
2355 * the only other possibility is that the user cancels. In that case
2356 * we will see i == -1. In that case, we will let the user attempt
2357 * manual login to the server they want to login, but passwords will
2358 * not be saved so that the password file will not be saved
2359 * unencrypted and rewritten again.
2361 if(encrypted){
2362 text = text2 = decrypt_file((char *)tmp, &i, (PERSONAL_CERT *)ps_global->pwdcert);
2363 switch(i){
2364 case -2: using_passfile = 0;
2365 break;
2367 case 1 : save_password = 1;
2368 using_passfile = 1;
2369 break;
2371 case -1: save_password = 0;
2372 using_passfile = 1;
2373 break;
2375 default: break;
2378 else
2379 fp = our_fopen(tmp, "rb"); /* reopen to read data */
2380 #endif /* SMIME */
2382 if(using_passfile == 0){
2383 #ifdef SMIME
2384 if(text) fs_give((void **)&text);
2385 #endif /* SMIME */
2386 return using_passfile;
2389 #ifdef SMIME
2390 for(n = 0; encrypted ? line_get(tmp, sizeof(tmp), &text2)
2391 : (fgets(tmp, sizeof(tmp), fp) != NULL); n++){
2392 #else /* SMIME */
2393 for(n = 0; fgets(tmp, sizeof(tmp), fp); n++){
2394 #endif /* SMIME */
2395 /*** do any necessary DEcryption here ***/
2396 xlate_key = n;
2397 for(i = 0; tmp[i]; i++)
2398 tmp[i] = xlate_out(tmp[i]);
2400 if(i && tmp[i-1] == '\n')
2401 tmp[i-1] = '\0'; /* blast '\n' */
2403 dprint((10, "read_passfile: %s\n", tmp ? tmp : "?"));
2404 ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
2405 for(i = 0, j = 0; tmp[i] && j < 5; j++){
2406 for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
2407 ; /* find end of data */
2409 if(tmp[i])
2410 tmp[i++] = '\0'; /* tie off data */
2413 dprint((10, "read_passfile: calling imap_set_passwd\n"));
2414 if(ui[0] && ui[1] && ui[2]){ /* valid field? */
2415 STRLIST_S hostlist[2];
2416 int flags = ui[3] ? atoi(ui[3]) : 0;
2418 hostlist[0].name = ui[2];
2419 if(ui[4]){
2420 hostlist[0].next = &hostlist[1];
2421 hostlist[1].name = ui[4];
2422 hostlist[1].next = NULL;
2424 else{
2425 hostlist[0].next = NULL;
2428 imap_set_passwd(l, ui[0], ui[1], hostlist, flags & 0x01, 0, 0);
2432 #ifdef SMIME
2433 if (text) fs_give((void **)&text);
2434 #else /* SMIME */
2435 fclose(fp);
2436 #endif /* SMIME */
2437 return(1);
2438 #endif /* PASSFILE */
2443 void
2444 write_passfile(pinerc, l)
2445 char *pinerc;
2446 MMLOGIN_S *l;
2448 #ifdef WINCRED
2449 # if (WINCRED > 0)
2450 char target[MAILTMPLEN];
2451 char blob[MAILTMPLEN];
2452 CREDENTIAL cred;
2453 LPTSTR ltarget = 0;
2455 if(using_passfile == 0)
2456 return;
2458 dprint((9, "write_passfile\n"));
2460 for(; l; l = l->next){
2461 snprintf(target, sizeof(target), "%s%s\t%s\t%d",
2462 TNAME,
2463 (l->hosts && l->hosts->name) ? l->hosts->name : "",
2464 l->user ? l->user : "",
2465 l->altflag);
2466 ltarget = utf8_to_lptstr((LPSTR) target);
2468 if(ltarget){
2469 snprintf(blob, sizeof(blob), "%s%s%s",
2470 l->passwd ? l->passwd : "",
2471 (l->hosts && l->hosts->next && l->hosts->next->name)
2472 ? "\t" : "",
2473 (l->hosts && l->hosts->next && l->hosts->next->name)
2474 ? l->hosts->next->name : "");
2475 memset((void *) &cred, 0, sizeof(cred));
2476 cred.Flags = 0;
2477 cred.Type = CRED_TYPE_GENERIC;
2478 cred.TargetName = ltarget;
2479 cred.CredentialBlobSize = strlen(blob)+1;
2480 cred.CredentialBlob = (LPBYTE) &blob;
2481 cred.Persist = CRED_PERSIST_ENTERPRISE;
2482 g_CredWriteW(&cred, 0);
2484 fs_give((void **) &ltarget);
2487 #endif /* WINCRED > 0 */
2489 #elif APPLEKEYCHAIN
2491 int rc;
2492 char target[MAILTMPLEN];
2493 char blob[MAILTMPLEN];
2494 SecKeychainItemRef itemRef = NULL;
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\t%s\t%d",
2503 (l->hosts && l->hosts->name) ? l->hosts->name : "",
2504 l->user ? l->user : "",
2505 l->altflag);
2507 snprintf(blob, sizeof(blob), "%s%s%s",
2508 l->passwd ? l->passwd : "",
2509 (l->hosts && l->hosts->next && l->hosts->next->name)
2510 ? "\t" : "",
2511 (l->hosts && l->hosts->next && l->hosts->next->name)
2512 ? l->hosts->next->name : "");
2514 dprint((10, "write_passfile: SecKeychainAddGenericPassword(NULL, %d, %s, %d, %s, %d, %s, NULL)\n", strlen(target), target, strlen(TNAME), TNAME, strlen(blob), blob));
2516 rc = SecKeychainAddGenericPassword(NULL,
2517 strlen(target), target,
2518 strlen(TNAME), TNAME,
2519 strlen(blob), blob,
2520 NULL);
2521 if(rc==0){
2522 dprint((10, "write_passfile: SecKeychainAddGenericPassword succeeded\n"));
2524 else{
2525 dprint((10, "write_passfile: SecKeychainAddGenericPassword returned rc=%d\n", rc));
2528 if(rc == errSecDuplicateItem){
2529 /* fix existing entry */
2530 dprint((10, "write_passfile: SecKeychainAddGenericPassword found existing entry\n"));
2531 itemRef = NULL;
2532 if(!(rc=SecKeychainFindGenericPassword(NULL,
2533 strlen(target), target,
2534 strlen(TNAME), TNAME,
2535 NULL, NULL,
2536 &itemRef)) && itemRef){
2538 rc = SecKeychainItemModifyAttributesAndData(itemRef, NULL, strlen(blob), blob);
2539 if(!rc){
2540 dprint((10, "write_passfile: SecKeychainItemModifyAttributesAndData returned rc=%d\n", rc));
2543 else{
2544 dprint((10, "write_passfile: SecKeychainFindGenericPassword returned rc=%d\n", rc));
2549 #else /* PASSFILE */
2551 char tmp[MAILTMPLEN];
2552 int i, n;
2553 FILE *fp;
2554 #ifdef SMIME
2555 char *text = NULL, tmp2[MAILTMPLEN];
2556 int len = 0;
2557 #endif
2559 if(using_passfile == 0)
2560 return;
2562 dprint((9, "write_passfile\n"));
2564 /* if there's no passfile to read, bag it!! */
2565 if(!passfile_name(pinerc, tmp, sizeof(tmp)) || !(fp = our_fopen(tmp, "wb"))){
2566 using_passfile = 0;
2567 return;
2570 #ifdef SMIME
2571 strncpy(tmp2, tmp, sizeof(tmp2)-1);
2572 tmp2[sizeof(tmp2)-1] = '\0';
2573 #endif /* SMIME */
2575 for(n = 0; l; l = l->next, n++){
2576 /*** do any necessary ENcryption here ***/
2577 snprintf(tmp, sizeof(tmp), "%s\t%s\t%s\t%d%s%s\n", l->passwd, l->user,
2578 l->hosts->name, l->altflag,
2579 (l->hosts->next && l->hosts->next->name) ? "\t" : "",
2580 (l->hosts->next && l->hosts->next->name) ? l->hosts->next->name
2581 : "");
2582 dprint((10, "write_passfile: %s", tmp ? tmp : "?"));
2583 xlate_key = n;
2584 for(i = 0; tmp[i]; i++)
2585 tmp[i] = xlate_in(tmp[i]);
2587 #ifdef SMIME
2588 if(len == 0){
2589 len = strlen(tmp) + 1;
2590 text = fs_get(len*sizeof(char));
2591 *text = '\0';
2593 if(strlen(text) + strlen(tmp) > len){
2594 len = strlen(text) + strlen(tmp) + 1;
2595 fs_resize((void **)&text, len*sizeof(char));
2597 strncat(text, tmp, strlen(tmp));
2598 #else /* SMIME */
2599 fputs(tmp, fp);
2600 #endif /* SMIME */
2603 fclose(fp);
2604 #ifdef SMIME
2605 if(text != NULL){
2606 if(encrypt_file((char *)tmp2, text, ps_global->pwdcert) == 0){
2607 if((fp = our_fopen(tmp2, "wb")) != NULL){
2608 fputs(text, fp);
2609 fclose(fp);
2612 fs_give((void **)&text);
2614 #endif /* SMIME */
2615 #endif /* PASSFILE */
2618 #endif /* LOCAL_PASSWD_CACHE */
2621 #if (WINCRED > 0)
2622 void
2623 erase_windows_credentials(void)
2625 LPCTSTR lfilter = TEXT(TNAMESTAR);
2626 DWORD count, k;
2627 PCREDENTIAL *pcred;
2629 if(!g_CredInited){
2630 if(init_wincred_funcs() != 1)
2631 return;
2634 if(g_CredEnumerateW(lfilter, 0, &count, &pcred)){
2635 if(pcred){
2636 for(k = 0; k < count; k++)
2637 g_CredDeleteW(pcred[k]->TargetName, CRED_TYPE_GENERIC, 0);
2639 g_CredFree((PVOID) pcred);
2644 void
2645 ask_erase_credentials(void)
2647 if(want_to(_("Erase previously preserved passwords"), 'y', 'x', NO_HELP, WT_NORM) == 'y'){
2648 erase_windows_credentials();
2649 q_status_message(SM_ORDER, 3, 3, "All preserved passwords have been erased");
2651 else
2652 q_status_message(SM_ORDER, 3, 3, "Previously preserved passwords will not be erased");
2655 #endif /* WINCRED */
2658 #ifdef LOCAL_PASSWD_CACHE
2661 * get_passfile_passwd - return the password contained in the special passord
2662 * cache. The file is assumed to be in the same directory
2663 * as the pinerc with the name defined above.
2666 get_passfile_passwd(pinerc, passwd, user, hostlist, altflag)
2667 char *pinerc, *passwd, *user;
2668 STRLIST_S *hostlist;
2669 int altflag;
2671 dprint((10, "get_passfile_passwd\n"));
2672 return((passfile_cache || read_passfile(pinerc, &passfile_cache))
2673 ? imap_get_passwd(passfile_cache, passwd,
2674 user, hostlist, altflag)
2675 : 0);
2679 is_using_passfile()
2681 return(using_passfile == 1);
2685 * Just trying to guess the username the user might want to use on this
2686 * host, the user will confirm.
2688 char *
2689 get_passfile_user(pinerc, hostlist)
2690 char *pinerc;
2691 STRLIST_S *hostlist;
2693 return((passfile_cache || read_passfile(pinerc, &passfile_cache))
2694 ? imap_get_user(passfile_cache, hostlist)
2695 : NULL);
2700 preserve_prompt(char *pinerc)
2702 #ifdef WINCRED
2703 # if (WINCRED > 0)
2705 * This prompt was going to be able to be turned on and off via a registry
2706 * setting controlled from the config menu. We decided to always use the
2707 * dialog for login, and there the prompt is unobtrusive enough to always
2708 * be in there. As a result, windows should never reach this, but now
2709 * OS X somewhat uses the behavior just described.
2711 if(mswin_store_pass_prompt()
2712 && (want_to(_("Preserve password for next login"),
2713 'y', 'x', NO_HELP, WT_NORM)
2714 == 'y'))
2715 return(1);
2716 else
2717 return(0);
2718 # else
2719 return(0);
2720 # endif
2722 #elif APPLEKEYCHAIN
2724 int rc;
2725 if((rc = macos_store_pass_prompt()) != 0){
2726 if(want_to(_("Preserve password for next login"),
2727 'y', 'x', NO_HELP, WT_NORM)
2728 == 'y'){
2729 if(rc == -1){
2730 macos_set_store_pass_prompt(1);
2731 q_status_message(SM_ORDER, 4, 4,
2732 _("Stop \"Preserve passwords?\" prompts by deleting Alpine Keychain entry"));
2734 return(1);
2736 else if(rc == -1){
2737 macos_set_store_pass_prompt(0);
2738 q_status_message(SM_ORDER, 4, 4,
2739 _("Restart \"Preserve passwords?\" prompts by deleting Alpine Keychain entry"));
2741 return(0);
2743 return(0);
2744 #else /* PASSFILE */
2745 char tmp[MAILTMPLEN];
2746 struct stat sbuf;
2748 if(!passfile_name(pinerc, tmp, sizeof(tmp)) || our_stat(tmp, &sbuf) < 0)
2749 return 0;
2751 if(F_OFF(F_DISABLE_PASSWORD_FILE_SAVING,ps_global))
2752 return(want_to(_("Preserve password on DISK for next login"),
2753 'y', 'x', NO_HELP, WT_NORM)
2754 == 'y');
2755 return(0);
2756 #endif /* PASSFILE */
2759 #endif /* LOCAL_PASSWD_CACHE */
2762 #ifdef APPLEKEYCHAIN
2765 * Returns:
2766 * 1 if store pass prompt is set in the "registry" to on
2767 * 0 if set to off
2768 * -1 if not set to anything
2771 macos_store_pass_prompt(void)
2773 char *data = NULL;
2774 UInt32 len = 0;
2775 int rc = -1;
2776 int val;
2778 if(storepassprompt == -1){
2779 if(!(rc=SecKeychainFindGenericPassword(NULL, 0, NULL,
2780 strlen(TNAMEPROMPT),
2781 TNAMEPROMPT, &len,
2782 (void **) &data, NULL))){
2783 val = (len == 1 && data && data[0] == '1');
2787 if(storepassprompt == -1 && !rc){
2788 if(val)
2789 storepassprompt = 1;
2790 else
2791 storepassprompt = 0;
2794 return(storepassprompt);
2798 void
2799 macos_set_store_pass_prompt(int val)
2801 storepassprompt = val ? 1 : 0;
2803 SecKeychainAddGenericPassword(NULL, 0, NULL, strlen(TNAMEPROMPT),
2804 TNAMEPROMPT, 1, val ? "1" : "0", NULL);
2808 void
2809 macos_erase_keychain(void)
2811 SecKeychainAttributeList attrList;
2812 SecKeychainSearchRef searchRef = NULL;
2813 SecKeychainAttribute attrs1[] = {
2814 { kSecAccountItemAttr, strlen(TNAME), TNAME }
2816 SecKeychainAttribute attrs2[] = {
2817 { kSecAccountItemAttr, strlen(TNAMEPROMPT), TNAMEPROMPT }
2820 dprint((9, "macos_erase_keychain\n"));
2823 * Seems like we ought to be able to combine attrs1 and attrs2
2824 * into a single array, but I couldn't get it to work.
2827 /* search for only our items in the keychain */
2828 attrList.count = 1;
2829 attrList.attr = attrs1;
2831 if(!SecKeychainSearchCreateFromAttributes(NULL,
2832 kSecGenericPasswordItemClass,
2833 &attrList,
2834 &searchRef)){
2835 if(searchRef){
2836 SecKeychainItemRef itemRef = NULL;
2839 * Go through each item we found and put it
2840 * into our list.
2842 while(!SecKeychainSearchCopyNext(searchRef, &itemRef) && itemRef){
2843 dprint((10, "read_passfile: SecKeychainSearchCopyNext got one\n"));
2844 SecKeychainItemDelete(itemRef);
2845 CFRelease(itemRef);
2848 CFRelease(searchRef);
2852 attrList.count = 1;
2853 attrList.attr = attrs2;
2855 if(!SecKeychainSearchCreateFromAttributes(NULL,
2856 kSecGenericPasswordItemClass,
2857 &attrList,
2858 &searchRef)){
2859 if(searchRef){
2860 SecKeychainItemRef itemRef = NULL;
2863 * Go through each item we found and put it
2864 * into our list.
2866 while(!SecKeychainSearchCopyNext(searchRef, &itemRef) && itemRef){
2867 SecKeychainItemDelete(itemRef);
2868 CFRelease(itemRef);
2871 CFRelease(searchRef);
2876 #endif /* APPLEKEYCHAIN */
2878 #ifdef LOCAL_PASSWD_CACHE
2881 * set_passfile_passwd - set the password file entry associated with
2882 * cache. The file is assumed to be in the same directory
2883 * as the pinerc with the name defined above.
2884 * already_prompted: 0 not prompted
2885 * 1 prompted, answered yes
2886 * 2 prompted, answered no
2888 void
2889 set_passfile_passwd(pinerc, passwd, user, hostlist, altflag, already_prompted)
2890 char *pinerc, *passwd, *user;
2891 STRLIST_S *hostlist;
2892 int altflag, already_prompted;
2894 dprint((10, "set_passfile_passwd\n"));
2895 if(((already_prompted == 0 && preserve_prompt(pinerc))
2896 || already_prompted == 1)
2897 && !ps_global->nowrite_password_cache
2898 && (passfile_cache || read_passfile(pinerc, &passfile_cache))){
2899 imap_set_passwd(&passfile_cache, passwd, user, hostlist, altflag, 0, 0);
2900 write_passfile(pinerc, passfile_cache);
2906 * Passfile lines are
2908 * passwd TAB user TAB hostname TAB flags [ TAB orig_hostname ] \n
2910 * In pine4.40 and before there was no orig_hostname.
2911 * This routine attempts to repair that.
2913 void
2914 update_passfile_hostlist(pinerc, user, hostlist, altflag)
2915 char *pinerc;
2916 char *user;
2917 STRLIST_S *hostlist;
2918 int altflag;
2920 #ifdef WINCRED
2921 return;
2922 #else /* !WINCRED */
2923 MMLOGIN_S *l;
2925 for(l = passfile_cache; l; l = l->next)
2926 if(imap_same_host(l->hosts, hostlist)
2927 && *user
2928 && !strcmp(user, l->user)
2929 && l->altflag == altflag){
2930 break;
2933 if(l && l->hosts && hostlist && !l->hosts->next && hostlist->next
2934 && hostlist->next->name
2935 && !ps_global->nowrite_password_cache){
2936 l->hosts->next = new_strlist(hostlist->next->name);
2937 write_passfile(pinerc, passfile_cache);
2939 #endif /* !WINCRED */
2942 #endif /* LOCAL_PASSWD_CACHE */
2945 #if (WINCRED > 0)
2947 * Load and init the WinCred structure.
2948 * This gives us a way to skip the WinCred code
2949 * if the dll doesn't exist.
2952 init_wincred_funcs(void)
2954 if(!g_CredInited)
2956 HMODULE hmod;
2958 /* Assume the worst. */
2959 g_CredInited = -1;
2961 hmod = LoadLibrary(TEXT("advapi32.dll"));
2962 if(hmod)
2964 FARPROC fpCredWriteW;
2965 FARPROC fpCredEnumerateW;
2966 FARPROC fpCredDeleteW;
2967 FARPROC fpCredFree;
2969 fpCredWriteW = GetProcAddress(hmod, "CredWriteW");
2970 fpCredEnumerateW = GetProcAddress(hmod, "CredEnumerateW");
2971 fpCredDeleteW = GetProcAddress(hmod, "CredDeleteW");
2972 fpCredFree = GetProcAddress(hmod, "CredFree");
2974 if(fpCredWriteW && fpCredEnumerateW && fpCredDeleteW && fpCredFree)
2976 g_CredWriteW = (CREDWRITEW *)fpCredWriteW;
2977 g_CredEnumerateW = (CREDENUMERATEW *)fpCredEnumerateW;
2978 g_CredDeleteW = (CREDDELETEW *)fpCredDeleteW;
2979 g_CredFree = (CREDFREE *)fpCredFree;
2981 g_CredInited = 1;
2985 mswin_set_erasecreds_callback(ask_erase_credentials);
2988 return g_CredInited;
2991 #endif /* WINCRED */