* Update to packages/alpine.spec to account for the new location of man
[alpine.git] / alpine / imap.c
blob495743e30bda22b50aaeb170b7d09c18d6d6fcbe
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-2017 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, rv = 0;
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.
2336 * Remark: setupwdcert will produce a null ps_global->pwdcert only when
2337 * it is called for the first time and there are certificates at all,
2338 * or when it is called after the first time and the user refuses to
2339 * create a self-signed certificate. In this situation we will just
2340 * let the user live in an insecure world, but no more passwords will
2341 * be saved in the password file, and only those found there will be used.
2343 tmp2[0] = '\0';
2344 fgets(tmp2, sizeof(tmp2), fp);
2345 fclose(fp);
2346 if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){
2347 /* there is an already existing password file, that is not encrypted
2348 * and there is no key to encrypt it yet, go again through setup_pwdcert
2349 * and encrypt it now.
2351 if(tmp2[0]){ /* not empty, UNencrypted password file */
2352 if(ps_global->pwdcert == NULL)
2353 rv = setup_pwdcert(&ps_global->pwdcert);
2354 if((rv == 0 || rv == -5) && ps_global->pwdcert == NULL)
2355 ps_global->pwdcert = (void *) ALPINE_self_signed_certificate(NULL, 0, ps_global->pwdcertdir, MASTERNAME);
2356 if(ps_global->pwdcert == NULL){
2357 q_status_message(SM_ORDER, 3, 3,
2358 " Failed to create private key. Using UNencrypted Password file. ");
2359 save_password = 0;
2361 else{
2362 if(rv == 1){
2363 q_status_message(SM_ORDER, 3, 3,
2364 " Failed to unlock private key. Using UNencrypted Password file. ");
2365 save_password = 0; /* do not save more passwords */
2368 if(ps_global->pwdcert != NULL
2369 && encrypt_file((char *)tmp, NULL, (PERSONAL_CERT *)ps_global->pwdcert))
2370 encrypted++;
2373 else {
2374 if(ps_global->pwdcert == NULL)
2375 rv = setup_pwdcert(&ps_global->pwdcert);
2376 encrypted++;
2380 * if password file is encrypted we attemtp to decrypt. We ask the
2381 * user for the password to unlock the password file. If the user
2382 * enters the password and it unlocks the file, use it and keep saving
2383 * passwords in it. If the user enters the wrong passwords and does
2384 * not unlock it, we will not see that here, but in decrypt_file, so
2385 * the only other possibility is that the user cancels. In that case
2386 * we will see i == -1. In that case, we will let the user attempt
2387 * manual login to the server they want to login, but passwords will
2388 * not be saved so that the password file will not be saved
2389 * unencrypted and rewritten again.
2391 if(encrypted){
2392 text = text2 = decrypt_file((char *)tmp, &i, (PERSONAL_CERT *)ps_global->pwdcert);
2393 switch(i){
2394 case -2: using_passfile = 0;
2395 break;
2397 case 1 : save_password = 1;
2398 using_passfile = 1;
2399 break;
2401 case -1: save_password = 0;
2402 using_passfile = 1;
2403 break;
2405 default: break;
2408 else
2409 fp = our_fopen(tmp, "rb"); /* reopen to read data */
2410 #endif /* SMIME */
2412 if(using_passfile == 0){
2413 #ifdef SMIME
2414 if(text) fs_give((void **)&text);
2415 #endif /* SMIME */
2416 return using_passfile;
2419 #ifdef SMIME
2420 for(n = 0; encrypted ? line_get(tmp, sizeof(tmp), &text2)
2421 : (fgets(tmp, sizeof(tmp), fp) != NULL); n++){
2422 #else /* SMIME */
2423 for(n = 0; fgets(tmp, sizeof(tmp), fp); n++){
2424 #endif /* SMIME */
2425 /*** do any necessary DEcryption here ***/
2426 xlate_key = n;
2427 for(i = 0; tmp[i]; i++)
2428 tmp[i] = xlate_out(tmp[i]);
2430 if(i && tmp[i-1] == '\n')
2431 tmp[i-1] = '\0'; /* blast '\n' */
2433 dprint((10, "read_passfile: %s\n", tmp ? tmp : "?"));
2434 ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
2435 for(i = 0, j = 0; tmp[i] && j < 5; j++){
2436 for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
2437 ; /* find end of data */
2439 if(tmp[i])
2440 tmp[i++] = '\0'; /* tie off data */
2443 dprint((10, "read_passfile: calling imap_set_passwd\n"));
2444 if(ui[0] && ui[1] && ui[2]){ /* valid field? */
2445 STRLIST_S hostlist[2];
2446 int flags = ui[3] ? atoi(ui[3]) : 0;
2448 hostlist[0].name = ui[2];
2449 if(ui[4]){
2450 hostlist[0].next = &hostlist[1];
2451 hostlist[1].name = ui[4];
2452 hostlist[1].next = NULL;
2454 else{
2455 hostlist[0].next = NULL;
2458 imap_set_passwd(l, ui[0], ui[1], hostlist, flags & 0x01, 0, 0);
2462 #ifdef SMIME
2463 if (text) fs_give((void **)&text);
2464 #else /* SMIME */
2465 fclose(fp);
2466 #endif /* SMIME */
2467 return(1);
2468 #endif /* PASSFILE */
2473 void
2474 write_passfile(pinerc, l)
2475 char *pinerc;
2476 MMLOGIN_S *l;
2478 #ifdef WINCRED
2479 # if (WINCRED > 0)
2480 char target[MAILTMPLEN];
2481 char blob[MAILTMPLEN];
2482 CREDENTIAL cred;
2483 LPTSTR ltarget = 0;
2485 if(using_passfile == 0)
2486 return;
2488 dprint((9, "write_passfile\n"));
2490 for(; l; l = l->next){
2491 snprintf(target, sizeof(target), "%s%s\t%s\t%d",
2492 TNAME,
2493 (l->hosts && l->hosts->name) ? l->hosts->name : "",
2494 l->user ? l->user : "",
2495 l->altflag);
2496 ltarget = utf8_to_lptstr((LPSTR) target);
2498 if(ltarget){
2499 snprintf(blob, sizeof(blob), "%s%s%s",
2500 l->passwd ? l->passwd : "",
2501 (l->hosts && l->hosts->next && l->hosts->next->name)
2502 ? "\t" : "",
2503 (l->hosts && l->hosts->next && l->hosts->next->name)
2504 ? l->hosts->next->name : "");
2505 memset((void *) &cred, 0, sizeof(cred));
2506 cred.Flags = 0;
2507 cred.Type = CRED_TYPE_GENERIC;
2508 cred.TargetName = ltarget;
2509 cred.CredentialBlobSize = strlen(blob)+1;
2510 cred.CredentialBlob = (LPBYTE) &blob;
2511 cred.Persist = CRED_PERSIST_ENTERPRISE;
2512 g_CredWriteW(&cred, 0);
2514 fs_give((void **) &ltarget);
2517 #endif /* WINCRED > 0 */
2519 #elif APPLEKEYCHAIN
2521 int rc;
2522 char target[MAILTMPLEN];
2523 char blob[MAILTMPLEN];
2524 SecKeychainItemRef itemRef = NULL;
2526 if(using_passfile == 0)
2527 return;
2529 dprint((9, "write_passfile\n"));
2531 for(; l; l = l->next){
2532 snprintf(target, sizeof(target), "%s\t%s\t%d",
2533 (l->hosts && l->hosts->name) ? l->hosts->name : "",
2534 l->user ? l->user : "",
2535 l->altflag);
2537 snprintf(blob, sizeof(blob), "%s%s%s",
2538 l->passwd ? l->passwd : "",
2539 (l->hosts && l->hosts->next && l->hosts->next->name)
2540 ? "\t" : "",
2541 (l->hosts && l->hosts->next && l->hosts->next->name)
2542 ? l->hosts->next->name : "");
2544 dprint((10, "write_passfile: SecKeychainAddGenericPassword(NULL, %d, %s, %d, %s, %d, %s, NULL)\n", strlen(target), target, strlen(TNAME), TNAME, strlen(blob), blob));
2546 rc = SecKeychainAddGenericPassword(NULL,
2547 strlen(target), target,
2548 strlen(TNAME), TNAME,
2549 strlen(blob), blob,
2550 NULL);
2551 if(rc==0){
2552 dprint((10, "write_passfile: SecKeychainAddGenericPassword succeeded\n"));
2554 else{
2555 dprint((10, "write_passfile: SecKeychainAddGenericPassword returned rc=%d\n", rc));
2558 if(rc == errSecDuplicateItem){
2559 /* fix existing entry */
2560 dprint((10, "write_passfile: SecKeychainAddGenericPassword found existing entry\n"));
2561 itemRef = NULL;
2562 if(!(rc=SecKeychainFindGenericPassword(NULL,
2563 strlen(target), target,
2564 strlen(TNAME), TNAME,
2565 NULL, NULL,
2566 &itemRef)) && itemRef){
2568 rc = SecKeychainItemModifyAttributesAndData(itemRef, NULL, strlen(blob), blob);
2569 if(!rc){
2570 dprint((10, "write_passfile: SecKeychainItemModifyAttributesAndData returned rc=%d\n", rc));
2573 else{
2574 dprint((10, "write_passfile: SecKeychainFindGenericPassword returned rc=%d\n", rc));
2579 #else /* PASSFILE */
2581 char tmp[MAILTMPLEN];
2582 int i, n;
2583 FILE *fp;
2584 #ifdef SMIME
2585 char *text = NULL, tmp2[MAILTMPLEN];
2586 int len = 0;
2587 #endif
2589 if(using_passfile == 0)
2590 return;
2592 dprint((9, "write_passfile\n"));
2594 /* if there's no passfile to read, bag it!! */
2595 if(!passfile_name(pinerc, tmp, sizeof(tmp)) || !(fp = our_fopen(tmp, "wb"))){
2596 using_passfile = 0;
2597 return;
2600 #ifdef SMIME
2601 strncpy(tmp2, tmp, sizeof(tmp2));
2602 tmp2[sizeof(tmp2)-1] = '\0';
2603 #endif /* SMIME */
2605 for(n = 0; l; l = l->next, n++){
2606 /*** do any necessary ENcryption here ***/
2607 snprintf(tmp, sizeof(tmp), "%s\t%s\t%s\t%d%s%s\n", l->passwd, l->user,
2608 l->hosts->name, l->altflag,
2609 (l->hosts->next && l->hosts->next->name) ? "\t" : "",
2610 (l->hosts->next && l->hosts->next->name) ? l->hosts->next->name
2611 : "");
2612 dprint((10, "write_passfile: %s", tmp ? tmp : "?"));
2613 xlate_key = n;
2614 for(i = 0; tmp[i]; i++)
2615 tmp[i] = xlate_in(tmp[i]);
2617 #ifdef SMIME
2618 if(len == 0){
2619 len = strlen(tmp) + 1;
2620 text = fs_get(len*sizeof(char));
2621 *text = '\0';
2623 if(strlen(text) + strlen(tmp) > len){
2624 len = strlen(text) + strlen(tmp) + 1;
2625 fs_resize((void **)&text, len*sizeof(char));
2627 strncat(text, tmp, strlen(tmp));
2628 #else /* SMIME */
2629 fputs(tmp, fp);
2630 #endif /* SMIME */
2633 fclose(fp);
2634 #ifdef SMIME
2635 if(text != NULL){
2636 if(ps_global->pwdcert == NULL){
2637 q_status_message(SM_ORDER, 3, 3, "Attempting to encrypt password file");
2638 i = setup_pwdcert(&ps_global->pwdcert);
2639 if(i == 0 && ps_global->pwdcert == NULL)
2640 ps_global->pwdcert = (void *) ALPINE_self_signed_certificate(NULL, 0, ps_global->pwdcertdir, MASTERNAME);
2642 if(ps_global->pwdcert == NULL){ /* we tried but failed */
2643 if(i == -1)
2644 q_status_message1(SM_ORDER, 3, 3, "Error: no directory %s to save certificates", ps_global->pwdcertdir);
2645 else
2646 q_status_message(SM_ORDER, 3, 3, "Refusing to write non-encrypted password file");
2648 else if(encrypt_file((char *)tmp2, text, ps_global->pwdcert) == 0)
2649 q_status_message(SM_ORDER, 3, 3, "Failed to encrypt password file");
2650 fs_give((void **)&text); /* do not save this text */
2652 #endif /* SMIME */
2653 #endif /* PASSFILE */
2656 #endif /* LOCAL_PASSWD_CACHE */
2659 #if (WINCRED > 0)
2660 void
2661 erase_windows_credentials(void)
2663 LPCTSTR lfilter = TEXT(TNAMESTAR);
2664 DWORD count, k;
2665 PCREDENTIAL *pcred;
2667 if(!g_CredInited){
2668 if(init_wincred_funcs() != 1)
2669 return;
2672 if(g_CredEnumerateW(lfilter, 0, &count, &pcred)){
2673 if(pcred){
2674 for(k = 0; k < count; k++)
2675 g_CredDeleteW(pcred[k]->TargetName, CRED_TYPE_GENERIC, 0);
2677 g_CredFree((PVOID) pcred);
2682 void
2683 ask_erase_credentials(void)
2685 if(want_to(_("Erase previously preserved passwords"), 'y', 'x', NO_HELP, WT_NORM) == 'y'){
2686 erase_windows_credentials();
2687 q_status_message(SM_ORDER, 3, 3, "All preserved passwords have been erased");
2689 else
2690 q_status_message(SM_ORDER, 3, 3, "Previously preserved passwords will not be erased");
2693 #endif /* WINCRED */
2696 #ifdef LOCAL_PASSWD_CACHE
2699 * get_passfile_passwd - return the password contained in the special passord
2700 * cache. The file is assumed to be in the same directory
2701 * as the pinerc with the name defined above.
2704 get_passfile_passwd(pinerc, passwd, user, hostlist, altflag)
2705 char *pinerc, *passwd, *user;
2706 STRLIST_S *hostlist;
2707 int altflag;
2709 dprint((10, "get_passfile_passwd\n"));
2710 return((passfile_cache || read_passfile(pinerc, &passfile_cache))
2711 ? imap_get_passwd(passfile_cache, passwd,
2712 user, hostlist, altflag)
2713 : 0);
2717 is_using_passfile()
2719 return(using_passfile == 1);
2723 * Just trying to guess the username the user might want to use on this
2724 * host, the user will confirm.
2726 char *
2727 get_passfile_user(pinerc, hostlist)
2728 char *pinerc;
2729 STRLIST_S *hostlist;
2731 return((passfile_cache || read_passfile(pinerc, &passfile_cache))
2732 ? imap_get_user(passfile_cache, hostlist)
2733 : NULL);
2738 preserve_prompt(char *pinerc)
2740 #ifdef WINCRED
2741 # if (WINCRED > 0)
2743 * This prompt was going to be able to be turned on and off via a registry
2744 * setting controlled from the config menu. We decided to always use the
2745 * dialog for login, and there the prompt is unobtrusive enough to always
2746 * be in there. As a result, windows should never reach this, but now
2747 * OS X somewhat uses the behavior just described.
2749 if(mswin_store_pass_prompt()
2750 && (want_to(_("Preserve password for next login"),
2751 'y', 'x', NO_HELP, WT_NORM)
2752 == 'y'))
2753 return(1);
2754 else
2755 return(0);
2756 # else
2757 return(0);
2758 # endif
2760 #elif APPLEKEYCHAIN
2762 int rc;
2763 if((rc = macos_store_pass_prompt()) != 0){
2764 if(want_to(_("Preserve password for next login"),
2765 'y', 'x', NO_HELP, WT_NORM)
2766 == 'y'){
2767 if(rc == -1){
2768 macos_set_store_pass_prompt(1);
2769 q_status_message(SM_ORDER, 4, 4,
2770 _("Stop \"Preserve passwords?\" prompts by deleting Alpine Keychain entry"));
2772 return(1);
2774 else if(rc == -1){
2775 macos_set_store_pass_prompt(0);
2776 q_status_message(SM_ORDER, 4, 4,
2777 _("Restart \"Preserve passwords?\" prompts by deleting Alpine Keychain entry"));
2779 return(0);
2781 return(0);
2782 #else /* PASSFILE */
2783 char tmp[MAILTMPLEN];
2784 struct stat sbuf;
2786 if(!passfile_name(pinerc, tmp, sizeof(tmp)) || our_stat(tmp, &sbuf) < 0)
2787 return 0;
2789 if(F_OFF(F_DISABLE_PASSWORD_FILE_SAVING,ps_global))
2790 return(want_to(_("Preserve password on DISK for next login"),
2791 'y', 'x', NO_HELP, WT_NORM)
2792 == 'y');
2793 return(0);
2794 #endif /* PASSFILE */
2797 #endif /* LOCAL_PASSWD_CACHE */
2800 #ifdef APPLEKEYCHAIN
2803 * Returns:
2804 * 1 if store pass prompt is set in the "registry" to on
2805 * 0 if set to off
2806 * -1 if not set to anything
2809 macos_store_pass_prompt(void)
2811 char *data = NULL;
2812 UInt32 len = 0;
2813 int rc = -1;
2814 int val;
2816 if(storepassprompt == -1){
2817 if(!(rc=SecKeychainFindGenericPassword(NULL, 0, NULL,
2818 strlen(TNAMEPROMPT),
2819 TNAMEPROMPT, &len,
2820 (void **) &data, NULL))){
2821 val = (len == 1 && data && data[0] == '1');
2825 if(storepassprompt == -1 && !rc){
2826 if(val)
2827 storepassprompt = 1;
2828 else
2829 storepassprompt = 0;
2832 return(storepassprompt);
2836 void
2837 macos_set_store_pass_prompt(int val)
2839 storepassprompt = val ? 1 : 0;
2841 SecKeychainAddGenericPassword(NULL, 0, NULL, strlen(TNAMEPROMPT),
2842 TNAMEPROMPT, 1, val ? "1" : "0", NULL);
2846 void
2847 macos_erase_keychain(void)
2849 SecKeychainAttributeList attrList;
2850 SecKeychainSearchRef searchRef = NULL;
2851 SecKeychainAttribute attrs1[] = {
2852 { kSecAccountItemAttr, strlen(TNAME), TNAME }
2854 SecKeychainAttribute attrs2[] = {
2855 { kSecAccountItemAttr, strlen(TNAMEPROMPT), TNAMEPROMPT }
2858 dprint((9, "macos_erase_keychain\n"));
2861 * Seems like we ought to be able to combine attrs1 and attrs2
2862 * into a single array, but I couldn't get it to work.
2865 /* search for only our items in the keychain */
2866 attrList.count = 1;
2867 attrList.attr = attrs1;
2869 if(!SecKeychainSearchCreateFromAttributes(NULL,
2870 kSecGenericPasswordItemClass,
2871 &attrList,
2872 &searchRef)){
2873 if(searchRef){
2874 SecKeychainItemRef itemRef = NULL;
2877 * Go through each item we found and put it
2878 * into our list.
2880 while(!SecKeychainSearchCopyNext(searchRef, &itemRef) && itemRef){
2881 dprint((10, "read_passfile: SecKeychainSearchCopyNext got one\n"));
2882 SecKeychainItemDelete(itemRef);
2883 CFRelease(itemRef);
2886 CFRelease(searchRef);
2890 attrList.count = 1;
2891 attrList.attr = attrs2;
2893 if(!SecKeychainSearchCreateFromAttributes(NULL,
2894 kSecGenericPasswordItemClass,
2895 &attrList,
2896 &searchRef)){
2897 if(searchRef){
2898 SecKeychainItemRef itemRef = NULL;
2901 * Go through each item we found and put it
2902 * into our list.
2904 while(!SecKeychainSearchCopyNext(searchRef, &itemRef) && itemRef){
2905 SecKeychainItemDelete(itemRef);
2906 CFRelease(itemRef);
2909 CFRelease(searchRef);
2914 #endif /* APPLEKEYCHAIN */
2916 #ifdef LOCAL_PASSWD_CACHE
2919 * set_passfile_passwd - set the password file entry associated with
2920 * cache. The file is assumed to be in the same directory
2921 * as the pinerc with the name defined above.
2922 * already_prompted: 0 not prompted
2923 * 1 prompted, answered yes
2924 * 2 prompted, answered no
2926 void
2927 set_passfile_passwd(pinerc, passwd, user, hostlist, altflag, already_prompted)
2928 char *pinerc, *passwd, *user;
2929 STRLIST_S *hostlist;
2930 int altflag, already_prompted;
2932 dprint((10, "set_passfile_passwd\n"));
2933 if(((already_prompted == 0 && preserve_prompt(pinerc))
2934 || already_prompted == 1)
2935 && !ps_global->nowrite_password_cache
2936 && (passfile_cache || read_passfile(pinerc, &passfile_cache))){
2937 imap_set_passwd(&passfile_cache, passwd, user, hostlist, altflag, 0, 0);
2938 write_passfile(pinerc, passfile_cache);
2944 * Passfile lines are
2946 * passwd TAB user TAB hostname TAB flags [ TAB orig_hostname ] \n
2948 * In pine4.40 and before there was no orig_hostname.
2949 * This routine attempts to repair that.
2951 void
2952 update_passfile_hostlist(pinerc, user, hostlist, altflag)
2953 char *pinerc;
2954 char *user;
2955 STRLIST_S *hostlist;
2956 int altflag;
2958 #ifdef WINCRED
2959 return;
2960 #else /* !WINCRED */
2961 MMLOGIN_S *l;
2963 for(l = passfile_cache; l; l = l->next)
2964 if(imap_same_host(l->hosts, hostlist)
2965 && *user
2966 && !strcmp(user, l->user)
2967 && l->altflag == altflag){
2968 break;
2971 if(l && l->hosts && hostlist && !l->hosts->next && hostlist->next
2972 && hostlist->next->name
2973 && !ps_global->nowrite_password_cache){
2974 l->hosts->next = new_strlist(hostlist->next->name);
2975 write_passfile(pinerc, passfile_cache);
2977 #endif /* !WINCRED */
2980 #endif /* LOCAL_PASSWD_CACHE */
2983 #if (WINCRED > 0)
2985 * Load and init the WinCred structure.
2986 * This gives us a way to skip the WinCred code
2987 * if the dll doesn't exist.
2990 init_wincred_funcs(void)
2992 if(!g_CredInited)
2994 HMODULE hmod;
2996 /* Assume the worst. */
2997 g_CredInited = -1;
2999 hmod = LoadLibrary(TEXT("advapi32.dll"));
3000 if(hmod)
3002 FARPROC fpCredWriteW;
3003 FARPROC fpCredEnumerateW;
3004 FARPROC fpCredDeleteW;
3005 FARPROC fpCredFree;
3007 fpCredWriteW = GetProcAddress(hmod, "CredWriteW");
3008 fpCredEnumerateW = GetProcAddress(hmod, "CredEnumerateW");
3009 fpCredDeleteW = GetProcAddress(hmod, "CredDeleteW");
3010 fpCredFree = GetProcAddress(hmod, "CredFree");
3012 if(fpCredWriteW && fpCredEnumerateW && fpCredDeleteW && fpCredFree)
3014 g_CredWriteW = (CREDWRITEW *)fpCredWriteW;
3015 g_CredEnumerateW = (CREDENUMERATEW *)fpCredEnumerateW;
3016 g_CredDeleteW = (CREDDELETEW *)fpCredDeleteW;
3017 g_CredFree = (CREDFREE *)fpCredFree;
3019 g_CredInited = 1;
3023 mswin_set_erasecreds_callback(ask_erase_credentials);
3026 return g_CredInited;
3029 #endif /* WINCRED */