2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2007 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "../pith/headers.h"
16 #include "../pith/mailcmd.h"
17 #include "../pith/conf.h"
18 #include "../pith/status.h"
19 #include "../pith/flag.h"
20 #include "../pith/thread.h"
21 #include "../pith/util.h"
22 #include "../pith/folder.h"
23 #include "../pith/sort.h"
24 #include "../pith/newmail.h"
25 #include "../pith/mailview.h"
26 #include "../pith/mailindx.h"
27 #include "../pith/save.h"
28 #include "../pith/news.h"
29 #include "../pith/sequence.h"
30 #include "../pith/stream.h"
31 #include "../pith/ldap.h"
32 #include "../pith/options.h"
33 #include "../pith/busy.h"
34 #include "../pith/icache.h"
35 #include "../pith/ablookup.h"
36 #include "../pith/search.h"
37 #include "../pith/charconv/utf8.h"
40 #include "../pico/osdep/mswin.h"
50 * optional function hooks
52 int (*pith_opt_read_msg_prompt
)(long, char *);
53 int (*pith_opt_reopen_folder
)(struct pine
*, int *);
54 int (*pith_opt_expunge_prompt
)(MAILSTREAM
*, char *, long);
55 void (*pith_opt_begin_closing
)(int, char *);
56 void get_new_message_count(MAILSTREAM
*, int, long *, long *);
57 char *new_messages_string(MAILSTREAM
*);
58 void search_for_our_regex_addresses(MAILSTREAM
*stream
, char type
,
59 int not, SEARCHSET
*searchset
);
63 /*----------------------------------------------------------------------
64 Complain about command on empty folder
67 type -- type of message that's missing
68 cmd -- string explaining command attempted
72 any_messages(MSGNO_S
*map
, char *type
, char *cmd
)
74 if(mn_get_total(map
) <= 0L){
75 q_status_message5(SM_ORDER
, 0, 2, "No %s%s%s%s%s",
78 THRD_INDX() ? "threads" : "messages",
79 (!cmd
|| *cmd
!= '.') ? " " : "",
80 cmd
? cmd
: "in folder");
88 /*----------------------------------------------------------------------
89 test whether or not we have a valid stream to set flags on
91 Args: state -- pine state containing vital signs
92 cmd -- string explaining command attempted
93 permflag -- associated permanent flag state
95 Result: returns 1 if we can set flags, otw 0 and complains
99 can_set_flag(struct pine
*state
, char *cmd
, int permflag
)
101 if((!permflag
&& READONLY_FOLDER(state
->mail_stream
))
102 || sp_dead_stream(state
->mail_stream
)){
103 q_status_message2(SM_ORDER
| (sp_dead_stream(state
->mail_stream
)
106 "Can't %s message. Folder is %s.", cmd
,
107 (sp_dead_stream(state
->mail_stream
)) ? "closed" : "read-only");
115 /*----------------------------------------------------------------------
116 Complain about command on empty folder
118 Args: type -- type of message that's missing
119 cmd -- string explaining command attempted
123 cmd_cancelled(char *cmd
)
125 /* TRANSLATORS: Arg is replaced with the command name or the word Command */
126 q_status_message1(SM_INFO
, 0, 2, _("%s cancelled"), cmd
? cmd
: _("Command"));
130 /*----------------------------------------------------------------------
131 Execute DELETE message command
133 Args: state -- Various satate info
134 msgmap -- map of c-client to local message numbers
136 Result: with side effect of "current" message delete flag set
140 cmd_delete(struct pine
*state
, MSGNO_S
*msgmap
, int copts
,
141 char *(*cmd_action_f
)(struct pine
*, MSGNO_S
*))
144 long msgno
, del_count
= 0L, new;
145 char *sequence
= NULL
, prompt
[128];
147 dprint((4, "\n - delete message -\n"));
148 if(!(any_messages(msgmap
, NULL
, "to Delete")
149 && can_set_flag(state
, "delete", state
->mail_stream
->perm_deleted
)))
154 if(sp_io_error_on_stream(state
->mail_stream
)){
155 sp_set_io_error_on_stream(state
->mail_stream
, 0);
156 pine_mail_check(state
->mail_stream
); /* forces write */
159 if(MCMD_ISAGG(copts
)){
160 sequence
= selected_sequence(state
->mail_stream
, msgmap
, &del_count
, 0);
161 snprintf(prompt
, sizeof(prompt
), "%ld%s message%s marked for deletion",
162 del_count
, (copts
& MCMD_AGG_2
) ? "" : " selected", plural(del_count
));
167 msgno
= mn_get_cur(msgmap
);
168 rawno
= mn_m2raw(msgmap
, msgno
);
169 del_count
= 1L; /* return current */
170 sequence
= cpystr(long2string(rawno
));
171 lastmsg
= (msgno
>= mn_get_total(msgmap
));
172 snprintf(prompt
, sizeof(prompt
), "%s%s marked for deletion",
173 lastmsg
? "Last message" : "Message ",
174 lastmsg
? "" : long2string(msgno
));
177 dprint((3, "DELETE: msg %s\n", sequence
? sequence
: "?"));
178 new = sp_new_mail_count(state
->mail_stream
);
179 mail_flag(state
->mail_stream
, sequence
, "\\DELETED", ST_SET
);
180 fs_give((void **) &sequence
);
181 if(new != sp_new_mail_count(state
->mail_stream
))
182 process_filter_patterns(state
->mail_stream
, state
->msgmap
,
183 sp_new_mail_count(state
->mail_stream
));
188 if((rv
= (*cmd_action_f
)(state
, msgmap
)) != NULL
)
189 strncat(prompt
, rv
, sizeof(prompt
) - strlen(prompt
)- 1);
192 if(!(copts
& MCMD_SILENT
))
193 q_status_message(SM_ORDER
, 0, 3, prompt
);
199 /*----------------------------------------------------------------------
200 Execute UNDELETE message command
202 Args: state -- Various satate info
203 msgmap -- map of c-client to local message numbers
205 Result: with side effect of "current" message delete flag UNset
209 cmd_undelete(struct pine
*state
, MSGNO_S
*msgmap
, int copts
)
213 int wasdeleted
= FALSE
, rv
= 0;
216 dprint((4, "\n - undelete -\n"));
217 if(!(any_messages(msgmap
, NULL
, "to Undelete")
218 && can_set_flag(state
, "undelete", state
->mail_stream
->perm_deleted
)))
223 if(MCMD_ISAGG(copts
)){
224 del_count
= 0L; /* return current */
225 sequence
= selected_sequence(state
->mail_stream
, msgmap
, &del_count
, 1);
232 del_count
= 1L; /* return current */
233 rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
));
234 sequence
= cpystr(long2string(rawno
));
235 wasdeleted
= (state
->mail_stream
236 && rawno
> 0L && rawno
<= state
->mail_stream
->nmsgs
237 && (mc
= mail_elt(state
->mail_stream
, rawno
))
241 * Mark this message manually flagged so we don't re-filter it
242 * with a filter which only sets flags.
244 if(msgno_exceptions(state
->mail_stream
, rawno
, "0", &exbits
, FALSE
))
245 exbits
|= MSG_EX_MANUNDEL
;
247 exbits
= MSG_EX_MANUNDEL
;
249 msgno_exceptions(state
->mail_stream
, rawno
, "0", &exbits
, TRUE
);
252 dprint((3, "UNDELETE: msg %s\n", sequence
? sequence
: "?"));
254 mail_flag(state
->mail_stream
, sequence
, "\\DELETED", 0L);
255 fs_give((void **) &sequence
);
257 if((copts
& MCMD_SILENT
) == 0){
258 if(del_count
== 1L && MCMD_ISAGG(copts
) == 0){
259 q_status_message(SM_ORDER
, 0, 3,
261 ? _("Deletion mark removed, message won't be deleted")
262 : _("Message not marked for deletion; no action taken"));
265 q_status_message2(SM_ORDER
, 0, 3,
266 _("Deletion mark removed from %s message%s"),
267 comatose(del_count
), plural(del_count
));
270 if(sp_io_error_on_stream(state
->mail_stream
)){
271 sp_set_io_error_on_stream(state
->mail_stream
, 0);
272 pine_mail_check(state
->mail_stream
); /* forces write */
280 cmd_expunge_work(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, char *seq
)
285 old_max_msgno
= mn_get_total(msgmap
);
286 delete_filtered_msgs(stream
);
287 ps_global
->expunge_in_progress
= 1;
288 mail_expunge_full(stream
, seq
, 0);
289 ps_global
->expunge_in_progress
= 0;
291 dprint((2,"expunge complete cur:%ld max:%ld\n",
292 mn_get_cur(msgmap
), mn_get_total(msgmap
)));
294 * This is only actually necessary if this causes the width of the
295 * message number field to change. That is, it depends on the
296 * format the user is using as well as on the max_msgno. Since it
297 * should be rare, we'll just do it whenever it happens.
298 * Also have to check for an increase in max_msgno on new mail.
300 if((old_max_msgno
>= 1000L && mn_get_total(msgmap
) < 1000L)
301 || (old_max_msgno
>= 10000L && mn_get_total(msgmap
) < 10000L)
302 || (old_max_msgno
>= 100000L && mn_get_total(msgmap
) < 100000L)){
303 clear_index_cache(stream
, 0);
308 * mm_exists and mm_expunge take care of updating max_msgno
309 * and selecting a new message should the selected get removed
313 reset_check_point(stream
);
320 broach_get_folder(CONTEXT_S
*context
, int *inbox
, char **folder
)
324 if(ps_global
->goto_default_rule
== GOTO_LAST_FLDR
){
325 tc
= context
? context
: ps_global
->context_current
;
326 *inbox
= 1; /* fill in last_folder below */
328 else if(ps_global
->goto_default_rule
== GOTO_FIRST_CLCTN
){
329 tc
= (ps_global
->context_list
->use
& CNTXT_INCMNG
)
330 ? ps_global
->context_list
->next
: ps_global
->context_list
;
331 ps_global
->last_unambig_folder
[0] = '\0';
332 *inbox
= 1; /* fill in last_folder below */
334 else if(ps_global
->goto_default_rule
== GOTO_FIRST_CLCTN_DEF_INBOX
){
335 tc
= (ps_global
->context_list
->use
& CNTXT_INCMNG
)
336 ? ps_global
->context_list
->next
: ps_global
->context_list
;
337 tc
->last_folder
[0] = '\0';
339 ps_global
->last_unambig_folder
[0] = '\0';
342 *inbox
= (ps_global
->cur_folder
343 && ps_global
->inbox_name
344 && strucmp(ps_global
->cur_folder
,ps_global
->inbox_name
) == 0
345 && (!ps_global
->context_current
346 || ps_global
->context_current
->use
& CNTXT_INCMNG
347 || (!(ps_global
->context_list
->use
& CNTXT_INCMNG
)
348 && ps_global
->context_current
== ps_global
->context_list
)));
350 tc
= ps_global
->context_list
; /* inbox's context */
351 else if(ps_global
->goto_default_rule
== GOTO_INBOX_FIRST_CLCTN
){
352 tc
= (ps_global
->context_list
->use
& CNTXT_INCMNG
)
353 ? ps_global
->context_list
->next
: ps_global
->context_list
;
354 ps_global
->last_unambig_folder
[0] = '\0';
357 tc
= context
? context
: ps_global
->context_current
;
362 *folder
= ps_global
->inbox_name
;
365 *folder
= (ps_global
->last_unambig_folder
[0])
366 ? ps_global
->last_unambig_folder
367 : ((tc
->last_folder
[0]) ? tc
->last_folder
: NULL
);
374 /*----------------------------------------------------------------------
375 Actually attempt to open given folder
377 Args: newfolder -- The folder name to open
378 streamp -- Candidate stream for recycling. This stream will either
379 be re-used, or it will be closed.
381 Result: 1 if the folder was successfully opened
382 0 if the folder open failed and went back to old folder
383 -1 if open failed and no folder is left open
385 Attempt to open the folder name given. If the open of the new folder
386 fails then the previously open folder will remain open, unless
387 something really bad has happened. The designate inbox will always be
388 kept open, and when a request to open it is made the already open
392 do_broach_folder(char *newfolder
, CONTEXT_S
*new_context
, MAILSTREAM
**streamp
,
393 long unsigned int flags
)
395 MAILSTREAM
*m
, *strm
, *stream
= streamp
? *streamp
: NULL
;
396 int open_inbox
, rv
, old_tros
, we_cancel
= 0,
397 do_reopen
= 0, n
, was_dead
= 0, cur_already_set
= 0;
398 char expanded_file
[MAX(MAXPATH
,MAILTMPLEN
)+1],
399 *old_folder
, *old_path
, *p
, *report
;
400 unsigned char *fname
;
401 long openmode
, rflags
= 0L, pc
= 0L, cur
, raw
;
402 ENVELOPE
*env
= NULL
;
405 unsigned perfolder_startup_rule
;
406 char tmp1
[MAILTMPLEN
], tmp2
[MAILTMPLEN
], *lname
, *mname
;
408 openmode
= SP_USERFLDR
;
410 dprint((1, "About to open folder \"%s\" inbox is: \"%s\"\n",
411 newfolder
? newfolder
: "?",
412 ps_global
->inbox_name
? ps_global
->inbox_name
: "?"));
415 *--- Set flag that we're opening the inbox, a special case.
417 * We want to know if inbox is being opened either by name OR
418 * fully qualified path...
420 if(strucmp(newfolder
, ps_global
->inbox_name
) == 0)
421 open_inbox
= (flags
& DB_INBOXWOCNTXT
|| new_context
== ps_global
->context_list
);
423 open_inbox
= (strcmp(newfolder
, ps_global
->VAR_INBOX_PATH
) == 0
424 || same_remote_mailboxes(newfolder
, ps_global
->VAR_INBOX_PATH
)
425 || (!IS_REMOTE(newfolder
)
426 && (lname
=mailboxfile(tmp1
,newfolder
))
427 && (mname
=mailboxfile(tmp2
,ps_global
->VAR_INBOX_PATH
))
428 && !strcmp(lname
,mname
)));
430 /* further checking for inbox open */
431 if(!open_inbox
&& new_context
&& context_isambig(newfolder
)){
432 if((p
= folder_is_nick(newfolder
, FOLDERS(new_context
), FN_WHOLE_NAME
)) != NULL
){
434 * Check for an incoming folder other
435 * than INBOX that also point to INBOX.
437 open_inbox
= (strucmp(p
, ps_global
->inbox_name
) == 0
438 || strcmp(p
, ps_global
->VAR_INBOX_PATH
) == 0
439 || same_remote_mailboxes(p
, ps_global
->VAR_INBOX_PATH
)
441 && (lname
=mailboxfile(tmp1
,p
))
442 && (mname
=mailboxfile(tmp2
,ps_global
->VAR_INBOX_PATH
))
443 && !strcmp(lname
,mname
)));
445 else if(!(new_context
->use
& CNTXT_INCMNG
)){
446 char tmp3
[MAILTMPLEN
];
449 * Check to see if we are opening INBOX using the folder name
450 * and a context. We won't have recognized this is the
451 * same as INBOX without applying the context first.
453 context_apply(tmp3
, new_context
, newfolder
, sizeof(tmp3
));
454 open_inbox
= (strucmp(tmp3
, ps_global
->inbox_name
) == 0
455 || strcmp(tmp3
, ps_global
->VAR_INBOX_PATH
) == 0
456 || same_remote_mailboxes(tmp3
, ps_global
->VAR_INBOX_PATH
)
458 && (lname
=mailboxfile(tmp1
,tmp3
))
459 && (mname
=mailboxfile(tmp2
,ps_global
->VAR_INBOX_PATH
))
460 && !strcmp(lname
,mname
)));
466 new_context
= ps_global
->context_list
; /* restore first context */
468 was_dead
= sp_a_locked_stream_is_dead();
470 /*----- Little to do to if reopening same folder -----*/
471 if(new_context
== ps_global
->context_current
&& ps_global
->mail_stream
472 && (strcmp(newfolder
, ps_global
->cur_folder
) == 0
473 || (open_inbox
&& sp_flagged(ps_global
->mail_stream
, SP_INBOX
)))){
475 pine_mail_close(stream
); /* don't need it */
479 if(sp_dead_stream(ps_global
->mail_stream
))
483 * If it is a stream which could probably discover newmail by
484 * reopening and user has YES set for those streams, or it
485 * is a stream which may discover newmail by reopening and
486 * user has YES set for those stream, then do_reopen.
490 (((ps_global
->mail_stream
->dtb
491 && ((ps_global
->mail_stream
->dtb
->flags
& DR_NONEWMAIL
)
492 || (ps_global
->mail_stream
->rdonly
493 && ps_global
->mail_stream
->dtb
->flags
494 & DR_NONEWMAILRONLY
)))
495 && (ps_global
->reopen_rule
== REOPEN_YES_YES
496 || ps_global
->reopen_rule
== REOPEN_YES_ASK_Y
497 || ps_global
->reopen_rule
== REOPEN_YES_ASK_N
498 || ps_global
->reopen_rule
== REOPEN_YES_NO
))
500 ((ps_global
->mail_stream
->dtb
501 && ps_global
->mail_stream
->rdonly
502 && !(ps_global
->mail_stream
->dtb
->flags
& DR_LOCAL
))
503 && (ps_global
->reopen_rule
== REOPEN_YES_YES
))))
507 * If it is a stream which could probably discover newmail by
508 * reopening and user has ASK set for those streams, or it
509 * is a stream which may discover newmail by reopening and
510 * user has ASK set for those stream, then ask.
513 && pith_opt_reopen_folder
514 && (*pith_opt_reopen_folder
)(ps_global
, &do_reopen
) < 0){
521 * If it's not healthy or if the user explicitly wants to
522 * do a reopen, we reset things and fall thru
523 * to actually reopen it.
525 if(sp_dead_stream(ps_global
->mail_stream
)){
526 dprint((2, "Stream was dead, reopening \"%s\"\n",
527 newfolder
? newfolder
: "?"));
531 pine_mail_actually_close(ps_global
->mail_stream
);
532 ps_global
->mangled_header
= 1;
533 clear_index_cache(ps_global
->mail_stream
, 0);
536 if(!(flags
& DB_NOVISIT
))
537 sp_set_recent_since_visited(ps_global
->mail_stream
, 0L);
539 return(1); /* successful open of same folder! */
544 * If ambiguous foldername (not fully qualified), make sure it's
545 * not a nickname for a folder in the given context...
548 /* might get reset below */
549 strncpy(expanded_file
, newfolder
, sizeof(expanded_file
));
550 expanded_file
[sizeof(expanded_file
)-1] = '\0';
552 if(!open_inbox
&& new_context
&& context_isambig(newfolder
)){
553 if((p
= folder_is_nick(newfolder
, FOLDERS(new_context
), FN_WHOLE_NAME
)) != NULL
){
554 strncpy(expanded_file
, p
, sizeof(expanded_file
));
555 expanded_file
[sizeof(expanded_file
)-1] = '\0';
556 dprint((2, "broach_folder: nickname for %s is %s\n",
557 expanded_file
? expanded_file
: "?",
558 newfolder
? newfolder
: "?"));
560 else if((new_context
->use
& CNTXT_INCMNG
)
561 && (folder_index(newfolder
, new_context
, FI_FOLDER
) < 0)
562 && !is_absolute_path(newfolder
)){
563 fname
= folder_name_decoded((unsigned char *)newfolder
);
564 q_status_message1(SM_ORDER
, 3, 4,
565 _("Can't find Incoming Folder %s."), fname
? (char *) fname
: newfolder
);
567 pine_mail_close(stream
);
570 fs_give((void **)&fname
);
576 /*--- Opening inbox, inbox has been already opened, the easy case ---*/
578 * [ It is probably true that we could eliminate most of this special ]
579 * [ inbox stuff and just get the inbox stream back when we do the ]
580 * [ context_open below, but figuring that out hasn't been done. ]
582 if(open_inbox
&& (strm
=sp_inbox_stream())){
583 if(sp_dead_stream(strm
)){
585 * if dead INBOX, just close it and let it be reopened.
586 * This is different from the do_reopen case above,
587 * because we're going from another open mail folder to the
590 dprint((2, "INBOX was dead, closing before reopening\n"));
591 pine_mail_actually_close(strm
);
595 * Clean up the mail_stream we're leaving.
597 if(ps_global
->mail_stream
598 && (!sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)
599 || (sp_flagged(ps_global
->mail_stream
, SP_INBOX
)
600 && F_ON(F_EXPUNGE_INBOX
, ps_global
))
601 || (!sp_flagged(ps_global
->mail_stream
, SP_INBOX
)
602 && sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)
603 && F_ON(F_EXPUNGE_STAYOPENS
, ps_global
))))
604 expunge_and_close(ps_global
->mail_stream
, NULL
,
605 sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)
606 ? EC_NO_CLOSE
: EC_NONE
);
607 else if(!sp_flagged(ps_global
->mail_stream
, SP_INBOX
)
608 && sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)){
610 * We want to save our position in the folder so that when we
611 * come back to this folder again, we can place the cursor on
612 * a reasonable message number.
614 sp_set_saved_cur_msg_id(ps_global
->mail_stream
, NULL
);
616 if(ps_global
->mail_stream
->nmsgs
> 0L){
617 cur
= mn_get_cur(sp_msgmap(ps_global
->mail_stream
));
618 raw
= mn_m2raw(sp_msgmap(ps_global
->mail_stream
), cur
);
619 if(raw
> 0L && raw
<= ps_global
->mail_stream
->nmsgs
)
620 env
= pine_mail_fetchstructure(ps_global
->mail_stream
,
623 if(env
&& env
->message_id
&& env
->message_id
[0])
624 sp_set_saved_cur_msg_id(ps_global
->mail_stream
,
630 * Make the already open inbox the current mailbox.
632 ps_global
->mail_stream
= strm
;
633 ps_global
->msgmap
= sp_msgmap(strm
);
635 if(was_dead
&& pith_opt_icon_text
)
636 (*pith_opt_icon_text
)(NULL
, IT_MCLOSED
);
638 dprint((7, "%ld %ld %x\n",
639 mn_get_cur(ps_global
->msgmap
),
640 mn_get_total(ps_global
->msgmap
),
641 ps_global
->mail_stream
));
643 * remember last context and folder
645 if(context_isambig(ps_global
->cur_folder
)){
646 ps_global
->context_last
= ps_global
->context_current
;
647 snprintf(ps_global
->context_current
->last_folder
,
648 sizeof(ps_global
->context_current
->last_folder
),
649 "%.*s", (int) sizeof(ps_global
->context_current
->last_folder
) - 1,ps_global
->cur_folder
);
650 ps_global
->last_unambig_folder
[0] = '\0';
653 ps_global
->context_last
= NULL
;
654 snprintf(ps_global
->last_unambig_folder
,
655 sizeof(ps_global
->last_unambig_folder
),
656 "%s", ps_global
->cur_folder
);
659 p
= sp_fldr(ps_global
->mail_stream
) ? sp_fldr(ps_global
->mail_stream
)
660 : ps_global
->inbox_name
;
661 strncpy(ps_global
->cur_folder
, p
, sizeof(ps_global
->cur_folder
)-1);
662 ps_global
->cur_folder
[sizeof(ps_global
->cur_folder
)-1] = '\0';
663 ps_global
->context_current
= ps_global
->context_list
;
664 reset_index_format();
665 clear_index_cache(ps_global
->mail_stream
, 0);
666 /* MUST sort before restoring msgno! */
667 refresh_sort(ps_global
->mail_stream
, ps_global
->msgmap
, SRT_NON
);
668 report
= new_messages_string(ps_global
->mail_stream
);
669 q_status_message3(SM_ORDER
, 0, 3,
670 (mn_get_total(ps_global
->msgmap
) > 1)
671 ? _("Opened folder \"%s\" with %s messages%s")
672 : _("Opened folder \"%s\" with %s message%s"),
673 ps_global
->inbox_name
,
674 long2string(mn_get_total(ps_global
->msgmap
)),
675 report
? report
: "");
677 fs_give((void **)&report
);
680 mswin_settitle(ps_global
->inbox_name
);
683 pine_mail_close(stream
);
685 if(!(flags
& DB_NOVISIT
))
686 sp_set_recent_since_visited(ps_global
->mail_stream
, 0L);
692 if(!new_context
&& !expand_foldername(expanded_file
,sizeof(expanded_file
))){
694 pine_mail_close(stream
);
700 * This is a safe time to clean up dead streams because nothing should
701 * be referencing them right now.
703 sp_cleanup_dead_streams();
707 old_sort
= SortArrival
; /* old sort */
708 old_tros
= 0; /* old reverse sort ? */
709 /*---- now close the old one we had open if there was one ----*/
710 if(ps_global
->mail_stream
!= NULL
){
711 old_folder
= cpystr(ps_global
->cur_folder
);
712 old_path
= cpystr(ps_global
->mail_stream
->original_mailbox
713 ? ps_global
->mail_stream
->original_mailbox
714 : ps_global
->mail_stream
->mailbox
);
715 old_sort
= mn_get_sort(ps_global
->msgmap
);
716 old_tros
= mn_get_revsort(ps_global
->msgmap
);
717 if(!sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)
718 || (sp_flagged(ps_global
->mail_stream
, SP_INBOX
)
719 && F_ON(F_EXPUNGE_INBOX
, ps_global
))
720 || (!sp_flagged(ps_global
->mail_stream
, SP_INBOX
)
721 && sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)
722 && F_ON(F_EXPUNGE_STAYOPENS
, ps_global
)))
723 expunge_and_close(ps_global
->mail_stream
, NULL
,
724 sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)
725 ? EC_NO_CLOSE
: EC_NONE
);
726 else if(!sp_flagged(ps_global
->mail_stream
, SP_INBOX
)
727 && sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)){
729 * We want to save our position in the folder so that when we
730 * come back to this folder again, we can place the cursor on
731 * a reasonable message number.
734 sp_set_saved_cur_msg_id(ps_global
->mail_stream
, NULL
);
736 if(ps_global
->mail_stream
->nmsgs
> 0L){
737 cur
= mn_get_cur(sp_msgmap(ps_global
->mail_stream
));
738 raw
= mn_m2raw(sp_msgmap(ps_global
->mail_stream
), cur
);
739 if(raw
> 0L && raw
<= ps_global
->mail_stream
->nmsgs
)
740 env
= pine_mail_fetchstructure(ps_global
->mail_stream
,
743 if(env
&& env
->message_id
&& env
->message_id
[0])
744 sp_set_saved_cur_msg_id(ps_global
->mail_stream
,
749 ps_global
->mail_stream
= NULL
;
752 snprintf(status_msg
, sizeof(status_msg
), "%sOpening \"", do_reopen
? "Re-" : "");
753 fname
= folder_name_decoded((unsigned char *)newfolder
);
754 strncat(status_msg
, pretty_fn(fname
? (char *) fname
: newfolder
),
755 sizeof(status_msg
)-strlen(status_msg
) - 2);
756 if(fname
) fs_give((void **)&fname
);
757 status_msg
[sizeof(status_msg
)-2] = '\0';
758 strncat(status_msg
, "\"", sizeof(status_msg
)-strlen(status_msg
) - 1);
759 status_msg
[sizeof(status_msg
)-1] = '\0';
760 we_cancel
= busy_cue(status_msg
, NULL
, 0);
763 * if requested, make access to folder readonly (only once)
765 if(ps_global
->open_readonly_on_startup
){
766 openmode
|= OP_READONLY
;
767 ps_global
->open_readonly_on_startup
= 0;
770 if(!(flags
& DB_NOVISIT
))
771 ps_global
->first_open_was_attempted
= 1;
773 openmode
|= SP_USEPOOL
;
776 sp_set_first_unseen(stream
, 0L);
778 /* in case we closed the old stream by cancelling the connection, do
779 * not let that interfere with opening the new stream.
781 ps_global
->user_says_cancel
= 0;
783 m
= context_open((new_context
&& !open_inbox
) ? new_context
: NULL
,
785 open_inbox
? ps_global
->VAR_INBOX_PATH
: expanded_file
,
786 openmode
| (open_inbox
? SP_INBOX
: 0),
790 && ps_global
->in_xoauth2_auth
791 && ps_global
->preserve_password
){
792 ps_global
->in_xoauth2_auth
= ps_global
->user_says_cancel
= ps_global
->preserve_password
= 0;
793 m
= context_open((new_context
&& !open_inbox
) ? new_context
: NULL
,
795 open_inbox
? ps_global
->VAR_INBOX_PATH
: expanded_file
,
796 openmode
| (open_inbox
? SP_INBOX
: 0),
801 * We aren't in a situation where we want a single cancel to
802 * apply to multiple opens.
804 ps_global
->user_says_cancel
= 0;
806 /* we are not in authentication mode anymore */
807 ps_global
->preserve_password
= ps_global
->in_xoauth2_auth
= 0;
813 dprint((8, "Opened folder %p \"%s\" (context: \"%s\")\n",
814 m
, (m
&& m
->mailbox
) ? m
->mailbox
: "nil",
815 (new_context
&& new_context
->context
)
816 ? new_context
->context
: "nil"));
819 /* Can get m != NULL if correct passwd for remote, but wrong name */
820 if(m
== NULL
|| m
->halfopen
){
821 /*-- non-existent local mailbox, or wrong passwd for remote mailbox--*/
822 /* fall back to currently open mailbox */
826 ps_global
->mail_stream
= NULL
;
828 pine_mail_actually_close(m
);
831 dprint((8, "Old folder: \"%s\"\n",
832 old_folder
== NULL
? "" : old_folder
));
833 if(old_folder
!= NULL
){
834 if(strucmp(old_folder
, ps_global
->inbox_name
) == 0){
835 ps_global
->mail_stream
= sp_inbox_stream();
836 ps_global
->msgmap
= sp_msgmap(ps_global
->mail_stream
);
838 dprint((8, "Reactivate inbox %ld %ld %p\n",
839 mn_get_cur(ps_global
->msgmap
),
840 mn_get_total(ps_global
->msgmap
),
841 ps_global
->mail_stream
));
842 p
= sp_fldr(ps_global
->mail_stream
)
843 ? sp_fldr(ps_global
->mail_stream
)
844 : ps_global
->inbox_name
;
845 strncpy(ps_global
->cur_folder
, p
,
846 sizeof(ps_global
->cur_folder
)-1);
847 ps_global
->cur_folder
[sizeof(ps_global
->cur_folder
)-1] = '\0';
850 ps_global
->mail_stream
= pine_mail_open(NULL
, old_path
,
852 /* mm_log will take care of error message here */
853 if(ps_global
->mail_stream
== NULL
){
857 ps_global
->msgmap
= sp_msgmap(ps_global
->mail_stream
);
858 mn_set_sort(ps_global
->msgmap
, old_sort
);
859 mn_set_revsort(ps_global
->msgmap
, old_tros
);
860 ps_global
->mangled_header
= 1;
861 reset_index_format();
862 clear_index_cache(ps_global
->mail_stream
, 0);
864 if(!(rflags
& SP_MATCH
)){
865 sp_set_expunge_count(ps_global
->mail_stream
, 0L);
866 sp_set_new_mail_count(ps_global
->mail_stream
, 0L);
867 sp_set_dead_stream(ps_global
->mail_stream
, 0);
868 sp_set_noticed_dead_stream(ps_global
->mail_stream
, 0);
870 reset_check_point(ps_global
->mail_stream
);
871 if(IS_NEWS(ps_global
->mail_stream
)
872 && ps_global
->mail_stream
->rdonly
)
873 msgno_exclude_deleted(ps_global
->mail_stream
,
874 sp_msgmap(ps_global
->mail_stream
),
877 if(mn_get_total(ps_global
->msgmap
) > 0)
878 mn_set_cur(ps_global
->msgmap
,
879 first_sorted_flagged(F_NONE
,
880 ps_global
->mail_stream
,
883 ? 0 : FSF_SKIP_CHID
));
885 if(!(mn_get_sort(ps_global
->msgmap
) == SortArrival
886 && !mn_get_revsort(ps_global
->msgmap
)))
887 refresh_sort(ps_global
->mail_stream
,
888 ps_global
->msgmap
, SRT_NON
);
891 fname
= folder_name_decoded((unsigned char *)old_folder
);
892 q_status_message1(SM_ORDER
, 0, 3,
893 "Folder \"%s\" reopened", fname
? (char *)fname
: old_folder
);
895 fs_give((void **)&fname
);
900 mn_set_cur(ps_global
->msgmap
,
901 MIN(mn_get_cur(ps_global
->msgmap
),
902 mn_get_total(ps_global
->msgmap
)));
904 fs_give((void **)&old_folder
);
905 fs_give((void **)&old_path
);
911 q_status_message(SM_ORDER
| SM_DING
, 0, 4, _("No folder opened"));
912 mn_set_total(ps_global
->msgmap
, 0L);
913 mn_set_nmsgs(ps_global
->msgmap
, 0L);
914 mn_set_cur(ps_global
->msgmap
, -1L);
915 ps_global
->cur_folder
[0] = '\0';
918 if(was_dead
&& !sp_a_locked_stream_is_dead() && pith_opt_icon_text
)
919 (*pith_opt_icon_text
)(NULL
, IT_MCLOSED
);
921 if(ps_global
->mail_stream
&& !(flags
& DB_NOVISIT
))
922 sp_set_recent_since_visited(ps_global
->mail_stream
, 0L);
924 if(ps_global
->mail_stream
)
925 sp_set_first_unseen(ps_global
->mail_stream
, 0L);
930 if(old_folder
!= NULL
){
931 fs_give((void **)&old_folder
);
932 fs_give((void **)&old_path
);
936 update_folder_unseen_by_stream(m
, UFU_NONE
);
938 /*----- success in opening the new folder ----*/
939 dprint((2, "Opened folder \"%s\" with %ld messages\n",
940 m
->mailbox
? m
->mailbox
: "?", m
->nmsgs
));
943 /*--- A Little house keeping ---*/
945 ps_global
->mail_stream
= m
;
946 if(!(flags
& DB_NOVISIT
))
947 sp_set_recent_since_visited(ps_global
->mail_stream
, 0L);
949 ps_global
->msgmap
= sp_msgmap(m
);
950 if(!(rflags
& SP_MATCH
)){
951 sp_set_expunge_count(m
, 0L);
952 sp_set_new_mail_count(m
, 0L);
953 sp_set_dead_stream(m
, 0);
954 sp_set_noticed_dead_stream(m
, 0);
955 sp_set_mail_box_changed(m
, 0);
956 reset_check_point(m
);
959 if(was_dead
&& !sp_a_locked_stream_is_dead() && pith_opt_icon_text
)
960 (*pith_opt_icon_text
)(NULL
, IT_MCLOSED
);
962 ps_global
->last_unambig_folder
[0] = '\0';
965 * remember old folder and context...
967 if(context_isambig(ps_global
->cur_folder
)){
968 ps_global
->context_last
= ps_global
->context_current
;
969 snprintf(ps_global
->context_current
->last_folder
,
970 sizeof(ps_global
->context_current
->last_folder
),
971 "%.*s", (int) sizeof(ps_global
->context_current
->last_folder
) - 1, ps_global
->cur_folder
);
972 ps_global
->last_unambig_folder
[0] = '\0';
975 ps_global
->context_last
= NULL
;
976 snprintf(ps_global
->last_unambig_folder
,
977 sizeof(ps_global
->last_unambig_folder
),
978 "%s", ps_global
->cur_folder
);
981 /* folder in a subdir of context? */
982 if(ps_global
->context_current
->dir
->prev
)
983 snprintf(ps_global
->cur_folder
, sizeof(ps_global
->cur_folder
), "%s%s",
984 ps_global
->context_current
->dir
->ref
, newfolder
);
986 strncpy(ps_global
->cur_folder
,
987 (open_inbox
) ? ps_global
->inbox_name
: newfolder
,
988 sizeof(ps_global
->cur_folder
)-1);
989 ps_global
->cur_folder
[sizeof(ps_global
->cur_folder
)-1] = '\0';
992 sp_set_fldr(ps_global
->mail_stream
, ps_global
->cur_folder
);
995 ps_global
->context_last
= ps_global
->context_current
;
996 ps_global
->context_current
= new_context
;
999 sp_set_context(ps_global
->mail_stream
, ps_global
->context_current
);
1002 clear_index_cache(ps_global
->mail_stream
, 0);
1003 reset_index_format();
1006 * Start news reading with messages the user's marked deleted
1007 * hidden from view...
1009 if(IS_NEWS(ps_global
->mail_stream
) && ps_global
->mail_stream
->rdonly
)
1010 msgno_exclude_deleted(ps_global
->mail_stream
, ps_global
->msgmap
, NULL
);
1012 if(we_cancel
&& F_OFF(F_QUELL_FILTER_MSGS
, ps_global
))
1016 * If the stream we got from the open above was already opened earlier
1017 * for some temporary use, then it wouldn't have been filtered. That's
1018 * why we need this flag, so that we will filter if needed.
1020 if(!sp_flagged(ps_global
->mail_stream
, SP_FILTERED
))
1021 process_filter_patterns(ps_global
->mail_stream
, ps_global
->msgmap
, 0L);
1024 * If no filtering messages wait until here to cancel the busy cue
1025 * because the user will be waiting for that filtering with nothing
1026 * showing the activity otherwise.
1028 if(we_cancel
&& F_ON(F_QUELL_FILTER_MSGS
, ps_global
))
1031 if(!(rflags
& SP_MATCH
) || !(rflags
& SP_LOCKED
))
1032 reset_sort_order(SRT_VRB
);
1033 else if(sp_new_mail_count(ps_global
->mail_stream
) > 0L
1034 || sp_unsorted_newmail(ps_global
->mail_stream
)
1035 || sp_need_to_rethread(ps_global
->mail_stream
))
1036 refresh_sort(ps_global
->mail_stream
, ps_global
->msgmap
, SRT_NON
);
1038 report
= new_messages_string(ps_global
->mail_stream
);
1039 fname
= folder_name_decoded((unsigned char *)newfolder
);
1040 q_status_message7(SM_ORDER
, 0, 4,
1041 "%s \"%s\" opened with %s message%s%s%s%s",
1042 IS_NEWS(ps_global
->mail_stream
)
1043 ? "News group" : "Folder",
1044 open_inbox
? pretty_fn(fname
? (char *) fname
: newfolder
)
1045 : (fname
? (char *)fname
: newfolder
),
1046 comatose(mn_get_total(ps_global
->msgmap
)),
1047 plural(mn_get_total(ps_global
->msgmap
)),
1049 && sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
))
1050 ? " (StayOpen)" : "",
1051 READONLY_FOLDER(ps_global
->mail_stream
)
1053 report
? report
: "");
1056 fs_give((void **)&fname
);
1059 fs_give((void **)&report
);
1062 mswin_settitle(pretty_fn(newfolder
));
1065 * Set current message number when re-opening Stay-Open or
1068 if(rflags
& SP_MATCH
){
1069 if(rflags
& SP_LOCKED
){
1070 if(F_OFF(F_STARTUP_STAYOPEN
, ps_global
)
1071 && (cur
= get_msgno_by_msg_id(ps_global
->mail_stream
,
1072 sp_saved_cur_msg_id(ps_global
->mail_stream
),
1073 ps_global
->msgmap
)) >= 1L
1074 && cur
<= mn_get_total(ps_global
->msgmap
)){
1076 mn_set_cur(ps_global
->msgmap
, (MsgNo
) cur
);
1077 if(flags
& DB_FROMTAB
){
1079 * When we TAB to a folder that is a StayOpen folder we try
1080 * to increment the current message # by one instead of doing
1081 * some search again. Some people probably won't like this
1082 * behavior, especially if the new message that has arrived
1083 * comes before where we are in the index. That's why we have
1084 * the F_STARTUP_STAYOPEN feature above.
1086 mn_inc_cur(m
, ps_global
->msgmap
, MH_NONE
);
1088 /* else leave it where it is */
1090 adjust_cur_to_visible(ps_global
->mail_stream
, ps_global
->msgmap
);
1095 * If we're reopening a cached open stream that wasn't explicitly
1096 * kept open by the user, then the user expects it to act pretty
1097 * much like we are re-opening the stream. A problem is that the
1098 * recent messages are still recent because we haven't closed the
1099 * stream, so we fake a quasi-recentness by remembering the last
1100 * uid assigned on the stream when we pine_mail_close. Then when
1101 * we come back messages with uids higher than that are recent.
1103 * If uid_validity has changed, then we don't use any special
1104 * treatment, but just do the regular search.
1106 if(m
->uid_validity
== sp_saved_uid_validity(m
)){
1110 * Because first_sorted_flagged uses sequence numbers, find the
1111 * sequence number of the first message after the old last
1112 * uid assigned. I.e., the first recent message.
1114 for(i
= m
->nmsgs
; i
> 0L; i
--)
1115 if(mail_uid(m
, i
) <= sp_saved_uid_last(m
))
1118 if(i
> 0L && i
< m
->nmsgs
)
1125 if(!cur_already_set
&& mn_get_total(ps_global
->msgmap
) > 0L){
1127 perfolder_startup_rule
= reset_startup_rule(ps_global
->mail_stream
);
1129 if(ps_global
->start_entry
> 0){
1130 mn_set_cur(ps_global
->msgmap
, mn_get_revsort(ps_global
->msgmap
)
1131 ? first_sorted_flagged(F_NONE
, m
,
1132 ps_global
->start_entry
,
1133 THREADING() ? 0 : FSF_SKIP_CHID
)
1134 : first_sorted_flagged(F_SRCHBACK
, m
,
1135 ps_global
->start_entry
,
1136 THREADING() ? 0 : FSF_SKIP_CHID
));
1137 ps_global
->start_entry
= 0;
1139 else if(perfolder_startup_rule
!= IS_NOTSET
||
1141 ps_global
->context_current
->use
& CNTXT_INCMNG
){
1142 unsigned use_this_startup_rule
;
1144 if(perfolder_startup_rule
!= IS_NOTSET
)
1145 use_this_startup_rule
= perfolder_startup_rule
;
1147 use_this_startup_rule
= ps_global
->inc_startup_rule
;
1149 switch(use_this_startup_rule
){
1151 * For news in incoming collection we're doing the same thing
1152 * for first-unseen and first-recent. In both those cases you
1153 * get first-unseen if FAKE_NEW is off and first-recent if
1154 * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the
1155 * same as first recent because all recent msgs are unseen
1156 * and all unrecent msgs are seen (see pine_mail_open).
1158 case IS_FIRST_UNSEEN
:
1160 mn_set_cur(ps_global
->msgmap
,
1162 && mn_get_sort(ps_global
->msgmap
) == SortArrival
1163 && !mn_get_revsort(ps_global
->msgmap
)
1164 && !get_lflag(ps_global
->mail_stream
, NULL
,
1165 sp_first_unseen(m
), MN_EXLD
)
1166 && (n
= mn_raw2m(ps_global
->msgmap
,
1167 sp_first_unseen(m
))))
1169 : first_sorted_flagged(F_UNSEEN
| F_UNDEL
, m
, pc
,
1170 THREADING() ? 0 : FSF_SKIP_CHID
));
1173 case IS_FIRST_RECENT
:
1176 * We could really use recent for news but this is the way
1177 * it has always worked, so we'll leave it. That is, if
1178 * the FAKE_NEW feature is on, recent and unseen are
1179 * equivalent, so it doesn't matter. If the feature isn't
1180 * on, all the undeleted messages are unseen and we start
1181 * at the first one. User controls with the FAKE_NEW feature.
1183 if(IS_NEWS(ps_global
->mail_stream
)){
1184 mn_set_cur(ps_global
->msgmap
,
1185 first_sorted_flagged(F_UNSEEN
|F_UNDEL
, m
, pc
,
1186 THREADING() ? 0 : FSF_SKIP_CHID
));
1189 mn_set_cur(ps_global
->msgmap
,
1190 first_sorted_flagged(F_RECENT
| F_UNSEEN
1193 THREADING() ? 0 : FSF_SKIP_CHID
));
1197 case IS_FIRST_IMPORTANT
:
1198 mn_set_cur(ps_global
->msgmap
,
1199 first_sorted_flagged(F_FLAG
|F_UNDEL
, m
, pc
,
1200 THREADING() ? 0 : FSF_SKIP_CHID
));
1203 case IS_FIRST_IMPORTANT_OR_UNSEEN
:
1205 if(IS_NEWS(ps_global
->mail_stream
))
1209 MsgNo flagged
, first_unseen
;
1211 flagged
= first_sorted_flagged(F_FLAG
|F_UNDEL
, m
, pc
,
1212 THREADING() ? 0 : FSF_SKIP_CHID
);
1213 first_unseen
= (sp_first_unseen(m
)
1214 && mn_get_sort(ps_global
->msgmap
) == SortArrival
1215 && !mn_get_revsort(ps_global
->msgmap
)
1216 && !get_lflag(ps_global
->mail_stream
, NULL
,
1217 sp_first_unseen(m
), MN_EXLD
)
1218 && (n
= mn_raw2m(ps_global
->msgmap
,
1219 sp_first_unseen(m
))))
1221 : first_sorted_flagged(F_UNSEEN
|F_UNDEL
, m
, pc
,
1222 THREADING() ? 0 : FSF_SKIP_CHID
);
1223 mn_set_cur(ps_global
->msgmap
,
1224 (MsgNo
) MIN((int) flagged
, (int) first_unseen
));
1230 case IS_FIRST_IMPORTANT_OR_RECENT
:
1232 if(IS_NEWS(ps_global
->mail_stream
))
1236 MsgNo flagged
, first_recent
;
1238 flagged
= first_sorted_flagged(F_FLAG
|F_UNDEL
, m
, pc
,
1239 THREADING() ? 0 : FSF_SKIP_CHID
);
1240 first_recent
= first_sorted_flagged(F_RECENT
| F_UNSEEN
1243 THREADING() ? 0 : FSF_SKIP_CHID
);
1244 mn_set_cur(ps_global
->msgmap
,
1245 (MsgNo
) MIN((int) flagged
, (int) first_recent
));
1251 mn_set_cur(ps_global
->msgmap
,
1252 first_sorted_flagged(F_UNDEL
, m
, pc
,
1253 THREADING() ? 0 : FSF_SKIP_CHID
));
1257 mn_set_cur(ps_global
->msgmap
,
1258 first_sorted_flagged(F_UNDEL
, m
, pc
,
1259 FSF_LAST
| (THREADING() ? 0 : FSF_SKIP_CHID
)));
1263 alpine_panic("Unexpected incoming startup case");
1268 else if(IS_NEWS(ps_global
->mail_stream
)){
1270 * This will go to two different places depending on the FAKE_NEW
1271 * feature (see pine_mail_open).
1273 mn_set_cur(ps_global
->msgmap
,
1274 first_sorted_flagged(F_UNSEEN
|F_UNDEL
, m
, pc
,
1275 THREADING() ? 0 : FSF_SKIP_CHID
));
1278 mn_set_cur(ps_global
->msgmap
,
1279 mn_get_revsort(ps_global
->msgmap
)
1281 : mn_get_total(ps_global
->msgmap
));
1284 adjust_cur_to_visible(ps_global
->mail_stream
, ps_global
->msgmap
);
1286 else if(!(rflags
& SP_MATCH
)){
1287 mn_set_cur(ps_global
->msgmap
, -1L);
1290 if(ps_global
->mail_stream
)
1291 sp_set_first_unseen(ps_global
->mail_stream
, 0L);
1297 /*----------------------------------------------------------------------
1298 Expand a folder name, taking account of the folders_dir and
1299 any home directory reference
1301 Args: filename -- The name of the file that is the folder
1303 Result: The folder name is expanded in place.
1304 Returns 0 and queues status message if unsuccessful.
1305 Input string is overwritten with expanded name.
1306 Returns 1 if successful.
1310 expand_foldername(char *filename
, size_t len
)
1312 char temp_filename
[MAXPATH
+1];
1314 dprint((5, "=== expand_foldername called (%s) ===\n",
1315 filename
? filename
: "?"));
1318 * We used to check for valid filename chars here if "filename"
1319 * didn't refer to a remote mailbox. This has been rethought
1322 strncpy(temp_filename
, filename
, sizeof(temp_filename
)-1);
1323 temp_filename
[sizeof(temp_filename
)-1] = '\0';
1324 if(strucmp(temp_filename
, "inbox") == 0) {
1325 strncpy(filename
, ps_global
->VAR_INBOX_PATH
== NULL
? "inbox" :
1326 ps_global
->VAR_INBOX_PATH
, len
-1);
1327 filename
[len
-1] = '\0';
1328 } else if(temp_filename
[0] == '{') {
1329 strncpy(filename
, temp_filename
, len
-1);
1330 filename
[len
-1] = '\0';
1331 } else if(ps_global
->restricted
&& filename_is_restricted(temp_filename
)){
1332 q_status_message(SM_ORDER
, 0, 3, "Can only open local folders");
1334 } else if(temp_filename
[0] == '*') {
1335 strncpy(filename
, temp_filename
, len
-1);
1336 filename
[len
-1] = '\0';
1337 } else if(ps_global
->VAR_OPER_DIR
&& filename_parent_ref(temp_filename
)){
1338 q_status_message(SM_ORDER
, 0, 3,
1339 "\"..\" not allowed in folder name");
1341 } else if (is_homedir_path(temp_filename
)){
1342 if(fnexpand(temp_filename
, sizeof(temp_filename
)) == NULL
) {
1343 q_status_message1(SM_ORDER
, 3, 3, "Error expanding folder \"%s\"", temp_filename
);
1346 strncpy(filename
, temp_filename
, len
-1);
1347 filename
[len
-1] = '\0';
1348 } else if(F_ON(F_USE_CURRENT_DIR
, ps_global
) || is_absolute_path(temp_filename
)){
1349 strncpy(filename
, temp_filename
, len
-1);
1350 filename
[len
-1] = '\0';
1351 } else if(ps_global
->VAR_OPER_DIR
){
1352 build_path(filename
, ps_global
->VAR_OPER_DIR
, temp_filename
, len
);
1354 build_path(filename
,
1356 ps_global
->folders_dir
,
1358 ps_global
->home_dir
,
1360 temp_filename
, len
);
1363 dprint((5, "returning \"%s\"\n", filename
? filename
: "?"));
1369 /*----------------------------------------------------------------------
1370 Expunge (if confirmed) and close a mail stream
1372 Args: stream -- The MAILSTREAM * to close
1373 final_msg -- If non-null, this should be set to point to a
1374 message to print out in the caller, it is allocated
1375 here and freed by the caller.
1377 Result: Mail box is expunged and closed. A message is displayed to
1381 expunge_and_close(MAILSTREAM
*stream
, char **final_msg
, long unsigned int flags
)
1383 long i
, delete_count
, seen_not_del
;
1384 char buff1
[MAX_SCREEN_COLS
+1], *moved_msg
= NULL
,
1385 buff2
[MAX_SCREEN_COLS
+1], *folder
;
1387 struct variable
*vars
= ps_global
->vars
;
1388 int ret
, expunge
= FALSE
, no_close
= 0;
1391 no_close
= (flags
& EC_NO_CLOSE
);
1393 if(!(stream
&& sp_flagged(stream
, SP_LOCKED
)))
1396 /* check for dead stream */
1397 if(stream
&& sp_dead_stream(stream
)){
1398 pine_mail_actually_close(stream
);
1403 context
= sp_context(stream
);
1404 folder
= STREAMNAME(stream
);
1406 dprint((2, "expunge_and_close: \"%s\"%s\n",
1407 folder
, no_close
? " (NO_CLOSE bit set)" : ""));
1409 update_folder_unseen_by_stream(stream
, UFU_NONE
);
1412 strncpy(ing
, "ed", sizeof(ing
));
1414 strncpy(ing
, "ing", sizeof(ing
));
1416 ing
[sizeof(ing
)-1] = '\0';
1421 if(!stream
->rdonly
){
1423 if(pith_opt_begin_closing
)
1424 (*pith_opt_begin_closing
)(flags
, folder
);
1426 mail_expunge_prefilter(stream
, MI_CLOSING
);
1429 * Be sure to expunge any excluded (filtered) msgs
1430 * Do it here so they're not copied into read/archived
1431 * folders *AND* to be sure we don't refilter them
1432 * next time the folder's opened.
1434 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
1435 if(get_lflag(stream
, NULL
, i
, MN_EXLD
)){ /* if there are any */
1436 delete_filtered_msgs(stream
); /* delete them all */
1441 /* Save read messages? */
1442 if(VAR_READ_MESSAGE_FOLDER
&& VAR_READ_MESSAGE_FOLDER
[0]
1443 && sp_flagged(stream
, SP_INBOX
)
1444 && (seen_not_del
= count_flagged(stream
, F_SEEN
| F_UNDEL
))){
1446 if(F_ON(F_AUTO_READ_MSGS
,ps_global
)
1447 || (pith_opt_read_msg_prompt
1448 && (*pith_opt_read_msg_prompt
)(seen_not_del
, VAR_READ_MESSAGE_FOLDER
)))
1449 /* move inbox's read messages */
1450 moved_msg
= move_read_msgs(stream
, VAR_READ_MESSAGE_FOLDER
,
1451 buff1
, sizeof(buff1
), -1L);
1453 else if(VAR_ARCHIVED_FOLDERS
)
1454 moved_msg
= move_read_incoming(stream
, context
, folder
,
1455 VAR_ARCHIVED_FOLDERS
,
1456 buff1
, sizeof(buff1
));
1459 * We need the count_flagged to be executed not only to set
1460 * delete_count, but also to set the searched bits in all of
1461 * the deleted messages. The searched bit is used in the monkey
1462 * business section below which undeletes deleted messages
1463 * before expunging. It determines which messages are deleted
1464 * by examining the searched bit, which had better be set or not
1465 * based on this count_flagged call rather than some random
1466 * search that happened earlier.
1468 delete_count
= count_flagged(stream
, F_DEL
);
1469 if(F_ON(F_EXPUNGE_MANUALLY
,ps_global
))
1473 if(!ps_global
->noexpunge_on_close
&& delete_count
){
1475 if(F_ON(F_FULL_AUTO_EXPUNGE
,ps_global
)
1476 || (F_ON(F_AUTO_EXPUNGE
, ps_global
)
1477 && ((!strucmp(folder
,ps_global
->inbox_name
))
1478 || (context
&& (context
->use
& CNTXT_INCMNG
)))
1479 && context_isambig(folder
))){
1482 else if(pith_opt_expunge_prompt
){
1483 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
1484 ret
= (*pith_opt_expunge_prompt
)(stream
, pretty_fn((char *)fname
), delete_count
);
1485 if(fname
) fs_give((void **) &fname
);
1488 /* get this message back in queue */
1490 q_status_message(SM_ORDER
,
1491 F_ON(F_AUTO_READ_MSGS
,ps_global
) ? 0 : 3, 5, moved_msg
);
1495 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
1497 filtered
= any_lflagged(sp_msgmap(stream
), MN_EXLD
);
1499 snprintf(buff2
, sizeof(buff2
),
1500 "%s%s%s%.30s%s%s %s message%s and remov%s %s.",
1501 no_close
? "" : "Clos",
1502 no_close
? "" : ing
,
1503 no_close
? "" : " \"",
1504 no_close
? "" : pretty_fn((char *)fname
),
1505 no_close
? "" : "\". ",
1506 final_msg
? "Kept" : "Keeping",
1507 comatose(stream
->nmsgs
- filtered
- delete_count
),
1508 plural(stream
->nmsgs
- filtered
- delete_count
),
1510 long2string(delete_count
));
1511 if(fname
) fs_give((void **)&fname
);
1513 *final_msg
= cpystr(buff2
);
1515 q_status_message(SM_ORDER
,
1517 (F_ON(F_AUTO_EXPUNGE
,ps_global
)
1518 || F_ON(F_FULL_AUTO_EXPUNGE
,ps_global
))
1522 flush_status_messages(1);
1523 ps_global
->mm_log_error
= 0;
1524 ps_global
->expunge_in_progress
= 1;
1525 mail_expunge(stream
);
1526 ps_global
->expunge_in_progress
= 0;
1527 if(ps_global
->mm_log_error
&& final_msg
&& *final_msg
){
1528 fs_give((void **)final_msg
);
1535 if(!ps_global
->noexpunge_on_close
&& expunge
){
1541 * filtered message monkey business.
1543 * 1) light sequence bits for legit deleted msgs
1544 * and store marker in local extension
1545 * 2) clear their deleted flag
1546 * 3) perform expunge to removed filtered msgs
1547 * 4) restore deleted flags for legit msgs
1548 * based on local extension bit
1550 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
1551 if(!get_lflag(stream
, NULL
, i
, MN_EXLD
)
1552 && (((mc
= mail_elt(stream
, i
)) && mc
->valid
&& mc
->deleted
)
1553 || (mc
&& !mc
->valid
&& mc
->searched
))){
1555 expbits
= MSG_EX_DELETE
;
1556 msgno_exceptions(stream
, i
, "0", &expbits
, TRUE
);
1558 else if((mc
= mail_elt(stream
, i
)) != NULL
)
1561 if((seq
= build_sequence(stream
, NULL
, NULL
)) != NULL
){
1562 mail_flag(stream
, seq
, "\\DELETED", ST_SILENT
);
1563 fs_give((void **) &seq
);
1566 ps_global
->mm_log_error
= 0;
1567 ps_global
->expunge_in_progress
= 1;
1568 mail_expunge(stream
);
1569 ps_global
->expunge_in_progress
= 0;
1571 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
1572 if((mc
= mail_elt(stream
, i
)) != NULL
)
1574 = (msgno_exceptions(stream
, i
, "0", &expbits
, FALSE
)
1575 && (expbits
& MSG_EX_DELETE
));
1577 if((seq
= build_sequence(stream
, NULL
, NULL
)) != NULL
){
1578 mail_flag(stream
, seq
, "\\DELETED", ST_SET
|ST_SILENT
);
1579 fs_give((void **) &seq
);
1583 if(!no_close
){ /* MAX_SCREEN_COLS+1 = sizeof(buff2) */
1584 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
1586 snprintf(buff2
, sizeof(buff2
),
1587 "Clos%s folder \"%.*s\". %s%s%s message%s.",
1589 MAX_SCREEN_COLS
+1-50, pretty_fn((char *) fname
),
1590 final_msg
? "Kept" : "Keeping",
1591 (stream
->nmsgs
== 1L) ? " single" : " all ",
1592 (stream
->nmsgs
> 1L)
1593 ? comatose(stream
->nmsgs
) : "",
1594 plural(stream
->nmsgs
));
1596 else{ /* MAX_SCREEN_COLS+1 = sizeof(buff2) */
1597 snprintf(buff2
, sizeof(buff2
), "Clos%s empty folder \"%.*s\"",
1598 ing
, MAX_SCREEN_COLS
+1-50, pretty_fn((char *) fname
));
1600 if(fname
) fs_give((void **)&fname
);
1603 *final_msg
= cpystr(buff2
);
1605 q_status_message(SM_ORDER
, 0, 3, buff2
);
1610 if(IS_NEWS(stream
)){
1612 * Mark the filtered messages deleted so they aren't
1613 * filtered next time.
1615 for(i
= 1L; i
<= stream
->nmsgs
; i
++){
1617 if(msgno_exceptions(stream
, i
, "0" , &exbits
, FALSE
)
1618 && (exbits
& MSG_EX_FILTERED
)){
1619 delete_filtered_msgs(stream
);
1623 /* first, look to archive read messages */
1624 if((moved_msg
= move_read_incoming(stream
, context
, folder
,
1625 VAR_ARCHIVED_FOLDERS
,
1626 buff1
, sizeof(buff1
))) != NULL
)
1627 q_status_message(SM_ORDER
,
1628 F_ON(F_AUTO_READ_MSGS
,ps_global
) ? 0 : 3, 5, moved_msg
);
1629 /* MAX_SCREEN_COLS+1 = sizeof(buff2) */
1630 snprintf(buff2
, sizeof(buff2
), "Clos%s news group \"%.*s\"",
1631 ing
, MAX_SCREEN_COLS
+1-50, pretty_fn(folder
));
1633 if(F_ON(F_NEWS_CATCHUP
, ps_global
)){
1636 /* count visible messages */
1637 (void) count_flagged(stream
, F_DEL
);
1638 for(i
= 1L, delete_count
= 0L; i
<= stream
->nmsgs
; i
++)
1639 if(!(get_lflag(stream
, NULL
, i
, MN_EXLD
)
1640 || ((mc
= mail_elt(stream
, i
)) && mc
->valid
1642 || (mc
&& !mc
->valid
&& mc
->searched
)))
1645 if(delete_count
&& pith_opt_expunge_prompt
){
1646 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
1647 ret
= (*pith_opt_expunge_prompt
)(stream
, pretty_fn((char *) fname
), delete_count
);
1648 if(fname
) fs_give((void **)&fname
);
1652 snprintf(seq
, sizeof(seq
), "1:%ld", stream
->nmsgs
);
1653 mail_flag(stream
, seq
, "\\DELETED", ST_SET
|ST_SILENT
);
1658 if(F_ON(F_NEWS_CROSS_DELETE
, ps_global
))
1659 cross_delete_crossposts(stream
);
1661 else{ /* MAX_SCREEN_COLS+1 = sizeof(buff2) */
1662 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
1663 snprintf(buff2
, sizeof(buff2
),
1664 "Clos%s read-only folder \"%.*s\". No changes to save",
1665 ing
, MAX_SCREEN_COLS
+1-60, pretty_fn((char *) fname
));
1666 if(fname
) fs_give((void **)&fname
);
1670 *final_msg
= cpystr(buff2
);
1672 q_status_message(SM_ORDER
, 0, 2, buff2
);
1676 * Make darn sure any mm_log fallout caused above gets seen...
1679 flush_status_messages(1);
1680 pine_mail_close(stream
);
1687 agg_select_all(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int *diff
, int on
)
1690 int hidden
= any_lflagged(msgmap
, MN_HIDE
) > 0L;
1692 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++){
1693 if(on
){ /* mark 'em all */
1694 set_lflag(stream
, msgmap
, i
, MN_SLCT
, 1);
1696 else { /* unmark 'em all */
1697 if(get_lflag(stream
, msgmap
, i
, MN_SLCT
)){
1701 set_lflag(stream
, msgmap
, i
, MN_SLCT
, 0);
1704 set_lflag(stream
, msgmap
, i
, MN_HIDE
, 0);
1710 /*----------------------------------------------------------------------
1711 Move all read messages from srcfldr to dstfldr
1713 Args: stream -- stream to usr
1714 dstfldr -- folder to receive moved messages
1715 buf -- place to write success message
1717 Returns: success message or NULL for failure
1720 move_read_msgs(MAILSTREAM
*stream
, char *dstfldr
, char *buf
, size_t buflen
, long int searched
)
1724 MSGNO_S
*msgmap
= NULL
;
1725 CONTEXT_S
*save_context
= NULL
;
1729 if(!is_absolute_path(dstfldr
)
1730 && !(save_context
= default_save_context(ps_global
->context_list
)))
1731 save_context
= ps_global
->context_list
;
1734 * Use the "searched" bit to select the set of messages
1735 * we want to save. If searched is non-neg, the message
1736 * cache already has the necessary "searched" bits set.
1739 searched
= count_flagged(stream
, F_SEEN
| F_UNDEL
);
1743 * We're going to be messing with SLCT flags in order
1744 * to do our work. If this stream is a StayOpen stream
1745 * we want to restore those flags after we're done
1746 * using them. So copy them into STMP so we can put them
1749 msgmap
= sp_msgmap(stream
);
1750 if(sp_flagged(stream
, SP_PERMLOCKED
))
1751 copy_lflags(stream
, msgmap
, MN_SLCT
, MN_STMP
);
1753 set_lflags(stream
, msgmap
, MN_SLCT
, 0);
1755 /* select search results */
1756 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
1757 if((raw
= mn_m2raw(msgmap
, i
)) > 0L && stream
1758 && raw
<= stream
->nmsgs
1759 && (mc
= mail_elt(stream
,raw
))
1760 && ((mc
->valid
&& mc
->seen
&& !mc
->deleted
)
1761 || (!mc
->valid
&& mc
->searched
)))
1762 set_lflag(stream
, msgmap
, i
, MN_SLCT
, 1);
1764 pseudo_selected(stream
, msgmap
);
1765 snprintf(buf
, buflen
, "Moving %s read message%s to \"%s\"",
1766 comatose(searched
), plural(searched
), dstfldr
);
1767 we_cancel
= busy_cue(buf
, NULL
, 0);
1768 if(save(ps_global
, stream
, save_context
, dstfldr
, msgmap
,
1769 SV_DELETE
| SV_FIX_DELS
| SV_INBOXWOCNTXT
) == searched
)
1770 strncpy(bufp
= buf
+ 1, "Moved", MIN(5,buflen
)); /* change Moving to Moved */
1772 buf
[buflen
-1] = '\0';
1774 cancel_busy_cue(bufp
? 0 : -1);
1776 if(sp_flagged(stream
, SP_PERMLOCKED
)){
1777 restore_selected(msgmap
);
1778 copy_lflags(stream
, msgmap
, MN_STMP
, MN_SLCT
);
1786 /*----------------------------------------------------------------------
1787 Move read messages from folder if listed in archive
1793 move_read_incoming(MAILSTREAM
*stream
, CONTEXT_S
*context
, char *folder
,
1794 char **archive
, char *buf
, size_t buflen
)
1796 char *s
, *d
, *f
= folder
;
1799 if(buf
&& buflen
> 0)
1802 if(archive
&& !sp_flagged(stream
, SP_INBOX
)
1803 && context
&& (context
->use
& CNTXT_INCMNG
)
1804 && ((context_isambig(folder
)
1805 && folder_is_nick(folder
, FOLDERS(context
), 0))
1806 || folder_index(folder
, context
, FI_FOLDER
) > 0)
1807 && (seen_undel
= count_flagged(stream
, F_SEEN
| F_UNDEL
))){
1809 for(; f
&& *archive
; archive
++){
1812 get_pair(*archive
, &s
, &d
, 1, 0);
1814 && (!strcmp(s
, folder
)
1815 || (context_isambig(folder
)
1816 && (p
= folder_is_nick(folder
, FOLDERS(context
), 0))
1817 && !strcmp(s
, p
)))){
1818 if(F_ON(F_AUTO_READ_MSGS
,ps_global
)
1819 || (pith_opt_read_msg_prompt
1820 && (*pith_opt_read_msg_prompt
)(seen_undel
, d
)))
1821 buf
= move_read_msgs(stream
, d
, buf
, buflen
, seen_undel
);
1823 f
= NULL
; /* bust out after cleaning up */
1826 fs_give((void **)&s
);
1827 fs_give((void **)&d
);
1831 return((buf
&& *buf
) ? buf
: NULL
);
1835 /*----------------------------------------------------------------------
1836 Delete all references to a deleted news posting
1841 cross_delete_crossposts(MAILSTREAM
*stream
)
1843 if(count_flagged(stream
, F_DEL
)){
1844 static char *fields
[] = {"Xref", NULL
};
1845 MAILSTREAM
*tstream
;
1846 CONTEXT_S
*fake_context
;
1847 char *xref
, *p
, *group
, *uidp
,
1848 *newgrp
, newfolder
[MAILTMPLEN
];
1849 long i
, hostlatch
= 0L;
1854 strncpy(newfolder
, stream
->mailbox
, sizeof(newfolder
));
1855 newfolder
[sizeof(newfolder
)-1] = '\0';
1856 if(!(newgrp
= strstr(newfolder
, "#news.")))
1857 return; /* weird mailbox */
1861 we_cancel
= busy_cue("Busy deleting crosspostings", NULL
, 1);
1863 /* build subscribed list */
1864 strncpy(newgrp
, "[]", sizeof(newfolder
)-(newgrp
-newfolder
));
1865 newfolder
[sizeof(newfolder
)-1] = '\0';
1866 fake_context
= new_context(newfolder
, 0);
1867 build_folder_list(NULL
, fake_context
, "*", NULL
, BFL_LSUB
);
1869 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
1870 if(!get_lflag(stream
, NULL
, i
, MN_EXLD
)
1871 && (mc
= mail_elt(stream
, i
)) && mc
->deleted
){
1873 if((xref
= pine_fetchheader_lines(stream
, i
, NULL
, fields
)) != NULL
){
1874 if((p
= strstr(xref
, ": ")) != NULL
){
1882 while(*++p
&& !isspace((unsigned char) *p
))
1888 /* tie off uid/host */
1894 * For the nonce, we're only deleting valid
1895 * uid's from outside the current newsgroup
1896 * and inside only subscribed newsgroups
1898 if(strcmp(group
, stream
->mailbox
1899 + (newgrp
- newfolder
))
1900 && folder_index(group
, fake_context
,
1902 if((uid
= strtoul(uidp
, NULL
, 10)) != 0L){
1903 strncpy(newgrp
, group
, sizeof(newfolder
)-(newgrp
-newfolder
));
1904 newfolder
[sizeof(newfolder
)-1] = '\0';
1905 if((tstream
= pine_mail_open(NULL
,
1909 mail_flag(tstream
, ulong2string(uid
),
1912 pine_mail_close(tstream
);
1916 break; /* bogus uid */
1919 else if(!hostlatch
++){
1922 if(stream
->mailbox
[0] == '{'
1923 && !((p
= strpbrk(stream
->mailbox
+1, "}:/"))
1924 && !struncmp(stream
->mailbox
+ 1,
1925 q
= canonical_name(group
),
1926 p
- (stream
->mailbox
+ 1))
1927 && q
[p
- (stream
->mailbox
+ 1)] == '\0'))
1928 break; /* different server? */
1931 break; /* bogus field! */
1935 fs_give((void **) &xref
);
1939 free_context(&fake_context
);
1948 * Original version from Eduardo Chappa.
1950 * Returns a string describing the number of new/unseen messages
1951 * for use in the status line. Can return NULL. Caller must free the memory.
1954 new_messages_string(MAILSTREAM
*stream
)
1956 char message
[80] = {'\0'};
1957 long new = 0L, uns
= 0L;
1958 int i
, imapstatus
= 0;
1960 for (i
= 0; ps_global
->index_disp_format
[i
].ctype
!= iNothing
1961 && ps_global
->index_disp_format
[i
].ctype
!= iIStatus
1962 && ps_global
->index_disp_format
[i
].ctype
!= iSIStatus
; i
++)
1965 imapstatus
= ps_global
->index_disp_format
[i
].ctype
== iIStatus
1966 || ps_global
->index_disp_format
[i
].ctype
== iSIStatus
;
1968 get_new_message_count(stream
, imapstatus
, &new, &uns
);
1971 snprintf(message
, sizeof(message
), " - %s%s%s%s%s%s%s",
1972 uns
!= 0L ? comatose((long) new) : "",
1973 uns
!= 0L ? " " : "",
1974 uns
!= 0L ? _("recent") : "",
1975 uns
> 0L ? ", " : "",
1976 uns
!= -1L ? comatose((long) uns
) : "",
1977 uns
!= -1L ? " " : "",
1978 uns
!= -1L ? _("unseen") : "");
1979 else if(!imapstatus
&& new > 0L)
1980 snprintf(message
, sizeof(message
), " - %s %s",
1981 comatose((long) new), _("new"));
1983 return(*message
? cpystr(message
) : NULL
);
1988 get_new_message_count(MAILSTREAM
*stream
, int imapstatus
,
1989 long *new, long *unseen
)
1999 *new = count_flagged(stream
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
2001 if(!IS_NEWS(stream
)){
2003 *unseen
= count_flagged(stream
, F_UNSEEN
| F_UNDEL
);
2009 if(IS_NEWS(stream
)){
2010 if(F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
)){
2012 *new = count_flagged(stream
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
2017 *new = count_flagged(stream
, F_UNSEEN
| F_UNDEL
| F_UNANS
);
2023 /*----------------------------------------------------------------------
2024 ZOOM the message index (set any and all necessary hidden flag bits)
2026 Args: state -- usual pine state
2027 msgmap -- usual message mapping
2028 Returns: number of messages zoomed in on
2032 zoom_index(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int onflag
)
2034 long i
, count
= 0L, first
= 0L, msgno
;
2035 PINETHRD_S
*thrd
= NULL
, *topthrd
= NULL
, *nthrd
;
2037 if(any_lflagged(msgmap
, onflag
)){
2039 if(THREADING() && sp_viewing_a_thread(stream
)){
2040 /* get top of current thread */
2041 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
2042 if(thrd
&& thrd
->top
)
2043 topthrd
= fetch_thread(stream
, thrd
->top
);
2046 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++){
2047 if(!get_lflag(stream
, msgmap
, i
, onflag
)){
2048 set_lflag(stream
, msgmap
, i
, MN_HIDE
, 1);
2053 * Because subject lines depend on whether or not
2054 * other parts of the thread above us are visible or not.
2056 if(THREADING() && !THRD_INDX()
2057 && ps_global
->thread_disp_style
== THREAD_MUTTLIKE
)
2058 clear_index_cache_ent(stream
, i
, 0);
2061 * If a selected message is hidden beneath a collapsed
2062 * thread (not beneath a thread index line, but a collapsed
2063 * thread or subthread) then we make it visible. The user
2064 * should be able to see the selected messages when they
2065 * Zoom. We could get a bit fancier and re-collapse the
2066 * thread when the user unzooms, but we don't do that
2069 if(THREADING() && !THRD_INDX()
2070 && get_lflag(stream
, msgmap
, i
, MN_CHID
)){
2073 * What we need to do is to unhide this message and
2074 * uncollapse any parent above us.
2075 * Also, when we uncollapse a parent, we need to
2076 * trace back down the tree and unhide until we get
2077 * to a collapse point or the end. That's what
2078 * set_thread_subtree does.
2081 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, i
));
2083 if(thrd
&& thrd
->parent
)
2084 thrd
= fetch_thread(stream
, thrd
->parent
);
2088 /* unhide and uncollapse its parents */
2090 /* if this parent is collapsed */
2091 if(get_lflag(stream
, NULL
, thrd
->rawno
, MN_COLL
)){
2092 /* uncollapse this parent and unhide its subtree */
2093 msgno
= mn_raw2m(msgmap
, thrd
->rawno
);
2094 if(msgno
> 0L && msgno
<= mn_get_total(msgmap
)){
2095 set_lflag(stream
, msgmap
, msgno
,
2096 MN_COLL
| MN_CHID
, 0);
2098 (nthrd
= fetch_thread(stream
, thrd
->next
)))
2099 set_thread_subtree(stream
, nthrd
, msgmap
,
2103 /* collapse symbol will be wrong */
2104 clear_index_cache_ent(stream
, msgno
, 0);
2108 * Continue up tree to next parent looking for
2109 * more collapse points.
2112 thrd
= fetch_thread(stream
, thrd
->parent
);
2121 /* find msgno of top of thread for msg i */
2122 if((thrd
=fetch_thread(stream
, mn_m2raw(msgmap
, i
)))
2124 first
= mn_raw2m(msgmap
, thrd
->top
);
2126 else if(THREADING() && sp_viewing_a_thread(stream
)){
2127 /* want first selected message in this thread */
2129 && (thrd
=fetch_thread(stream
, mn_m2raw(msgmap
, i
)))
2131 && topthrd
->rawno
== thrd
->top
)
2141 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
2142 if(count_lflags_in_thread(stream
, thrd
, msgmap
, onflag
) == 0)
2143 mn_set_cur(msgmap
, first
);
2145 else if((THREADING() && sp_viewing_a_thread(stream
))
2146 || !get_lflag(stream
, msgmap
, mn_get_cur(msgmap
), onflag
)){
2151 * Nothing was selected in the thread we were in, so
2152 * drop back to the Thread Index instead. Set the current
2153 * thread to the first one that has a selection in it.
2156 unview_thread(state
, stream
, msgmap
);
2158 i
= next_sorted_flagged(F_UNDEL
, stream
, 1L, &flags
);
2160 if(flags
& NSF_FLAG_MATCH
2161 && (thrd
=fetch_thread(stream
, mn_m2raw(msgmap
, i
)))
2163 first
= mn_raw2m(msgmap
, thrd
->top
);
2165 first
= 1L; /* can't happen */
2167 mn_set_cur(msgmap
, first
);
2170 if(msgline_hidden(stream
, msgmap
, mn_get_cur(msgmap
), 0))
2171 mn_set_cur(msgmap
, first
);
2181 /*----------------------------------------------------------------------
2182 UnZOOM the message index (clear any and all hidden flag bits)
2184 Args: state -- usual pine state
2185 msgmap -- usual message mapping
2186 Returns: 1 if hidden bits to clear and they were, 0 if none to clear
2190 unzoom_index(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
)
2194 if(!any_lflagged(msgmap
, MN_HIDE
))
2197 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
2198 set_lflag(stream
, msgmap
, i
, MN_HIDE
, 0);
2205 agg_text_select(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, char type
, char *namehdr
,
2206 int not, int check_for_my_addresses
,
2207 char *sstring
, char *charset
, SEARCHSET
**limitsrch
)
2209 int old_imap
, we_cancel
;
2210 int me_with_regex
= 0;
2212 SEARCHPGM
*srchpgm
, *pgm
, *secondpgm
= NULL
, *thirdpgm
= NULL
;
2213 SEARCHPGM
*mepgm
= NULL
;
2218 old_imap
= (is_imap_stream(stream
) && !modern_imap_stream(stream
));
2221 * Special case code for matching one of the user's addresses.
2223 if(check_for_my_addresses
){
2226 if(F_OFF(F_DISABLE_REGEX
, ps_global
)){
2227 for(t
= ps_global
->VAR_ALT_ADDRS
; !me_with_regex
&& t
&& t
[0] && t
[0][0]; t
++){
2229 if(contains_regex_special_chars(alt
))
2235 * In this case we can't use search because it doesn't support
2236 * regex. So we have to manually do the whole thing ourselves.
2237 * The searching is done in the subroutine and the searched bits
2238 * will be set on return.
2241 search_for_our_regex_addresses(stream
, type
, not, limitsrch
? *limitsrch
: NULL
);
2245 PATGRP_S
*patgrp
= NULL
;
2246 PATTERN_S
*p
= NULL
;
2247 PATTERN_S
*pattern
= NULL
, **nextp
;
2251 * We're going to use the pattern matching machinery to generate
2252 * a search program. We build a pattern whose only purpose is
2253 * to generate the program.
2257 /* add standard me addresses to list */
2258 if(ps_global
->VAR_USER_ID
){
2259 if(ps_global
->userdomain
&& ps_global
->userdomain
[0]){
2260 p
= (PATTERN_S
*) fs_get(sizeof(*p
));
2261 memset((void *) p
, 0, sizeof(*p
));
2262 snprintf(buf
, sizeof(buf
), "%s@%s", ps_global
->VAR_USER_ID
,
2263 ps_global
->userdomain
);
2264 p
->substring
= cpystr(buf
);
2269 if(!ps_global
->userdomain
&& ps_global
->localdomain
&& ps_global
->localdomain
[0]){
2270 p
= (PATTERN_S
*) fs_get(sizeof(*p
));
2271 memset((void *) p
, 0, sizeof(*p
));
2272 snprintf(buf
, sizeof(buf
), "%s@%s", ps_global
->VAR_USER_ID
,
2273 ps_global
->localdomain
);
2274 p
->substring
= cpystr(buf
);
2279 if(!ps_global
->userdomain
&& ps_global
->hostname
&& ps_global
->hostname
[0]){
2280 p
= (PATTERN_S
*) fs_get(sizeof(*p
));
2281 memset((void *) p
, 0, sizeof(*p
));
2282 snprintf(buf
, sizeof(buf
), "%s@%s", ps_global
->VAR_USER_ID
,
2283 ps_global
->hostname
);
2284 p
->substring
= cpystr(buf
);
2290 /* add user's alternate addresses */
2291 for(t
= ps_global
->VAR_ALT_ADDRS
; t
&& t
[0] && t
[0][0]; t
++){
2294 p
= (PATTERN_S
*) fs_get(sizeof(*p
));
2295 memset((void *) p
, 0, sizeof(*p
));
2296 p
->substring
= cpystr(alt
);
2302 patgrp
= (PATGRP_S
*) fs_get(sizeof(*patgrp
));
2303 memset((void *) patgrp
, 0, sizeof(*patgrp
));
2307 patgrp
->recip
= pattern
;
2310 patgrp
->partic
= pattern
;
2313 patgrp
->from
= pattern
;
2316 patgrp
->cc
= pattern
;
2319 patgrp
->to
= pattern
;
2322 q_status_message(SM_ORDER
, 3, 3, "Unhandled case in agg_text_select");
2326 mepgm
= match_pattern_srchpgm(patgrp
, stream
, NULL
);
2328 free_patgrp(&patgrp
);
2333 if(not && !old_imap
){
2334 srchpgm
= mail_newsearchpgm();
2335 srchpgm
->not = mail_newsearchpgmlist();
2336 srchpgm
->not->pgm
= mepgm
;
2344 /* create a search program and fill it in */
2345 srchpgm
= pgm
= mail_newsearchpgm();
2346 if(not && !old_imap
){
2347 srchpgm
->not = mail_newsearchpgmlist();
2348 srchpgm
->not->pgm
= mail_newsearchpgm();
2349 pgm
= srchpgm
->not->pgm
;
2355 case 'g' : /* X-GM-EXT-1 */
2356 pgm
->x_gm_ext1
= mail_newstringlist();
2357 pgm
->x_gm_ext1
->text
.data
= (unsigned char *) cpystr(namehdr
);
2358 pgm
->x_gm_ext1
->text
.size
= strlen(namehdr
);
2361 case 'h' : /* Any header */
2362 pgm
->header
= mail_newsearchheader (namehdr
, sstring
);
2365 case 'r' : /* TO or CC */
2367 /* No OR on old servers */
2368 pgm
->to
= mail_newstringlist();
2369 pgm
->to
->text
.data
= (unsigned char *) cpystr(sstring
);
2370 pgm
->to
->text
.size
= strlen(sstring
);
2371 secondpgm
= mail_newsearchpgm();
2372 secondpgm
->cc
= mail_newstringlist();
2373 secondpgm
->cc
->text
.data
= (unsigned char *) cpystr(sstring
);
2374 secondpgm
->cc
->text
.size
= strlen(sstring
);
2377 pgm
->or = mail_newsearchor();
2378 pgm
->or->first
->to
= mail_newstringlist();
2379 pgm
->or->first
->to
->text
.data
= (unsigned char *) cpystr(sstring
);
2380 pgm
->or->first
->to
->text
.size
= strlen(sstring
);
2381 pgm
->or->second
->cc
= mail_newstringlist();
2382 pgm
->or->second
->cc
->text
.data
= (unsigned char *) cpystr(sstring
);
2383 pgm
->or->second
->cc
->text
.size
= strlen(sstring
);
2388 case 'p' : /* TO or CC or FROM */
2390 /* No OR on old servers */
2391 pgm
->to
= mail_newstringlist();
2392 pgm
->to
->text
.data
= (unsigned char *) cpystr(sstring
);
2393 pgm
->to
->text
.size
= strlen(sstring
);
2394 secondpgm
= mail_newsearchpgm();
2395 secondpgm
->cc
= mail_newstringlist();
2396 secondpgm
->cc
->text
.data
= (unsigned char *) cpystr(sstring
);
2397 secondpgm
->cc
->text
.size
= strlen(sstring
);
2398 thirdpgm
= mail_newsearchpgm();
2399 thirdpgm
->from
= mail_newstringlist();
2400 thirdpgm
->from
->text
.data
= (unsigned char *) cpystr(sstring
);
2401 thirdpgm
->from
->text
.size
= strlen(sstring
);
2404 pgm
->or = mail_newsearchor();
2405 pgm
->or->first
->to
= mail_newstringlist();
2406 pgm
->or->first
->to
->text
.data
= (unsigned char *) cpystr(sstring
);
2407 pgm
->or->first
->to
->text
.size
= strlen(sstring
);
2409 pgm
->or->second
->or = mail_newsearchor();
2410 pgm
->or->second
->or->first
->cc
= mail_newstringlist();
2411 pgm
->or->second
->or->first
->cc
->text
.data
=
2412 (unsigned char *) cpystr(sstring
);
2413 pgm
->or->second
->or->first
->cc
->text
.size
= strlen(sstring
);
2414 pgm
->or->second
->or->second
->from
= mail_newstringlist();
2415 pgm
->or->second
->or->second
->from
->text
.data
=
2416 (unsigned char *) cpystr(sstring
);
2417 pgm
->or->second
->or->second
->from
->text
.size
= strlen(sstring
);
2422 case 'f' : /* FROM */
2423 pgm
->from
= mail_newstringlist();
2424 pgm
->from
->text
.data
= (unsigned char *) cpystr(sstring
);
2425 pgm
->from
->text
.size
= strlen(sstring
);
2429 pgm
->cc
= mail_newstringlist();
2430 pgm
->cc
->text
.data
= (unsigned char *) cpystr(sstring
);
2431 pgm
->cc
->text
.size
= strlen(sstring
);
2435 pgm
->to
= mail_newstringlist();
2436 pgm
->to
->text
.data
= (unsigned char *) cpystr(sstring
);
2437 pgm
->to
->text
.size
= strlen(sstring
);
2440 case 's' : /* SUBJECT */
2441 pgm
->subject
= mail_newstringlist();
2442 pgm
->subject
->text
.data
= (unsigned char *) cpystr(sstring
);
2443 pgm
->subject
->text
.size
= strlen(sstring
);
2446 case 'a' : /* ALL TEXT */
2447 pgm
->text
= mail_newstringlist();
2448 pgm
->text
->text
.data
= (unsigned char *) cpystr(sstring
);
2449 pgm
->text
->text
.size
= strlen(sstring
);
2452 case 'b' : /* ALL BODY TEXT */
2453 pgm
->body
= mail_newstringlist();
2454 pgm
->body
->text
.data
= (unsigned char *) cpystr(sstring
);
2455 pgm
->body
->text
.size
= strlen(sstring
);
2459 dprint((1,"\n - BOTCH: select_text unrecognized type\n"));
2464 * If we happen to have any messages excluded, make sure we
2465 * don't waste time searching their text...
2467 srchpgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
2469 /* TRANSLATORS: warning to user that we're busy selecting messages */
2470 we_cancel
= busy_cue(_("Busy Selecting"), NULL
, 1);
2472 searchflags
= SE_NOPREFETCH
| (secondpgm
? 0 : SE_FREE
);
2474 pine_mail_search_full(stream
, !old_imap
? charset
: NULL
, srchpgm
,
2477 /* search for To or Cc; or To or Cc or From on old imap server */
2480 srchpgm
->msgno
= NULL
;
2481 mail_free_searchpgm(&srchpgm
);
2484 secondpgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
2485 searchflags
|= (SE_RETAIN
| (thirdpgm
? 0 : SE_FREE
));
2487 pine_mail_search_full(stream
, NULL
, secondpgm
, searchflags
);
2491 secondpgm
->msgno
= NULL
;
2492 mail_free_searchpgm(&secondpgm
);
2495 thirdpgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
2496 searchflags
|= SE_FREE
;
2497 pine_mail_search_full(stream
, NULL
, thirdpgm
, searchflags
);
2501 /* we know this was freed in mail_search, let caller know */
2505 if(old_imap
&& not){
2510 * Old imap server doesn't have a NOT, so we actually searched for
2511 * the subject (or whatever) instead of !subject. Flip the searched
2514 for(msgno
= 1L; msgno
<= mn_get_total(msgmap
); msgno
++)
2515 if(stream
&& msgno
<= stream
->nmsgs
){
2516 if((mc
= mail_elt(stream
, msgno
)) != NULL
)
2517 mc
->searched
= mc
->searched
? NIL
: T
;
2529 search_for_our_regex_addresses(MAILSTREAM
*stream
, char type
, int not,
2530 SEARCHSET
*searchset
)
2532 long rawno
, count
= 0L;
2534 ADDRESS
*addr1
= NULL
, *addr2
= NULL
, *addr3
= NULL
;
2536 SEARCHSET
*s
, *ss
= NULL
;
2537 extern MAILSTREAM
*mm_search_stream
;
2538 extern long mm_search_count
;
2540 mm_search_count
= 0L;
2541 mm_search_stream
= stream
;
2546 /* set searched bits to zero */
2547 for(rawno
= 1L; rawno
<= stream
->nmsgs
; rawno
++)
2548 if((mc
=mail_elt(stream
, rawno
)) != NULL
)
2551 /* set sequence bits for envelopes we need */
2552 for(rawno
= 1L; rawno
<= stream
->nmsgs
; rawno
++){
2553 if((mc
= mail_elt(stream
, rawno
)) != NULL
){
2554 if((!searchset
|| in_searchset(searchset
, (unsigned long) rawno
))
2555 && !mc
->private.msg
.env
){
2565 * Set up a searchset that will control the fetch ahead.
2568 ss
= build_searchset(stream
);
2570 SEARCHSET
**sset
= NULL
;
2572 mail_parameters(NULL
, SET_FETCHLOOKAHEADLIMIT
, (void *) count
);
2574 /* this resets automatically after the first fetch */
2575 sset
= (SEARCHSET
**) mail_parameters(stream
,
2583 for(s
= searchset
; s
; s
= s
->next
){
2584 for(rawno
= s
->first
; rawno
<= s
->last
; rawno
++){
2585 env
= pine_mail_fetchenvelope(stream
, rawno
);
2586 addr1
= addr2
= addr3
= NULL
;
2589 addr1
= env
? env
->to
: NULL
;
2590 addr2
= env
? env
->cc
: NULL
;
2593 addr1
= env
? env
->to
: NULL
;
2594 addr2
= env
? env
->cc
: NULL
;
2595 addr3
= env
? env
->from
: NULL
;
2598 addr1
= env
? env
->from
: NULL
;
2601 addr1
= env
? env
->cc
: NULL
;
2605 addr1
= env
? env
->to
: NULL
;
2608 q_status_message(SM_ORDER
, 3, 3, "Unhandled case2 in agg_text_select");
2612 if(addr1
&& address_is_us(addr1
, ps_global
)){
2613 if(rawno
> 0L && rawno
<= stream
->nmsgs
2614 && (mc
=mail_elt(stream
, rawno
)) != NULL
)
2615 mm_searched(stream
, rawno
);
2617 else if(addr2
&& address_is_us(addr2
, ps_global
)){
2618 if(rawno
> 0L && rawno
<= stream
->nmsgs
2619 && (mc
=mail_elt(stream
, rawno
)) != NULL
)
2620 mm_searched(stream
, rawno
);
2622 else if(addr3
&& address_is_us(addr3
, ps_global
)){
2623 if(rawno
> 0L && rawno
<= stream
->nmsgs
2624 && (mc
=mail_elt(stream
, rawno
)) != NULL
)
2625 mm_searched(stream
, rawno
);
2631 mail_free_searchset(&ss
);
2634 for(rawno
= 1L; rawno
<= stream
->nmsgs
; rawno
++){
2635 if((mc
=mail_elt(stream
, rawno
)) && mc
->searched
)
2645 agg_flag_select(MAILSTREAM
*stream
, int not, int crit
, SEARCHSET
**limitsrch
)
2649 pgm
= mail_newsearchpgm();
2655 /* this is the same as seen or deleted or answered */
2656 pgm
->not = mail_newsearchpgmlist();
2657 notpgm
= pgm
->not->pgm
= mail_newsearchpgm();
2658 notpgm
->unseen
= notpgm
->undeleted
= notpgm
->unanswered
= 1;
2661 pgm
->unseen
= pgm
->undeleted
= pgm
->unanswered
= 1;
2691 * Not a true "not", we are implicitly only interested in undeleted.
2694 pgm
->unanswered
= pgm
->undeleted
= 1;
2696 pgm
->answered
= pgm
->undeleted
= 1;
2703 for(slpp
= (not) ? &pgm
->unkeyword
: &pgm
->keyword
;
2705 slpp
= &(*slpp
)->next
)
2708 *slpp
= mail_newstringlist();
2709 (*slpp
)->text
.data
= (unsigned char *) cpystr(FORWARDED_FLAG
);
2710 (*slpp
)->text
.size
= (unsigned long) strlen(FORWARDED_FLAG
);
2728 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
2729 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
2730 /* we know this was freed in mail_search, let caller know */
2739 * Get the user name from the mailbox portion of an address.
2741 * Args: mailbox -- the mailbox portion of an address (lhs of address)
2742 * target -- a buffer to put the result in
2743 * len -- length of the target buffer
2745 * Returns the left most portion up to the first '%', ':' or '@',
2746 * and to the right of any '!' (as if c-client would give us such a mailbox).
2747 * Returns NULL if it can't find a username to point to.
2750 get_uname(char *mailbox
, char *target
, int len
)
2754 if(!mailbox
|| !*mailbox
)
2757 end
= strlen(mailbox
) - 1;
2758 for(start
= end
; start
> -1 && mailbox
[start
] != '!'; start
--)
2759 if(strindex("%:@", mailbox
[start
]))
2762 start
++; /* compensate for either case above */
2764 for(i
= start
; i
<= end
&& (i
-start
) < (len
-1); i
++) /* copy name */
2765 target
[i
-start
] = isupper((unsigned char)mailbox
[i
])
2766 ? tolower((unsigned char)mailbox
[i
])
2769 target
[i
-start
] = '\0'; /* tie it off */
2771 return(*target
? target
: NULL
);