* Set default ssl configuration for Homebrew in MAC OSX to
[alpine.git] / alpine / imap.c
blob6db0a0bc126ccbb724fa7f0cec4fe22e6716f0d0
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-2014 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(void);
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 char *passfile_name(char *, char *, size_t);
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 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)
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->ttyo)
1263 return(pine_tcptimeout_noscreen(elapsed, sincelast, host));
1265 suspend_busy_cue();
1268 * Prompt after a minute (since by then things are probably really bad)
1269 * A prompt timeout means "keep trying"...
1271 if(elapsed >= (long)ps_global->tcp_query_timeout){
1272 int clear_inverse;
1274 ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
1275 if((clear_inverse = !InverseState()) != 0)
1276 StartInverse();
1278 Writechar(BELL, 0);
1280 PutLine2(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global), 0,
1281 _("No reply in %s seconds from server %s. Break connection?"),
1282 long2string(elapsed), host);
1283 CleartoEOLN();
1284 fflush(stdout);
1285 flush_input();
1286 ch = read_char(7);
1287 if(ps_global->read_bail || ch == 'y' || ch == 'Y'){
1288 ps_global->read_bail = 0;
1289 ps_global->user_says_cancel = 1;
1290 rv = 0L;
1293 if(clear_inverse)
1294 EndInverse();
1296 ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
1299 if(rv == 1L){ /* just warn 'em something's up */
1300 q_status_message2(SM_ORDER, 0, 0,
1301 _("No reply in %s seconds from server %s. Still Waiting..."),
1302 long2string(elapsed), host);
1303 flush_status_messages(0); /* make sure it's seen */
1306 mark_status_dirty(); /* make sure it get's cleared */
1308 resume_busy_cue((rv == 1) ? 3 : 0);
1309 ps_global->tcptimeout = 0;
1311 return(rv);
1314 QUOTALIST *pine_quotalist_copy (QUOTALIST *pquota)
1316 QUOTALIST *cquota = NULL;
1318 if(pquota){
1319 cquota = mail_newquotalist();
1320 if (pquota->name && *pquota->name)
1321 cquota->name = cpystr(pquota->name);
1322 cquota->usage = pquota->usage;
1323 cquota->limit = pquota->limit;
1324 if (pquota->next)
1325 cquota->next = pine_quotalist_copy(pquota->next);
1327 return cquota;
1331 /* c-client callback to handle quota */
1333 void
1334 pine_parse_quota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
1336 ps_global->quota = pine_quotalist_copy (pquota);
1340 * C-client callback to handle SSL/TLS certificate validation failures
1342 * Returning 0 means error becomes fatal
1343 * Non-zero means certificate problem is ignored and SSL session is
1344 * established
1346 * We remember the answer and won't re-ask for subsequent open attempts to
1347 * the same hostname.
1349 long
1350 pine_sslcertquery(char *reason, char *host, char *cert)
1352 char tmp[500];
1353 char *unknown = "<unknown>";
1354 long rv = 0L;
1355 STRLIST_S hostlist;
1356 int ok_novalidate = 0, warned = 0;
1358 dprint((1, "sslcertificatequery: host=%s reason=%s cert=%s\n",
1359 host ? host : "?", reason ? reason : "?",
1360 cert ? cert : "?"));
1362 hostlist.name = host ? host : "";
1363 hostlist.next = NULL;
1366 * See if we've been asked about this host before.
1368 if(imap_get_ssl(cert_failure_list, &hostlist, &ok_novalidate, &warned)){
1369 /* we were asked before, did we say Yes? */
1370 if(ok_novalidate)
1371 rv++;
1373 if(rv){
1374 dprint((5,
1375 "sslcertificatequery: approved automatically\n"));
1376 return(rv);
1379 dprint((1, "sslcertificatequery: we were asked before and said No, so ask again\n"));
1382 if(ps_global->ttyo){
1383 SCROLL_S sargs;
1384 STORE_S *in_store, *out_store;
1385 gf_io_t pc, gc;
1386 HANDLE_S *handles = NULL;
1387 int the_answer = 'n';
1389 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS)) ||
1390 !(out_store = so_get(CharStar, NULL, EDIT_ACCESS)))
1391 goto try_wantto;
1393 so_puts(in_store, "<HTML><P>");
1394 so_puts(in_store, _("There was a failure validating the SSL/TLS certificate for the server"));
1396 so_puts(in_store, "<P><CENTER>");
1397 so_puts(in_store, host ? host : unknown);
1398 so_puts(in_store, "</CENTER>");
1400 so_puts(in_store, "<P>");
1401 so_puts(in_store, _("The reason for the failure was"));
1403 /* squirrel away details */
1404 if(details_host)
1405 fs_give((void **)&details_host);
1406 if(details_reason)
1407 fs_give((void **)&details_reason);
1408 if(details_cert)
1409 fs_give((void **)&details_cert);
1411 details_host = cpystr(host ? host : unknown);
1412 details_reason = cpystr(reason ? reason : unknown);
1413 details_cert = cpystr(cert ? cert : unknown);
1415 so_puts(in_store, "<P><CENTER>");
1416 snprintf(tmp, sizeof(tmp), "%s (<A HREF=\"X-Alpine-Cert:\">details</A>)",
1417 reason ? reason : unknown);
1418 tmp[sizeof(tmp)-1] = '\0';
1420 so_puts(in_store, tmp);
1421 so_puts(in_store, "</CENTER>");
1423 so_puts(in_store, "<P>");
1424 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."));
1426 so_puts(in_store, "<P>");
1427 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"));
1429 so_puts(in_store, "<P><CENTER>");
1430 so_puts(in_store, "/novalidate-cert");
1431 so_puts(in_store, "</CENTER>");
1433 so_puts(in_store, "<P>");
1434 so_puts(in_store, _("to the name of the folder you attempted to access. In other words, wherever you see the characters"));
1436 so_puts(in_store, "<P><CENTER>");
1437 so_puts(in_store, host ? host : unknown);
1438 so_puts(in_store, "</CENTER>");
1440 so_puts(in_store, "<P>");
1441 so_puts(in_store, _("in your configuration, replace those characters with"));
1443 so_puts(in_store, "<P><CENTER>");
1444 so_puts(in_store, host ? host : unknown);
1445 so_puts(in_store, "/novalidate-cert");
1446 so_puts(in_store, "</CENTER>");
1448 so_puts(in_store, "<P>");
1449 so_puts(in_store, _("Answer \"Yes\" to ignore the warning and continue, \"No\" to cancel the open of this folder."));
1451 so_seek(in_store, 0L, 0);
1452 init_handles(&handles);
1453 gf_filter_init();
1454 gf_link_filter(gf_html2plain,
1455 gf_html2plain_opt(NULL,
1456 ps_global->ttyo->screen_cols, non_messageview_margin(),
1457 &handles, NULL, GFHP_LOCAL_HANDLES));
1458 gf_set_so_readc(&gc, in_store);
1459 gf_set_so_writec(&pc, out_store);
1460 gf_pipe(gc, pc);
1461 gf_clear_so_writec(out_store);
1462 gf_clear_so_readc(in_store);
1464 memset(&sargs, 0, sizeof(SCROLL_S));
1465 sargs.text.handles = handles;
1466 sargs.text.text = so_text(out_store);
1467 sargs.text.src = CharStar;
1468 sargs.text.desc = _("help text");
1469 sargs.bar.title = _("SSL/TLS CERTIFICATE VALIDATION FAILURE");
1470 sargs.proc.tool = answer_cert_failure;
1471 sargs.proc.data.p = (void *)&the_answer;
1472 sargs.keys.menu = &ans_certquery_keymenu;
1473 /* don't want to re-enter c-client */
1474 sargs.quell_newmail = 1;
1475 setbitmap(sargs.keys.bitmap);
1476 sargs.help.text = h_tls_validation_failure;
1477 sargs.help.title = _("HELP FOR CERT VALIDATION FAILURE");
1479 scrolltool(&sargs);
1481 if(the_answer == 'y')
1482 rv++;
1484 ps_global->mangled_screen = 1;
1485 ps_global->painted_body_on_startup = 0;
1486 ps_global->painted_footer_on_startup = 0;
1487 so_give(&in_store);
1488 so_give(&out_store);
1489 free_handles(&handles);
1490 if(details_host)
1491 fs_give((void **)&details_host);
1492 if(details_reason)
1493 fs_give((void **)&details_reason);
1494 if(details_cert)
1495 fs_give((void **)&details_cert);
1497 else{
1499 * If screen hasn't been initialized yet, use want_to.
1501 try_wantto:
1502 memset((void *)tmp, 0, sizeof(tmp));
1503 strncpy(tmp,
1504 reason ? reason : _("SSL/TLS certificate validation failure"),
1505 sizeof(tmp));
1506 tmp[sizeof(tmp)-1] = '\0';
1507 strncat(tmp, _(": Continue anyway "), sizeof(tmp)-strlen(tmp)-1);
1509 if(want_to(tmp, 'n', 'x', NO_HELP, WT_NORM) == 'y')
1510 rv++;
1513 if(rv == 0)
1514 q_status_message1(SM_ORDER, 1, 3, _("Open of %s cancelled"),
1515 host ? host : unknown);
1517 imap_set_passwd(&cert_failure_list, "", "", &hostlist, 0, rv ? 1 : 0, 0);
1519 dprint((5, "sslcertificatequery: %s\n",
1520 rv ? "approved" : "rejected"));
1522 return(rv);
1526 char *
1527 pine_newsrcquery(MAILSTREAM *stream, char *mulname, char *name)
1529 char buf[MAILTMPLEN];
1531 if((can_access(mulname, ACCESS_EXISTS) == 0)
1532 || !(can_access(name, ACCESS_EXISTS) == 0))
1533 return(mulname);
1535 snprintf(buf, sizeof(buf),
1536 _("Rename newsrc \"%s%s\" for use as new host-specific newsrc"),
1537 last_cmpnt(name),
1538 strlen(last_cmpnt(name)) > 15 ? "..." : "");
1539 buf[sizeof(buf)-1] = '\0';
1540 if(want_to(buf, 'n', 'n', NO_HELP, WT_NORM) == 'y')
1541 rename_file(name, mulname);
1542 return(mulname);
1547 url_local_certdetails(char *url)
1549 if(!struncmp(url, "x-alpine-cert:", 14)){
1550 STORE_S *store;
1551 SCROLL_S sargs;
1552 char *folded;
1554 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
1555 q_status_message(SM_ORDER | SM_DING, 7, 10,
1556 _("Error allocating space for details."));
1557 return(0);
1560 so_puts(store, _("Host given by user:\n\n "));
1561 so_puts(store, details_host);
1562 so_puts(store, _("\n\nReason for failure:\n\n "));
1563 so_puts(store, details_reason);
1564 so_puts(store, _("\n\nCertificate being verified:\n\n"));
1565 folded = fold(details_cert, ps_global->ttyo->screen_cols, ps_global->ttyo->screen_cols, " ", " ", FLD_NONE);
1566 so_puts(store, folded);
1567 fs_give((void **)&folded);
1568 so_puts(store, "\n");
1570 memset(&sargs, 0, sizeof(SCROLL_S));
1571 sargs.text.text = so_text(store);
1572 sargs.text.src = CharStar;
1573 sargs.text.desc = _("Details");
1574 sargs.bar.title = _("CERT VALIDATION DETAILS");
1575 sargs.help.text = NO_HELP;
1576 sargs.help.title = NULL;
1577 sargs.quell_newmail = 1;
1578 sargs.help.text = h_tls_failure_details;
1579 sargs.help.title = _("HELP FOR CERT VALIDATION DETAILS");
1581 scrolltool(&sargs);
1583 so_give(&store); /* free resources associated with store */
1584 ps_global->mangled_screen = 1;
1585 return(1);
1588 return(0);
1593 * C-client callback to handle SSL/TLS certificate validation failures
1595 void
1596 pine_sslfailure(char *host, char *reason, long unsigned int flags)
1598 SCROLL_S sargs;
1599 STORE_S *store;
1600 int the_answer = 'n', indent, len, cols;
1601 char buf[500], buf2[500];
1602 char *folded;
1603 char *hst = host ? host : "<unknown>";
1604 char *rsn = reason ? reason : "<unknown>";
1605 char *notls = "/notls";
1606 STRLIST_S hostlist;
1607 int ok_novalidate = 0, warned = 0;
1610 dprint((1, "sslfailure: host=%s reason=%s\n",
1611 hst ? hst : "?",
1612 rsn ? rsn : "?"));
1614 if(flags & NET_SILENT)
1615 return;
1617 hostlist.name = host ? host : "";
1618 hostlist.next = NULL;
1621 * See if we've been told about this host before.
1623 if(imap_get_ssl(cert_failure_list, &hostlist, &ok_novalidate, &warned)){
1624 /* we were told already */
1625 if(warned){
1626 snprintf(buf, sizeof(buf), _("SSL/TLS failure for %s: %s"), hst, rsn);
1627 buf[sizeof(buf)-1] = '\0';
1628 mm_log(buf, ERROR);
1629 return;
1633 cols = ps_global->ttyo ? ps_global->ttyo->screen_cols : 80;
1634 cols--;
1636 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS)))
1637 return;
1639 strncpy(buf, _("There was an SSL/TLS failure for the server"), sizeof(buf));
1640 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1641 so_puts(store, folded);
1642 fs_give((void **)&folded);
1643 so_puts(store, "\n");
1645 if((len=strlen(hst)) <= cols){
1646 if((indent=((cols-len)/2)) > 0)
1647 so_puts(store, repeat_char(indent, SPACE));
1649 so_puts(store, hst);
1650 so_puts(store, "\n");
1652 else{
1653 strncpy(buf, hst, sizeof(buf));
1654 buf[sizeof(buf)-1] = '\0';
1655 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1656 so_puts(store, folded);
1657 fs_give((void **)&folded);
1660 so_puts(store, "\n");
1662 strncpy(buf, _("The reason for the failure was"), sizeof(buf));
1663 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1664 so_puts(store, folded);
1665 fs_give((void **)&folded);
1666 so_puts(store, "\n");
1668 if((len=strlen(rsn)) <= cols){
1669 if((indent=((cols-len)/2)) > 0)
1670 so_puts(store, repeat_char(indent, SPACE));
1672 so_puts(store, rsn);
1673 so_puts(store, "\n");
1675 else{
1676 strncpy(buf, rsn, sizeof(buf));
1677 buf[sizeof(buf)-1] = '\0';
1678 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1679 so_puts(store, folded);
1680 fs_give((void **)&folded);
1683 so_puts(store, "\n");
1685 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));
1686 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1687 so_puts(store, folded);
1688 fs_give((void **)&folded);
1689 so_puts(store, "\n");
1691 if((len=strlen(notls)) <= cols){
1692 if((indent=((cols-len)/2)) > 0)
1693 so_puts(store, repeat_char(indent, SPACE));
1695 so_puts(store, notls);
1696 so_puts(store, "\n");
1698 else{
1699 strncpy(buf, notls, sizeof(buf));
1700 buf[sizeof(buf)-1] = '\0';
1701 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1702 so_puts(store, folded);
1703 fs_give((void **)&folded);
1706 so_puts(store, "\n");
1708 strncpy(buf, _("to the name of the mail server you are attempting to access. In other words, wherever you see the characters"),
1709 sizeof(buf));
1710 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1711 so_puts(store, folded);
1712 fs_give((void **)&folded);
1713 so_puts(store, "\n");
1715 if((len=strlen(hst)) <= cols){
1716 if((indent=((cols-len)/2)) > 0)
1717 so_puts(store, repeat_char(indent, SPACE));
1719 so_puts(store, hst);
1720 so_puts(store, "\n");
1722 else{
1723 strncpy(buf, hst, sizeof(buf));
1724 buf[sizeof(buf)-1] = '\0';
1725 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1726 so_puts(store, folded);
1727 fs_give((void **)&folded);
1730 so_puts(store, "\n");
1732 strncpy(buf, _("in your configuration, replace those characters with"),
1733 sizeof(buf));
1734 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1735 so_puts(store, folded);
1736 fs_give((void **)&folded);
1737 so_puts(store, "\n");
1739 snprintf(buf2, sizeof(buf2), "%s%s", hst, notls);
1740 buf2[sizeof(buf2)-1] = '\0';
1741 if((len=strlen(buf2)) <= cols){
1742 if((indent=((cols-len)/2)) > 0)
1743 so_puts(store, repeat_char(indent, SPACE));
1745 so_puts(store, buf2);
1746 so_puts(store, "\n");
1748 else{
1749 strncpy(buf, buf2, sizeof(buf));
1750 buf[sizeof(buf)-1] = '\0';
1751 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1752 so_puts(store, folded);
1753 fs_give((void **)&folded);
1756 so_puts(store, "\n");
1758 if(ps_global->ttyo){
1759 strncpy(buf, _("Type RETURN to continue."), sizeof(buf));
1760 folded = fold(buf, cols, cols, "", "", FLD_NONE);
1761 so_puts(store, folded);
1762 fs_give((void **)&folded);
1765 memset(&sargs, 0, sizeof(SCROLL_S));
1766 sargs.text.text = so_text(store);
1767 sargs.text.src = CharStar;
1768 sargs.text.desc = _("help text");
1769 sargs.bar.title = _("SSL/TLS FAILURE");
1770 sargs.proc.tool = answer_cert_failure;
1771 sargs.proc.data.p = (void *)&the_answer;
1772 sargs.keys.menu = &ans_certfail_keymenu;
1773 setbitmap(sargs.keys.bitmap);
1774 /* don't want to re-enter c-client */
1775 sargs.quell_newmail = 1;
1776 sargs.help.text = h_tls_failure;
1777 sargs.help.title = _("HELP FOR TLS/SSL FAILURE");
1779 if(ps_global->ttyo)
1780 scrolltool(&sargs);
1781 else{
1782 char **q, **qp;
1783 char *p;
1784 unsigned char c;
1785 int cnt = 0;
1788 * The screen isn't initialized yet, which should mean that this
1789 * is the result of a -p argument. Display_args_err knows how to deal
1790 * with the uninitialized screen, so we mess with the data to get it
1791 * in shape for display_args_err. This is pretty hacky.
1794 so_seek(store, 0L, 0); /* rewind */
1795 /* count the lines */
1796 while(so_readc(&c, store))
1797 if(c == '\n')
1798 cnt++;
1800 qp = q = (char **)fs_get((cnt+1) * sizeof(char *));
1801 memset(q, 0, (cnt+1) * sizeof(char *));
1803 so_seek(store, 0L, 0); /* rewind */
1804 p = buf;
1805 while(so_readc(&c, store)){
1806 if(c == '\n'){
1807 *p = '\0';
1808 *qp++ = cpystr(buf);
1809 p = buf;
1811 else
1812 *p++ = c;
1815 display_args_err(NULL, q, 0);
1816 free_list_array(&q);
1819 ps_global->mangled_screen = 1;
1820 ps_global->painted_body_on_startup = 0;
1821 ps_global->painted_footer_on_startup = 0;
1822 so_give(&store);
1824 imap_set_passwd(&cert_failure_list, "", "", &hostlist, 0, ok_novalidate, 1);
1829 answer_cert_failure(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
1831 int rv = 1;
1833 ps_global->next_screen = SCREEN_FUN_NULL;
1835 switch(cmd){
1836 case MC_YES :
1837 *(int *)(sparms->proc.data.p) = 'y';
1838 break;
1840 case MC_NO :
1841 *(int *)(sparms->proc.data.p) = 'n';
1842 break;
1844 default:
1845 panic("Unexpected command in answer_cert_failure");
1846 break;
1849 return(rv);
1853 /*----------------------------------------------------------------------
1854 This can be used to prevent the flickering of the check_cue char
1855 caused by numerous (5000+) fetches by c-client. Right now, the only
1856 practical use found is newsgroup subsciption.
1858 check_cue_display will check if this global is set, and won't clear
1859 the check_cue_char if set.
1860 ----*/
1861 void
1862 set_read_predicted(int i)
1864 ps_global->read_predicted = i==1;
1865 #ifdef _WINDOWS
1866 if(!i && F_ON(F_SHOW_DELAY_CUE, ps_global))
1867 check_cue_display(" ");
1868 #endif
1872 /*----------------------------------------------------------------------
1873 Exported method to retrieve logged in user name associated with stream
1875 Args: host -- host to find associated login name with.
1877 Result:
1878 ----*/
1879 void *
1880 pine_block_notify(int reason, void *data)
1882 switch(reason){
1883 case BLOCK_SENSITIVE: /* sensitive code, disallow alarms */
1884 break;
1886 case BLOCK_NONSENSITIVE: /* non-sensitive code, allow alarms */
1887 break;
1889 case BLOCK_TCPWRITE: /* blocked on TCP write */
1890 case BLOCK_FILELOCK: /* blocked on file locking */
1891 #ifdef _WINDOWS
1892 if(F_ON(F_SHOW_DELAY_CUE, ps_global))
1893 check_cue_display(">");
1895 mswin_setcursor(MSWIN_CURSOR_BUSY);
1896 #endif
1897 break;
1899 case BLOCK_DNSLOOKUP: /* blocked on DNS lookup */
1900 case BLOCK_TCPOPEN: /* blocked on TCP open */
1901 case BLOCK_TCPREAD: /* blocked on TCP read */
1902 case BLOCK_TCPCLOSE: /* blocked on TCP close */
1903 #ifdef _WINDOWS
1904 if(F_ON(F_SHOW_DELAY_CUE, ps_global))
1905 check_cue_display("<");
1907 mswin_setcursor(MSWIN_CURSOR_BUSY);
1908 #endif
1909 break;
1911 default :
1912 case BLOCK_NONE: /* not blocked */
1913 #ifdef _WINDOWS
1914 if(F_ON(F_SHOW_DELAY_CUE, ps_global))
1915 check_cue_display(" ");
1916 #endif
1917 break;
1921 return(NULL);
1925 void
1926 mm_expunged_current(long unsigned int rawno)
1928 /* expunged something we're viewing? */
1929 if(!ps_global->expunge_in_progress
1930 && (mn_is_cur(ps_global->msgmap, mn_raw2m(ps_global->msgmap, (long) rawno))
1931 && (ps_global->prev_screen == mail_view_screen
1932 || ps_global->prev_screen == attachment_screen))){
1933 ps_global->next_screen = mail_index_screen;
1934 q_status_message(SM_ORDER | SM_DING , 3, 3,
1935 "Message you were viewing is gone!");
1940 #ifdef PASSFILE
1943 * Specific functions to support caching username/passwd/host
1944 * triples on disk for use from one session to the next...
1947 #define FIRSTCH 0x20
1948 #define LASTCH 0x7e
1949 #define TABSZ (LASTCH - FIRSTCH + 1)
1951 static int xlate_key;
1955 * xlate_in() - xlate_in the given character
1957 char
1958 xlate_in(int c)
1960 register int eti;
1962 eti = xlate_key;
1963 if((c >= FIRSTCH) && (c <= LASTCH)){
1964 eti += (c - FIRSTCH);
1965 eti -= (eti >= 2*TABSZ) ? 2*TABSZ : (eti >= TABSZ) ? TABSZ : 0;
1966 return((xlate_key = eti) + FIRSTCH);
1968 else
1969 return(c);
1974 * xlate_out() - xlate_out the given character
1976 char
1977 xlate_out(char c)
1979 register int dti;
1980 register int xch;
1982 if((c >= FIRSTCH) && (c <= LASTCH)){
1983 xch = c - (dti = xlate_key);
1984 xch += (xch < FIRSTCH-TABSZ) ? 2*TABSZ : (xch < FIRSTCH) ? TABSZ : 0;
1985 dti = (xch - FIRSTCH) + dti;
1986 dti -= (dti >= 2*TABSZ) ? 2*TABSZ : (dti >= TABSZ) ? TABSZ : 0;
1987 xlate_key = dti;
1988 return(xch);
1990 else
1991 return(c);
1995 char *
1996 passfile_name(char *pinerc, char *path, size_t len)
1998 struct stat sbuf;
1999 char *p = NULL;
2000 int i, j;
2002 if(!path || !((pinerc && pinerc[0]) || ps_global->passfile))
2003 return(NULL);
2005 if(ps_global->passfile)
2006 strncpy(path, ps_global->passfile, len-1);
2007 else{
2008 if((p = last_cmpnt(pinerc)) && *(p-1) && *(p-1) != PASSFILE[0])
2009 for(i = 0; pinerc < p && i < len; pinerc++, i++)
2010 path[i] = *pinerc;
2011 else
2012 i = 0;
2014 for(j = 0; (i < len) && (path[i] = PASSFILE[j]); i++, j++)
2019 path[len-1] = '\0';
2021 dprint((9, "Looking for passfile \"%s\"\n",
2022 path ? path : "?"));
2024 #if defined(DOS) || defined(OS2)
2025 return((our_stat(path, &sbuf) == 0
2026 && ((sbuf.st_mode & S_IFMT) == S_IFREG))
2027 ? path : NULL);
2028 #else
2029 /* First, make sure it's ours and not sym-linked */
2030 if(our_lstat(path, &sbuf) == 0
2031 && ((sbuf.st_mode & S_IFMT) == S_IFREG)
2032 && sbuf.st_uid == getuid()){
2033 /* if too liberal permissions, fix them */
2034 if((sbuf.st_mode & 077) != 0)
2035 if(our_chmod(path, sbuf.st_mode & ~077) != 0)
2036 return(NULL);
2038 return(path);
2040 else
2041 return(NULL);
2042 #endif
2044 #endif /* PASSFILE */
2047 #ifdef LOCAL_PASSWD_CACHE
2050 int
2051 line_get(char *tmp, size_t len, char **textp)
2053 char *s;
2055 tmp[0] = '\0';
2056 if (*textp == NULL || strlen(*textp) >= len - 1
2057 || (s = strchr(*textp, '\n')) == NULL)
2058 return 0;
2060 *s = '\0';
2061 if(*(s-1) == '\r')
2062 *(s-1) = '\0';
2064 strcpy(tmp, *textp);
2065 strcat(tmp, "\n");
2066 *textp = s+1;
2068 return 1;
2071 * For UNIX:
2072 * Passfile lines are
2074 * passwd TAB user TAB hostname TAB flags [ TAB orig_hostname ] \n
2076 * In pine4.40 and before there was no orig_hostname, and there still isn't
2077 * if it is the same as hostname.
2079 * else for WINDOWS:
2080 * Use Windows credentials. The TargetName of the credential is
2081 * UWash_Alpine_<hostname:port>\tuser\taltflag
2082 * and the blob consists of
2083 * passwd\torighost (if different from host)
2085 * We don't use anything fancy we just copy out all the credentials which
2086 * begin with TNAME and put them into our cache, so we don't lookup based
2087 * on the TargetName or anything like that. That was so we could re-use
2088 * the existing code and so that orighost data could be easily used.
2091 read_passfile(pinerc, l)
2092 char *pinerc;
2093 MMLOGIN_S **l;
2095 #ifdef WINCRED
2096 # if (WINCRED > 0)
2097 LPCTSTR lfilter = TEXT(TNAMESTAR);
2098 DWORD count, k;
2099 PCREDENTIAL *pcred;
2100 char *tmp, *blob, *target = NULL;
2101 char *host, *user, *sflags, *passwd, *orighost;
2102 char *ui[5];
2103 int i, j;
2105 if(using_passfile == 0)
2106 return(using_passfile);
2108 if(!g_CredInited){
2109 if(init_wincred_funcs() != 1){
2110 using_passfile = 0;
2111 return(using_passfile);
2115 dprint((9, "read_passfile\n"));
2117 using_passfile = 1;
2119 if(g_CredEnumerateW(lfilter, 0, &count, &pcred)){
2120 if(pcred){
2121 for(k = 0; k < count; k++){
2123 host = user = sflags = passwd = orighost = NULL;
2124 ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
2126 target = lptstr_to_utf8(pcred[k]->TargetName);
2127 tmp = srchstr(target, TNAME);
2129 if(tmp){
2130 tmp += strlen(TNAME);
2131 for(i = 0, j = 0; tmp[i] && j < 3; j++){
2132 for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
2133 ; /* find end of data */
2135 if(tmp[i])
2136 tmp[i++] = '\0'; /* tie off data */
2139 host = ui[0];
2140 user = ui[1];
2141 sflags = ui[2];
2144 blob = (char *) pcred[k]->CredentialBlob;
2145 if(blob){
2146 for(i = 0, j = 3; blob[i] && j < 5; j++){
2147 for(ui[j] = &blob[i]; blob[i] && blob[i] != '\t'; i++)
2148 ; /* find end of data */
2150 if(blob[i])
2151 blob[i++] = '\0'; /* tie off data */
2154 passwd = ui[3];
2155 orighost = ui[4];
2158 if(passwd && host && user){ /* valid field? */
2159 STRLIST_S hostlist[2];
2160 int flags = sflags ? atoi(sflags) : 0;
2162 hostlist[0].name = host;
2163 if(orighost){
2164 hostlist[0].next = &hostlist[1];
2165 hostlist[1].name = orighost;
2166 hostlist[1].next = NULL;
2168 else{
2169 hostlist[0].next = NULL;
2172 imap_set_passwd(l, passwd, user, hostlist, flags & 0x01, 0, 0);
2175 if(target)
2176 fs_give((void **) &target);
2179 g_CredFree((PVOID) pcred);
2183 return(1);
2185 # else /* old windows */
2186 using_passfile = 0;
2187 return(0);
2188 # endif
2190 #elif APPLEKEYCHAIN
2192 char target[MAILTMPLEN];
2193 char *tmp, *host, *user, *sflags, *passwd, *orighost;
2194 char *ui[5];
2195 int i, j, k, rc;
2196 SecKeychainAttributeList attrList;
2197 SecKeychainSearchRef searchRef = NULL;
2198 SecKeychainAttribute attrs[] = {
2199 { kSecAccountItemAttr, strlen(TNAME), TNAME }
2202 if(using_passfile == 0)
2203 return(using_passfile);
2205 dprint((9, "read_passfile\n"));
2208 /* search for only our items in the keychain */
2209 attrList.count = 1;
2210 attrList.attr = attrs;
2212 using_passfile = 1;
2213 if(!(rc=SecKeychainSearchCreateFromAttributes(NULL,
2214 kSecGenericPasswordItemClass,
2215 &attrList,
2216 &searchRef))){
2217 dprint((10, "read_passfile: SecKeychainSearchCreate succeeded\n"));
2218 if(searchRef){
2219 SecKeychainItemRef itemRef = NULL;
2220 SecKeychainAttributeInfo info;
2221 SecKeychainAttributeList *attrList = NULL;
2222 UInt32 blength = 0;
2223 char *blob = NULL;
2224 char *blobcopy = NULL; /* NULL terminated copy */
2226 UInt32 tags[] = {kSecAccountItemAttr,
2227 kSecServiceItemAttr};
2228 UInt32 formats[] = {0,0};
2230 dprint((10, "read_passfile: searchRef not NULL\n"));
2231 info.count = 2;
2232 info.tag = tags;
2233 info.format = formats;
2236 * Go through each item we found and put it
2237 * into our list.
2239 while(!(rc=SecKeychainSearchCopyNext(searchRef, &itemRef)) && itemRef){
2240 dprint((10, "read_passfile: SecKeychainSearchCopyNext got one\n"));
2241 rc = SecKeychainItemCopyAttributesAndData(itemRef,
2242 &info, NULL,
2243 &attrList,
2244 &blength,
2245 (void **) &blob);
2246 if(rc == 0 && attrList){
2247 dprint((10, "read_passfile: SecKeychainItemCopyAttributesAndData succeeded, count=%d\n", attrList->count));
2249 blobcopy = (char *) fs_get((blength + 1) * sizeof(char));
2250 strncpy(blobcopy, (char *) blob, blength);
2251 blobcopy[blength] = '\0';
2254 * I'm not real clear on how this works. It seems to be
2255 * necessary to combine the attributes from two passes
2256 * (attrList->count == 2) in order to get the full set
2257 * of attributes we inserted into the keychain in the
2258 * first place. So, we reset host...orighost outside of
2259 * the following for loop, not inside.
2261 host = user = sflags = passwd = orighost = NULL;
2262 ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
2264 for(k = 0; k < attrList->count; k++){
2266 if(attrList->attr[k].length){
2267 strncpy(target,
2268 (char *) attrList->attr[k].data,
2269 MIN(attrList->attr[k].length,sizeof(target)));
2270 target[MIN(attrList->attr[k].length,sizeof(target)-1)] = '\0';
2273 tmp = target;
2274 for(i = 0, j = 0; tmp[i] && j < 3; j++){
2275 for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
2276 ; /* find end of data */
2278 if(tmp[i])
2279 tmp[i++] = '\0'; /* tie off data */
2282 if(ui[0])
2283 host = ui[0];
2285 if(ui[1])
2286 user = ui[1];
2288 if(ui[2])
2289 sflags = ui[2];
2291 for(i = 0, j = 3; blobcopy[i] && j < 5; j++){
2292 for(ui[j] = &blobcopy[i]; blobcopy[i] && blobcopy[i] != '\t'; i++)
2293 ; /* find end of data */
2295 if(blobcopy[i])
2296 blobcopy[i++] = '\0'; /* tie off data */
2299 if(ui[3])
2300 passwd = ui[3];
2302 if(ui[4])
2303 orighost = ui[4];
2305 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:""));
2308 if(passwd && host && user){ /* valid field? */
2309 STRLIST_S hostlist[2];
2310 int flags = sflags ? atoi(sflags) : 0;
2312 hostlist[0].name = host;
2313 if(orighost){
2314 hostlist[0].next = &hostlist[1];
2315 hostlist[1].name = orighost;
2316 hostlist[1].next = NULL;
2318 else{
2319 hostlist[0].next = NULL;
2322 imap_set_passwd(l, passwd, user, hostlist, flags & 0x01, 0, 0);
2325 if(blobcopy)
2326 fs_give((void **) & blobcopy);
2328 SecKeychainItemFreeAttributesAndData(attrList, (void *) blob);
2330 else{
2331 using_passfile = 0;
2332 dprint((10, "read_passfile: SecKeychainItemCopyAttributesAndData failed, rc=%d\n", rc));
2335 CFRelease(itemRef);
2336 itemRef = NULL;
2339 CFRelease(searchRef);
2341 else{
2342 using_passfile = 0;
2343 dprint((10, "read_passfile: searchRef NULL\n"));
2346 else{
2347 using_passfile = 0;
2348 dprint((10, "read_passfile: SecKeychainSearchCreateFromAttributes failed, rc=%d\n", rc));
2351 return(using_passfile);
2353 #else /* PASSFILE */
2355 char tmp[MAILTMPLEN], *ui[5];
2356 int i, j, n;
2357 #ifdef SMIME
2358 char tmp2[MAILTMPLEN];
2359 char *text = NULL, *text2 = NULL;
2360 int encrypted = 0;
2361 #endif /* SMIME */
2362 FILE *fp;
2364 if(using_passfile == 0)
2365 return(using_passfile);
2367 dprint((9, "read_passfile\n"));
2369 /* if there's no password to read, bag it!! */
2370 if(!passfile_name(pinerc, tmp, sizeof(tmp)) || !(fp = our_fopen(tmp, "rb"))){
2371 using_passfile = 0;
2372 return(using_passfile);
2375 #ifdef SMIME
2376 smime_init();
2377 if(ps_global->smime->pwdcert == NULL)
2378 setup_pwdcert(&ps_global->smime->pwdcert);
2379 tmp2[0] = '\0';
2380 fgets(tmp2, sizeof(tmp2), fp);
2381 fclose(fp);
2382 if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){
2383 if(encrypt_file((char *)tmp, NULL, (PERSONAL_CERT *)ps_global->smime->pwdcert))
2384 encrypted++;
2386 else
2387 encrypted++;
2390 * if password file is encrypted we attemtp to decrypt. We ask the
2391 * user for the password to unlock the password file. If the user
2392 * enters the password and it unlocks the file, use it and keep saving
2393 * passwords in it. If the user enters the wrong passwords and does
2394 * not unlock it, we will not see that here, but in decrypt_file, so
2395 * the only other possibility is that the user cancels. In that case
2396 * we will see i == -1. In that case, we will let the user attempt
2397 * manual login to the server they want to login, but passwords will
2398 * not be saved so that the password file will not be saved
2399 * unencrypted and rewritten again.
2401 if(encrypted){
2402 text = text2 = decrypt_file((char *)tmp, &i, (PERSONAL_CERT *)ps_global->smime->pwdcert);
2403 switch(i){
2404 case 1 : save_password = 1;
2405 break;
2407 case -1: save_password = 0;
2408 break;
2410 default: break;
2413 else
2414 fp = our_fopen(tmp, "rb"); /* reopen to read data */
2415 #endif /* SMIME */
2417 using_passfile = 1;
2418 #ifdef SMIME
2419 for(n = 0; encrypted ? line_get(tmp, sizeof(tmp), &text2)
2420 : (fgets(tmp, sizeof(tmp), fp) != NULL); n++){
2421 #else /* SMIME */
2422 for(n = 0; fgets(tmp, sizeof(tmp), fp); n++){
2423 #endif /* SMIME */
2424 /*** do any necessary DEcryption here ***/
2425 xlate_key = n;
2426 for(i = 0; tmp[i]; i++)
2427 tmp[i] = xlate_out(tmp[i]);
2429 if(i && tmp[i-1] == '\n')
2430 tmp[i-1] = '\0'; /* blast '\n' */
2432 dprint((10, "read_passfile: %s\n", tmp ? tmp : "?"));
2433 ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
2434 for(i = 0, j = 0; tmp[i] && j < 5; j++){
2435 for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
2436 ; /* find end of data */
2438 if(tmp[i])
2439 tmp[i++] = '\0'; /* tie off data */
2442 dprint((10, "read_passfile: calling imap_set_passwd\n"));
2443 if(ui[0] && ui[1] && ui[2]){ /* valid field? */
2444 STRLIST_S hostlist[2];
2445 int flags = ui[3] ? atoi(ui[3]) : 0;
2447 hostlist[0].name = ui[2];
2448 if(ui[4]){
2449 hostlist[0].next = &hostlist[1];
2450 hostlist[1].name = ui[4];
2451 hostlist[1].next = NULL;
2453 else{
2454 hostlist[0].next = NULL;
2457 imap_set_passwd(l, ui[0], ui[1], hostlist, flags & 0x01, 0, 0);
2461 #ifdef SMIME
2462 if (text) fs_give((void **)&text);
2463 #else /* SMIME */
2464 fclose(fp);
2465 #endif /* SMIME */
2466 return(1);
2467 #endif /* PASSFILE */
2472 void
2473 write_passfile(pinerc, l)
2474 char *pinerc;
2475 MMLOGIN_S *l;
2477 #ifdef WINCRED
2478 # if (WINCRED > 0)
2479 char target[MAILTMPLEN];
2480 char blob[MAILTMPLEN];
2481 CREDENTIAL cred;
2482 LPTSTR ltarget = 0;
2484 if(using_passfile == 0)
2485 return;
2487 dprint((9, "write_passfile\n"));
2489 for(; l; l = l->next){
2490 snprintf(target, sizeof(target), "%s%s\t%s\t%d",
2491 TNAME,
2492 (l->hosts && l->hosts->name) ? l->hosts->name : "",
2493 l->user ? l->user : "",
2494 l->altflag);
2495 ltarget = utf8_to_lptstr((LPSTR) target);
2497 if(ltarget){
2498 snprintf(blob, sizeof(blob), "%s%s%s",
2499 l->passwd ? l->passwd : "",
2500 (l->hosts && l->hosts->next && l->hosts->next->name)
2501 ? "\t" : "",
2502 (l->hosts && l->hosts->next && l->hosts->next->name)
2503 ? l->hosts->next->name : "");
2504 memset((void *) &cred, 0, sizeof(cred));
2505 cred.Flags = 0;
2506 cred.Type = CRED_TYPE_GENERIC;
2507 cred.TargetName = ltarget;
2508 cred.CredentialBlobSize = strlen(blob)+1;
2509 cred.CredentialBlob = (LPBYTE) &blob;
2510 cred.Persist = CRED_PERSIST_ENTERPRISE;
2511 g_CredWriteW(&cred, 0);
2513 fs_give((void **) &ltarget);
2516 #endif /* WINCRED > 0 */
2518 #elif APPLEKEYCHAIN
2520 int rc;
2521 char target[MAILTMPLEN];
2522 char blob[MAILTMPLEN];
2523 SecKeychainItemRef itemRef = NULL;
2525 if(using_passfile == 0)
2526 return;
2528 dprint((9, "write_passfile\n"));
2530 for(; l; l = l->next){
2531 snprintf(target, sizeof(target), "%s\t%s\t%d",
2532 (l->hosts && l->hosts->name) ? l->hosts->name : "",
2533 l->user ? l->user : "",
2534 l->altflag);
2536 snprintf(blob, sizeof(blob), "%s%s%s",
2537 l->passwd ? l->passwd : "",
2538 (l->hosts && l->hosts->next && l->hosts->next->name)
2539 ? "\t" : "",
2540 (l->hosts && l->hosts->next && l->hosts->next->name)
2541 ? l->hosts->next->name : "");
2543 dprint((10, "write_passfile: SecKeychainAddGenericPassword(NULL, %d, %s, %d, %s, %d, %s, NULL)\n", strlen(target), target, strlen(TNAME), TNAME, strlen(blob), blob));
2545 rc = SecKeychainAddGenericPassword(NULL,
2546 strlen(target), target,
2547 strlen(TNAME), TNAME,
2548 strlen(blob), blob,
2549 NULL);
2550 if(rc==0){
2551 dprint((10, "write_passfile: SecKeychainAddGenericPassword succeeded\n"));
2553 else{
2554 dprint((10, "write_passfile: SecKeychainAddGenericPassword returned rc=%d\n", rc));
2557 if(rc == errSecDuplicateItem){
2558 /* fix existing entry */
2559 dprint((10, "write_passfile: SecKeychainAddGenericPassword found existing entry\n"));
2560 itemRef = NULL;
2561 if(!(rc=SecKeychainFindGenericPassword(NULL,
2562 strlen(target), target,
2563 strlen(TNAME), TNAME,
2564 NULL, NULL,
2565 &itemRef)) && itemRef){
2567 rc = SecKeychainItemModifyAttributesAndData(itemRef, NULL, strlen(blob), blob);
2568 if(!rc){
2569 dprint((10, "write_passfile: SecKeychainItemModifyAttributesAndData returned rc=%d\n", rc));
2572 else{
2573 dprint((10, "write_passfile: SecKeychainFindGenericPassword returned rc=%d\n", rc));
2578 #else /* PASSFILE */
2580 char tmp[MAILTMPLEN];
2581 int i, n;
2582 FILE *fp;
2583 #ifdef SMIME
2584 char *text = NULL, tmp2[MAILTMPLEN];
2585 int len = 0;
2586 #endif
2588 if(using_passfile == 0)
2589 return;
2591 dprint((9, "write_passfile\n"));
2593 /* if there's no passfile to read, bag it!! */
2594 if(!passfile_name(pinerc, tmp, sizeof(tmp)) || !(fp = our_fopen(tmp, "wb"))){
2595 using_passfile = 0;
2596 return;
2599 #ifdef SMIME
2600 strncpy(tmp2, tmp, sizeof(tmp2)-1);
2601 tmp2[sizeof(tmp2)-1] = '\0';
2602 #endif /* SMIME */
2604 for(n = 0; l; l = l->next, n++){
2605 /*** do any necessary ENcryption here ***/
2606 snprintf(tmp, sizeof(tmp), "%s\t%s\t%s\t%d%s%s\n", l->passwd, l->user,
2607 l->hosts->name, l->altflag,
2608 (l->hosts->next && l->hosts->next->name) ? "\t" : "",
2609 (l->hosts->next && l->hosts->next->name) ? l->hosts->next->name
2610 : "");
2611 dprint((10, "write_passfile: %s", tmp ? tmp : "?"));
2612 xlate_key = n;
2613 for(i = 0; tmp[i]; i++)
2614 tmp[i] = xlate_in(tmp[i]);
2616 #ifdef SMIME
2617 if(len == 0){
2618 len = strlen(tmp) + 1;
2619 text = fs_get(len*sizeof(char));
2620 *text = '\0';
2622 if(strlen(text) + strlen(tmp) > len){
2623 len = strlen(text) + strlen(tmp) + 1;
2624 fs_resize((void **)&text, len*sizeof(char));
2626 strcat(text, tmp);
2627 #else /* SMIME */
2628 fputs(tmp, fp);
2629 #endif /* SMIME */
2632 fclose(fp);
2633 #ifdef SMIME
2634 if(encrypt_file((char *)tmp2, text, (PERSONAL_CERT *) ps_global->smime->pwdcert) == 0){
2635 if((fp = our_fopen(tmp2, "wb")) != NULL){
2636 fputs(text, fp);
2637 fclose(fp);
2640 fs_give((void **)&text);
2641 #endif /* SMIME */
2642 #endif /* PASSFILE */
2645 #endif /* LOCAL_PASSWD_CACHE */
2648 #if (WINCRED > 0)
2649 void
2650 erase_windows_credentials(void)
2652 LPCTSTR lfilter = TEXT(TNAMESTAR);
2653 DWORD count, k;
2654 PCREDENTIAL *pcred;
2656 if(!g_CredInited){
2657 if(init_wincred_funcs() != 1)
2658 return;
2661 if(g_CredEnumerateW(lfilter, 0, &count, &pcred)){
2662 if(pcred){
2663 for(k = 0; k < count; k++)
2664 g_CredDeleteW(pcred[k]->TargetName, CRED_TYPE_GENERIC, 0);
2666 g_CredFree((PVOID) pcred);
2671 void
2672 ask_erase_credentials(void)
2674 if(want_to(_("Erase previously preserved passwords"), 'y', 'x', NO_HELP, WT_NORM) == 'y'){
2675 erase_windows_credentials();
2676 q_status_message(SM_ORDER, 3, 3, "All preserved passwords have been erased");
2678 else
2679 q_status_message(SM_ORDER, 3, 3, "Previously preserved passwords will not be erased");
2682 #endif /* WINCRED */
2685 #ifdef LOCAL_PASSWD_CACHE
2688 * get_passfile_passwd - return the password contained in the special passord
2689 * cache. The file is assumed to be in the same directory
2690 * as the pinerc with the name defined above.
2693 get_passfile_passwd(pinerc, passwd, user, hostlist, altflag)
2694 char *pinerc, *passwd, *user;
2695 STRLIST_S *hostlist;
2696 int altflag;
2698 dprint((10, "get_passfile_passwd\n"));
2699 return((passfile_cache || read_passfile(pinerc, &passfile_cache))
2700 ? imap_get_passwd(passfile_cache, passwd,
2701 user, hostlist, altflag)
2702 : 0);
2706 is_using_passfile()
2708 return(using_passfile == 1);
2712 * Just trying to guess the username the user might want to use on this
2713 * host, the user will confirm.
2715 char *
2716 get_passfile_user(pinerc, hostlist)
2717 char *pinerc;
2718 STRLIST_S *hostlist;
2720 return((passfile_cache || read_passfile(pinerc, &passfile_cache))
2721 ? imap_get_user(passfile_cache, hostlist)
2722 : NULL);
2727 preserve_prompt(void)
2729 #ifdef WINCRED
2730 # if (WINCRED > 0)
2732 * This prompt was going to be able to be turned on and off via a registry
2733 * setting controlled from the config menu. We decided to always use the
2734 * dialog for login, and there the prompt is unobtrusive enough to always
2735 * be in there. As a result, windows should never reach this, but now
2736 * OS X somewhat uses the behavior just described.
2738 if(mswin_store_pass_prompt()
2739 && (want_to(_("Preserve password for next login"),
2740 'y', 'x', NO_HELP, WT_NORM)
2741 == 'y'))
2742 return(1);
2743 else
2744 return(0);
2745 # else
2746 return(0);
2747 # endif
2749 #elif APPLEKEYCHAIN
2751 int rc;
2752 if(rc = macos_store_pass_prompt()){
2753 if(want_to(_("Preserve password for next login"),
2754 'y', 'x', NO_HELP, WT_NORM)
2755 == 'y'){
2756 if(rc == -1){
2757 macos_set_store_pass_prompt(1);
2758 q_status_message(SM_ORDER, 4, 4,
2759 _("Stop \"Preserve passwords?\" prompts by deleting Alpine Keychain entry"));
2761 return(1);
2763 else if(rc == -1){
2764 macos_set_store_pass_prompt(0);
2765 q_status_message(SM_ORDER, 4, 4,
2766 _("Restart \"Preserve passwords?\" prompts by deleting Alpine Keychain entry"));
2768 return(0);
2770 return(0);
2771 #else /* PASSFILE */
2772 return(want_to(_("Preserve password on DISK for next login"),
2773 'y', 'x', NO_HELP, WT_NORM)
2774 == 'y');
2775 #endif /* PASSFILE */
2778 #endif /* LOCAL_PASSWD_CACHE */
2781 #ifdef APPLEKEYCHAIN
2784 * Returns:
2785 * 1 if store pass prompt is set in the "registry" to on
2786 * 0 if set to off
2787 * -1 if not set to anything
2790 macos_store_pass_prompt(void)
2792 char *data = NULL;
2793 UInt32 len = 0;
2794 int rc = -1;
2795 int val;
2797 if(storepassprompt == -1){
2798 if(!(rc=SecKeychainFindGenericPassword(NULL, 0, NULL,
2799 strlen(TNAMEPROMPT),
2800 TNAMEPROMPT, &len,
2801 (void **) &data, NULL))){
2802 val = (len == 1 && data && data[0] == '1');
2806 if(storepassprompt == -1 && !rc){
2807 if(val)
2808 storepassprompt = 1;
2809 else
2810 storepassprompt = 0;
2813 return(storepassprompt);
2817 void
2818 macos_set_store_pass_prompt(int val)
2820 storepassprompt = val ? 1 : 0;
2822 SecKeychainAddGenericPassword(NULL, 0, NULL, strlen(TNAMEPROMPT),
2823 TNAMEPROMPT, 1, val ? "1" : "0", NULL);
2827 void
2828 macos_erase_keychain(void)
2830 SecKeychainAttributeList attrList;
2831 SecKeychainSearchRef searchRef = NULL;
2832 SecKeychainAttribute attrs1[] = {
2833 { kSecAccountItemAttr, strlen(TNAME), TNAME }
2835 SecKeychainAttribute attrs2[] = {
2836 { kSecAccountItemAttr, strlen(TNAMEPROMPT), TNAMEPROMPT }
2839 dprint((9, "macos_erase_keychain\n"));
2842 * Seems like we ought to be able to combine attrs1 and attrs2
2843 * into a single array, but I couldn't get it to work.
2846 /* search for only our items in the keychain */
2847 attrList.count = 1;
2848 attrList.attr = attrs1;
2850 if(!SecKeychainSearchCreateFromAttributes(NULL,
2851 kSecGenericPasswordItemClass,
2852 &attrList,
2853 &searchRef)){
2854 if(searchRef){
2855 SecKeychainItemRef itemRef = NULL;
2858 * Go through each item we found and put it
2859 * into our list.
2861 while(!SecKeychainSearchCopyNext(searchRef, &itemRef) && itemRef){
2862 dprint((10, "read_passfile: SecKeychainSearchCopyNext got one\n"));
2863 SecKeychainItemDelete(itemRef);
2864 CFRelease(itemRef);
2867 CFRelease(searchRef);
2871 attrList.count = 1;
2872 attrList.attr = attrs2;
2874 if(!SecKeychainSearchCreateFromAttributes(NULL,
2875 kSecGenericPasswordItemClass,
2876 &attrList,
2877 &searchRef)){
2878 if(searchRef){
2879 SecKeychainItemRef itemRef = NULL;
2882 * Go through each item we found and put it
2883 * into our list.
2885 while(!SecKeychainSearchCopyNext(searchRef, &itemRef) && itemRef){
2886 SecKeychainItemDelete(itemRef);
2887 CFRelease(itemRef);
2890 CFRelease(searchRef);
2895 #endif /* APPLEKEYCHAIN */
2897 #ifdef LOCAL_PASSWD_CACHE
2900 * set_passfile_passwd - set the password file entry associated with
2901 * cache. The file is assumed to be in the same directory
2902 * as the pinerc with the name defined above.
2903 * already_prompted: 0 not prompted
2904 * 1 prompted, answered yes
2905 * 2 prompted, answered no
2907 void
2908 set_passfile_passwd(pinerc, passwd, user, hostlist, altflag, already_prompted)
2909 char *pinerc, *passwd, *user;
2910 STRLIST_S *hostlist;
2911 int altflag, already_prompted;
2913 dprint((10, "set_passfile_passwd\n"));
2914 if((passfile_cache || read_passfile(pinerc, &passfile_cache))
2915 && !ps_global->nowrite_password_cache
2916 && ((already_prompted == 0 && preserve_prompt())
2917 || already_prompted == 1)){
2918 imap_set_passwd(&passfile_cache, passwd, user, hostlist, altflag, 0, 0);
2919 write_passfile(pinerc, passfile_cache);
2925 * Passfile lines are
2927 * passwd TAB user TAB hostname TAB flags [ TAB orig_hostname ] \n
2929 * In pine4.40 and before there was no orig_hostname.
2930 * This routine attempts to repair that.
2932 void
2933 update_passfile_hostlist(pinerc, user, hostlist, altflag)
2934 char *pinerc;
2935 char *user;
2936 STRLIST_S *hostlist;
2937 int altflag;
2939 #ifdef WINCRED
2940 return;
2941 #else /* !WINCRED */
2942 MMLOGIN_S *l;
2943 STRLIST_S *h1, *h2;
2945 for(l = passfile_cache; l; l = l->next)
2946 if(imap_same_host(l->hosts, hostlist)
2947 && *user
2948 && !strcmp(user, l->user)
2949 && l->altflag == altflag){
2950 break;
2953 if(l && l->hosts && hostlist && !l->hosts->next && hostlist->next
2954 && hostlist->next->name
2955 && !ps_global->nowrite_password_cache){
2956 l->hosts->next = new_strlist(hostlist->next->name);
2957 write_passfile(pinerc, passfile_cache);
2959 #endif /* !WINCRED */
2962 #endif /* LOCAL_PASSWD_CACHE */
2965 #if (WINCRED > 0)
2967 * Load and init the WinCred structure.
2968 * This gives us a way to skip the WinCred code
2969 * if the dll doesn't exist.
2972 init_wincred_funcs(void)
2974 if(!g_CredInited)
2976 HMODULE hmod;
2978 /* Assume the worst. */
2979 g_CredInited = -1;
2981 hmod = LoadLibrary(TEXT("advapi32.dll"));
2982 if(hmod)
2984 FARPROC fpCredWriteW;
2985 FARPROC fpCredEnumerateW;
2986 FARPROC fpCredDeleteW;
2987 FARPROC fpCredFree;
2989 fpCredWriteW = GetProcAddress(hmod, "CredWriteW");
2990 fpCredEnumerateW = GetProcAddress(hmod, "CredEnumerateW");
2991 fpCredDeleteW = GetProcAddress(hmod, "CredDeleteW");
2992 fpCredFree = GetProcAddress(hmod, "CredFree");
2994 if(fpCredWriteW && fpCredEnumerateW && fpCredDeleteW && fpCredFree)
2996 g_CredWriteW = (CREDWRITEW *)fpCredWriteW;
2997 g_CredEnumerateW = (CREDENUMERATEW *)fpCredEnumerateW;
2998 g_CredDeleteW = (CREDDELETEW *)fpCredDeleteW;
2999 g_CredFree = (CREDFREE *)fpCredFree;
3001 g_CredInited = 1;
3005 mswin_set_erasecreds_callback(ask_erase_credentials);
3008 return g_CredInited;
3011 #endif /* WINCRED */