1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: imap.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
5 /* ========================================================================
6 * Copyright 2006-2007 University of Washington
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * ========================================================================
17 /*======================================================================
19 The call back routines for the c-client/imap
20 - handles error messages and other notification
21 - handles prelimirary notification of new mail and expunged mail
22 - prompting for imap server login and password
29 #include "../../../c-client/c-client.h"
31 #include "../../../pith/state.h"
32 #include "../../../pith/debug.h"
33 #include "../../../pith/string.h"
34 #include "../../../pith/flag.h"
35 #include "../../../pith/imap.h"
36 #include "../../../pith/status.h"
37 #include "../../../pith/osdep/collate.h"
47 long imap_seq_exec(MAILSTREAM
*, char *,long (*)(MAILSTREAM
*, long, void *), void *);
48 long imap_seq_exec_append(MAILSTREAM
*, long, void *);
52 * Exported globals setup by searching functions to tell mm_searched
53 * where to put message numbers that matched the search criteria,
54 * and to allow mm_searched to return number of matches.
56 //MAILSTREAM *mm_search_stream;
58 //MM_LIST_S *mm_list_info;
62 /*----------------------------------------------------------------------
63 Queue imap log message for display in the message line
65 Args: string -- The message
66 errflg -- flag set to 1 if pertains to an error
68 Result: Message queued for display
70 The c-client/imap reports most of it's status and errors here
73 mm_log(char *string
, long errflg
)
82 dprint((SYSDBG_ERR
, "%.*s (%ld)", 128, string
, errflg
));
85 now
= time((time_t *)0);
86 tm_now
= localtime(&now
);
88 dprint((ps_global
->debug_imap
? 0 : (errflg
== ERROR
? 1 : 2),
89 "IMAP %2.2d:%2.2d:%2.2d %d/%d mm_log %s: %s\n",
90 tm_now
->tm_hour
, tm_now
->tm_min
, tm_now
->tm_sec
, tm_now
->tm_mon
+1,
101 if(errflg
== ERROR
&& !strncmp(string
, "[TRYCREATE]", 11)){
102 ps_global
->try_to_create
= 1;
105 else if(ps_global
->try_to_create
106 || (sp_dead_stream(ps_global
->mail_stream
)
107 && (!strncmp(string
, "[CLOSED]", 8) || strstr(string
, "No-op"))))
109 * Don't display if creating new folder OR
110 * warning about a dead stream ...
114 /*---- replace all "mailbox" with "folder" ------*/
115 strncpy(message
, string
, sizeof(message
));
116 message
[sizeof(message
) - 1] = '\0';
117 occurance
= srchstr(message
, "mailbox");
119 if(!*(occurance
+7) || isspace((unsigned char)*(occurance
+7))){
120 was_capitalized
= isupper((unsigned char)*occurance
);
121 rplstr(occurance
, 7, 7, (errflg
== PARSE
? "address" : "folder"));
123 *occurance
= (errflg
== PARSE
? 'A' : 'F');
128 occurance
= srchstr(occurance
, "mailbox");
132 ps_global
->mm_log_error
= 1;
134 if(errflg
== PARSE
|| (errflg
== ERROR
&& ps_global
->noshow_error
)){
135 strncpy(ps_global
->c_client_error
, message
, sizeof(ps_global
->c_client_error
));
136 ps_global
->c_client_error
[sizeof(ps_global
->c_client_error
)-1] = '\0';
139 if(ps_global
->noshow_error
140 || (ps_global
->noshow_warn
&& errflg
== WARN
)
141 || !(errflg
== ERROR
|| errflg
== WARN
))
142 return; /* Only care about errors; don't print when asked not to */
144 /*---- Display the message ------*/
145 q_status_message((errflg
== ERROR
) ? (SM_ORDER
| SM_DING
) : SM_ORDER
,
148 strncpy(ps_global
->last_error
, message
, sizeof(ps_global
->last_error
));
149 ps_global
->last_error
[sizeof(ps_global
->last_error
)-1] = '\0';
155 /*----------------------------------------------------------------------
156 receive notification from IMAP
158 Args: stream -- Mail stream message is relevant to
159 string -- The message text
160 errflag -- Set if it is a serious error
162 Result: message displayed in status line
164 The facility is for general notices, such as connection to server;
165 server shutting down etc... It is used infrequently.
166 ----------------------------------------------------------------------*/
168 mm_notify(MAILSTREAM
*stream
, char *string
, long errflag
)
170 if(errflag
== ERROR
){
171 dprint((SYSDBG_ERR
, "mm_notify: %s (%ld)", string
, errflag
));
174 /* be sure to log the message... */
176 if(ps_global
->debug_imap
)
177 dprint((0, "IMAP mm_notify %s : %s (%s) : %s\n",
179 (errflag
== ERROR
) ? "error" :
180 (errflag
== WARN
) ? "warning" :
181 (errflag
== BYE
) ? "bye" : "unknown",
182 (stream
&& stream
->mailbox
) ? stream
->mailbox
: "-no folder-",
183 (stream
&& stream
== sp_inbox_stream()) ? "inboxstream" :
184 (stream
&& stream
== ps_global
->mail_stream
) ? "mailstream" :
185 (stream
) ? "abookstream?" : "nostream",
189 strncpy(ps_global
->last_error
, string
, 500);
190 ps_global
->last_error
[499] = '\0';
193 * Then either set special bits in the pine struct or
194 * display the message if it's tagged as an "ALERT" or
195 * its errflag > NIL (i.e., WARN, or ERROR)
198 if(stream
== ps_global
->mail_stream
){
199 if(sp_dead_stream(ps_global
->mail_stream
))
202 sp_set_dead_stream(ps_global
->mail_stream
, 1);
204 else if(stream
&& stream
== sp_inbox_stream()){
205 if(sp_dead_stream(stream
))
208 sp_set_dead_stream(stream
, 1);
211 else if(!strncmp(string
, "[TRYCREATE]", 11))
212 ps_global
->try_to_create
= 1;
213 else if(!strncmp(string
, "[ALERT]", 7))
214 q_status_message2(SM_MODAL
, 3, 3, "Alert received while accessing \"%s\": %s",
215 (stream
&& stream
->mailbox
)
216 ? stream
->mailbox
: "-no folder-",
217 rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
, SIZEOF_20KBUF
, string
));
218 else if(!strncmp(string
, "[UNSEEN ", 8)){
222 for(p
= string
+ 8; isdigit(*p
); p
++)
223 n
= (n
* 10) + (*p
- '0');
225 sp_set_first_unseen(ps_global
->mail_stream
, n
);
227 else if(!strncmp(string
, "[READ-ONLY]", 11)
228 && !(stream
&& stream
->mailbox
&& IS_NEWS(stream
)))
229 q_status_message2(SM_ORDER
| SM_DING
, 3, 3, "%s : %s",
230 (stream
&& stream
->mailbox
)
231 ? stream
->mailbox
: "-no folder-",
233 else if(errflag
&& (errflag
== WARN
|| errflag
== ERROR
))
234 q_status_message(SM_ORDER
| ((errflag
== ERROR
) ? SM_DING
: 0),
235 3, 6, ps_global
->last_error
);
239 mm_login_method_work (NETMBX
*mb
,char *user
,void *login
,long trial
, char *method
, char *usethisprompt
, char *altuserforcache
)
244 /*----------------------------------------------------------------------
245 Do work of getting login and password from user for IMAP login
247 Args: mb -- The mail box property struct
248 user -- Buffer to return the user name in
249 passwd -- Buffer to return the passwd in
250 trial -- The trial number or number of attempts to login
252 Result: username and password passed back to imap
255 mm_login_work(NETMBX
*mb
, char *user
, char **passwd
, long trial
, char *usethisprompt
, char *altuserforcache
)
257 STRLIST_S hostlist
[2];
264 if((l
= strlen(mb
->orighost
)) > 0 && l
< CRED_REQ_SIZE
)
265 strcpy(peCredentialRequestor
, mb
->orighost
);
267 if(trial
){ /* one shot only! */
269 peCredentialError
= 1;
275 if(ps_global
&& ps_global
->anonymous
) {
276 /*------ Anonymous login mode --------*/
278 strcpy(user
, "anonymous");
279 sprintf(pwd
, "%s@%s", ps_global
->VAR_USER_ID
,
280 ps_global
->hostname
);
283 user
[0] = pwd
[0] = '\0';
290 #if WEB_REQUIRE_SECURE_IMAP
291 /* we *require* secure authentication */
292 if(!(mb
->sslflag
|| mb
->tlsflag
) && strcmp("localhost",mb
->host
)){
293 user
[0] = pwd
[0] = '\0';
300 * heavily paranoid about offering password to server
301 * that the users hasn't also indicated the remote user
305 strcpy(user
, mb
->user
);
307 else if(ps_global
->prc
308 && ps_global
->prc
->name
309 && mail_valid_net_parse(ps_global
->prc
->name
,&cmb
)
311 strcpy(user
, cmb
.user
);
315 * don't blindly offer user/pass
317 user
[0] = pwd
[0] = '\0';
323 * set up host list for sybil servers...
325 hostlist
[0].name
= mb
->host
;
326 if(mb
->orighost
[0] && strucmp(mb
->host
, mb
->orighost
)){
327 hostlist
[0].next
= &hostlist
[1];
328 hostlist
[1].name
= mb
->orighost
;
329 hostlist
[1].next
= NULL
;
332 hostlist
[0].next
= NULL
;
334 /* try last working password associated with this host. */
335 if(!imap_get_passwd(mm_login_list
, passwd
, user
, hostlist
, (mb
->sslflag
|| mb
->tlsflag
))){
337 user
[0] = pwd
[0] = '\0';
344 /*----------------------------------------------------------------------
345 Receive notification of an error writing to disk
347 Args: stream -- The stream the error occurred on
348 errcode -- The system error code (errno)
349 serious -- Flag indicating error is serious (mail may be lost)
351 Result: If error is non serious, the stream is marked as having an error
352 and deletes are disallowed until error clears
353 If error is serious this returns with syslogging if possible
356 mm_diskerror (MAILSTREAM
*stream
, long errcode
, long serious
)
358 if(!serious
&& stream
== ps_global
->mail_stream
) {
359 sp_set_io_error_on_stream(ps_global
->mail_stream
, 1);
362 dprint((SYSDBG_ERR
, "mm_diskerror: mailbox: %s, errcode: %ld, serious: %ld\n",
363 (stream
&& stream
->mailbox
) ? stream
->mailbox
: "", errcode
, serious
));
370 * alpine_tcptimeout - C-client callback to handle tcp-related timeouts.
373 alpine_tcptimeout(long elapsed
, long sincelast
)
375 long rv
= 1L; /* keep trying by default */
377 dprint((SYSDBG_INFO
, "tcptimeout: waited %s seconds\n", long2string(elapsed
)));
379 if(elapsed
> WP_TCP_TIMEOUT
){
380 dprint((SYSDBG_ERR
, "tcptimeout: BAIL after %s seconds\n", long2string(elapsed
)));
389 * C-client callback to handle SSL/TLS certificate validation failures
391 * Returning 0 means error becomes fatal
392 * Non-zero means certificate problem is ignored and SSL session is
395 * We remember the answer and won't re-ask for subsequent open attempts to
399 alpine_sslcertquery(char *reason
, char *host
, char *cert
)
403 for(p
= peCertHosts
; p
; p
= p
->next
)
404 if(!strucmp(p
->name
, host
))
408 snprintf(peCredentialRequestor
, CRED_REQ_SIZE
, "%s++%s", host
? host
: "?", reason
? reason
: "UNKNOWN");
409 q_status_message(SM_ORDER
, 0, 3, "SSL Certificate Problem");
410 dprint((SYSDBG_INFO
, "sslcertificatequery: host=%s reason=%s cert=%s\n",
411 host
? host
: "?", reason
? reason
: "?",
418 * C-client callback to handle SSL/TLS certificate validation failures
421 alpine_sslfailure(char *host
, char *reason
, unsigned long flags
)
424 snprintf(peCredentialRequestor
, CRED_REQ_SIZE
, "%s++%s", host
? host
: "?", reason
? reason
: "UNKNOWN");
425 q_status_message1(SM_ORDER
, 0, 3, "SSL Certificate Failure: %s", reason
? reason
: "?");
426 dprint((SYSDBG_INFO
, "SSL Invalid Cert (%s) : %s", host
, reason
));
431 /*----------------------------------------------------------------------
432 This can be used to prevent the flickering of the check_cue char
433 caused by numerous (5000+) fetches by c-client. Right now, the only
434 practical use found is newsgroup subsciption.
436 check_cue_display will check if this global is set, and won't clear
437 the check_cue_char if set.
440 set_read_predicted(int i
)
444 /*----------------------------------------------------------------------
445 Exported method to display status of mail check
447 Args: putstr -- should be NO LONGER THAN 2 bytes
449 Result: putstr displayed at upper-left-hand corner of screen
452 check_cue_display(char *putstr
)
459 alpine_set_passwd(char *user
, char *passwd
, char *host
, int altflag
)
461 STRLIST_S hostlist
[1];
463 hostlist
[0].name
= host
;
464 hostlist
[0].next
= NULL
;
466 imap_set_passwd(&mm_login_list
, passwd
, user
, hostlist
, altflag
, 1, 0);
471 alpine_clear_passwd(char *user
, char *host
)
474 STRLIST_S hostlist
[1];
476 hostlist
[0].name
= host
;
477 hostlist
[0].next
= NULL
;
479 for(lp
= &mm_login_list
; *lp
; lp
= &(*lp
)->next
)
480 if(imap_same_host((*lp
)->hosts
, hostlist
)
481 && (!*user
|| !strcmp(user
, (*lp
)->user
))){
486 fs_give((void **) &l
->user
);
488 free_strlist(&l
->hosts
);
497 fs_give((void **) &l
);
505 alpine_have_passwd(char *user
, char *host
, int altflag
)
507 STRLIST_S hostlist
[1];
509 hostlist
[0].name
= host
;
510 hostlist
[0].next
= NULL
;
512 return(imap_get_passwd(mm_login_list
, NULL
, user
, hostlist
, altflag
));
517 alpine_get_user(char *host
)
519 STRLIST_S hostlist
[1];
521 hostlist
[0].name
= host
;
522 hostlist
[0].next
= NULL
;
524 return(imap_get_user(mm_login_list
, hostlist
));