1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: mailcmd.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2017 Eduardo Chappa
8 * Copyright 2006-2007 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 #include "../pith/headers.h"
20 #include "../pith/mailcmd.h"
21 #include "../pith/conf.h"
22 #include "../pith/status.h"
23 #include "../pith/flag.h"
24 #include "../pith/thread.h"
25 #include "../pith/util.h"
26 #include "../pith/folder.h"
27 #include "../pith/sort.h"
28 #include "../pith/newmail.h"
29 #include "../pith/mailview.h"
30 #include "../pith/mailindx.h"
31 #include "../pith/save.h"
32 #include "../pith/news.h"
33 #include "../pith/sequence.h"
34 #include "../pith/stream.h"
35 #include "../pith/ldap.h"
36 #include "../pith/options.h"
37 #include "../pith/busy.h"
38 #include "../pith/icache.h"
39 #include "../pith/ablookup.h"
40 #include "../pith/search.h"
41 #include "../pith/charconv/utf8.h"
44 #include "../pico/osdep/mswin.h"
54 * optional function hooks
56 int (*pith_opt_read_msg_prompt
)(long, char *);
57 int (*pith_opt_reopen_folder
)(struct pine
*, int *);
58 int (*pith_opt_expunge_prompt
)(MAILSTREAM
*, char *, long);
59 void (*pith_opt_begin_closing
)(int, char *);
60 void get_new_message_count(MAILSTREAM
*, int, long *, long *);
61 char *new_messages_string(MAILSTREAM
*);
62 void search_for_our_regex_addresses(MAILSTREAM
*stream
, char type
,
63 int not, SEARCHSET
*searchset
);
67 /*----------------------------------------------------------------------
68 Complain about command on empty folder
71 type -- type of message that's missing
72 cmd -- string explaining command attempted
76 any_messages(MSGNO_S
*map
, char *type
, char *cmd
)
78 if(mn_get_total(map
) <= 0L){
79 q_status_message5(SM_ORDER
, 0, 2, "No %s%s%s%s%s",
82 THRD_INDX() ? "threads" : "messages",
83 (!cmd
|| *cmd
!= '.') ? " " : "",
84 cmd
? cmd
: "in folder");
92 /*----------------------------------------------------------------------
93 test whether or not we have a valid stream to set flags on
95 Args: state -- pine state containing vital signs
96 cmd -- string explaining command attempted
97 permflag -- associated permanent flag state
99 Result: returns 1 if we can set flags, otw 0 and complains
103 can_set_flag(struct pine
*state
, char *cmd
, int permflag
)
105 if((!permflag
&& READONLY_FOLDER(state
->mail_stream
))
106 || sp_dead_stream(state
->mail_stream
)){
107 q_status_message2(SM_ORDER
| (sp_dead_stream(state
->mail_stream
)
110 "Can't %s message. Folder is %s.", cmd
,
111 (sp_dead_stream(state
->mail_stream
)) ? "closed" : "read-only");
119 /*----------------------------------------------------------------------
120 Complain about command on empty folder
122 Args: type -- type of message that's missing
123 cmd -- string explaining command attempted
127 cmd_cancelled(char *cmd
)
129 /* TRANSLATORS: Arg is replaced with the command name or the word Command */
130 q_status_message1(SM_INFO
, 0, 2, _("%s cancelled"), cmd
? cmd
: _("Command"));
134 /*----------------------------------------------------------------------
135 Execute DELETE message command
137 Args: state -- Various satate info
138 msgmap -- map of c-client to local message numbers
140 Result: with side effect of "current" message delete flag set
144 cmd_delete(struct pine
*state
, MSGNO_S
*msgmap
, int copts
,
145 char *(*cmd_action_f
)(struct pine
*, MSGNO_S
*))
148 long msgno
, del_count
= 0L, new;
149 char *sequence
= NULL
, prompt
[128];
151 dprint((4, "\n - delete message -\n"));
152 if(!(any_messages(msgmap
, NULL
, "to Delete")
153 && can_set_flag(state
, "delete", state
->mail_stream
->perm_deleted
)))
158 if(sp_io_error_on_stream(state
->mail_stream
)){
159 sp_set_io_error_on_stream(state
->mail_stream
, 0);
160 pine_mail_check(state
->mail_stream
); /* forces write */
163 if(MCMD_ISAGG(copts
)){
164 sequence
= selected_sequence(state
->mail_stream
, msgmap
, &del_count
, 0);
165 snprintf(prompt
, sizeof(prompt
), "%ld%s message%s marked for deletion",
166 del_count
, (copts
& MCMD_AGG_2
) ? "" : " selected", plural(del_count
));
171 msgno
= mn_get_cur(msgmap
);
172 rawno
= mn_m2raw(msgmap
, msgno
);
173 del_count
= 1L; /* return current */
174 sequence
= cpystr(long2string(rawno
));
175 lastmsg
= (msgno
>= mn_get_total(msgmap
));
176 snprintf(prompt
, sizeof(prompt
), "%s%s marked for deletion",
177 lastmsg
? "Last message" : "Message ",
178 lastmsg
? "" : long2string(msgno
));
181 dprint((3, "DELETE: msg %s\n", sequence
? sequence
: "?"));
182 new = sp_new_mail_count(state
->mail_stream
);
183 mail_flag(state
->mail_stream
, sequence
, "\\DELETED", ST_SET
);
184 fs_give((void **) &sequence
);
185 if(new != sp_new_mail_count(state
->mail_stream
))
186 process_filter_patterns(state
->mail_stream
, state
->msgmap
,
187 sp_new_mail_count(state
->mail_stream
));
192 if((rv
= (*cmd_action_f
)(state
, msgmap
)) != NULL
)
193 strncat(prompt
, rv
, sizeof(prompt
) - strlen(prompt
)- 1);
196 if(!(copts
& MCMD_SILENT
))
197 q_status_message(SM_ORDER
, 0, 3, prompt
);
203 /*----------------------------------------------------------------------
204 Execute UNDELETE message command
206 Args: state -- Various satate info
207 msgmap -- map of c-client to local message numbers
209 Result: with side effect of "current" message delete flag UNset
213 cmd_undelete(struct pine
*state
, MSGNO_S
*msgmap
, int copts
)
217 int wasdeleted
= FALSE
, rv
= 0;
220 dprint((4, "\n - undelete -\n"));
221 if(!(any_messages(msgmap
, NULL
, "to Undelete")
222 && can_set_flag(state
, "undelete", state
->mail_stream
->perm_deleted
)))
227 if(MCMD_ISAGG(copts
)){
228 del_count
= 0L; /* return current */
229 sequence
= selected_sequence(state
->mail_stream
, msgmap
, &del_count
, 1);
236 del_count
= 1L; /* return current */
237 rawno
= mn_m2raw(msgmap
, mn_get_cur(msgmap
));
238 sequence
= cpystr(long2string(rawno
));
239 wasdeleted
= (state
->mail_stream
240 && rawno
> 0L && rawno
<= state
->mail_stream
->nmsgs
241 && (mc
= mail_elt(state
->mail_stream
, rawno
))
245 * Mark this message manually flagged so we don't re-filter it
246 * with a filter which only sets flags.
248 if(msgno_exceptions(state
->mail_stream
, rawno
, "0", &exbits
, FALSE
))
249 exbits
|= MSG_EX_MANUNDEL
;
251 exbits
= MSG_EX_MANUNDEL
;
253 msgno_exceptions(state
->mail_stream
, rawno
, "0", &exbits
, TRUE
);
256 dprint((3, "UNDELETE: msg %s\n", sequence
? sequence
: "?"));
258 mail_flag(state
->mail_stream
, sequence
, "\\DELETED", 0L);
259 fs_give((void **) &sequence
);
261 if((copts
& MCMD_SILENT
) == 0){
262 if(del_count
== 1L && MCMD_ISAGG(copts
) == 0){
263 q_status_message(SM_ORDER
, 0, 3,
265 ? _("Deletion mark removed, message won't be deleted")
266 : _("Message not marked for deletion; no action taken"));
269 q_status_message2(SM_ORDER
, 0, 3,
270 _("Deletion mark removed from %s message%s"),
271 comatose(del_count
), plural(del_count
));
274 if(sp_io_error_on_stream(state
->mail_stream
)){
275 sp_set_io_error_on_stream(state
->mail_stream
, 0);
276 pine_mail_check(state
->mail_stream
); /* forces write */
284 cmd_expunge_work(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, char *seq
)
289 old_max_msgno
= mn_get_total(msgmap
);
290 delete_filtered_msgs(stream
);
291 ps_global
->expunge_in_progress
= 1;
292 mail_expunge_full(stream
, seq
, 0);
293 ps_global
->expunge_in_progress
= 0;
295 dprint((2,"expunge complete cur:%ld max:%ld\n",
296 mn_get_cur(msgmap
), mn_get_total(msgmap
)));
298 * This is only actually necessary if this causes the width of the
299 * message number field to change. That is, it depends on the
300 * format the user is using as well as on the max_msgno. Since it
301 * should be rare, we'll just do it whenever it happens.
302 * Also have to check for an increase in max_msgno on new mail.
304 if((old_max_msgno
>= 1000L && mn_get_total(msgmap
) < 1000L)
305 || (old_max_msgno
>= 10000L && mn_get_total(msgmap
) < 10000L)
306 || (old_max_msgno
>= 100000L && mn_get_total(msgmap
) < 100000L)){
307 clear_index_cache(stream
, 0);
312 * mm_exists and mm_expunge take care of updating max_msgno
313 * and selecting a new message should the selected get removed
317 reset_check_point(stream
);
324 broach_get_folder(CONTEXT_S
*context
, int *inbox
, char **folder
)
328 if(ps_global
->goto_default_rule
== GOTO_LAST_FLDR
){
329 tc
= context
? context
: ps_global
->context_current
;
330 *inbox
= 1; /* fill in last_folder below */
332 else if(ps_global
->goto_default_rule
== GOTO_FIRST_CLCTN
){
333 tc
= (ps_global
->context_list
->use
& CNTXT_INCMNG
)
334 ? ps_global
->context_list
->next
: ps_global
->context_list
;
335 ps_global
->last_unambig_folder
[0] = '\0';
336 *inbox
= 1; /* fill in last_folder below */
338 else if(ps_global
->goto_default_rule
== GOTO_FIRST_CLCTN_DEF_INBOX
){
339 tc
= (ps_global
->context_list
->use
& CNTXT_INCMNG
)
340 ? ps_global
->context_list
->next
: ps_global
->context_list
;
341 tc
->last_folder
[0] = '\0';
343 ps_global
->last_unambig_folder
[0] = '\0';
346 *inbox
= (ps_global
->cur_folder
347 && ps_global
->inbox_name
348 && strucmp(ps_global
->cur_folder
,ps_global
->inbox_name
) == 0
349 && (!ps_global
->context_current
350 || ps_global
->context_current
->use
& CNTXT_INCMNG
351 || (!(ps_global
->context_list
->use
& CNTXT_INCMNG
)
352 && ps_global
->context_current
== ps_global
->context_list
)));
354 tc
= ps_global
->context_list
; /* inbox's context */
355 else if(ps_global
->goto_default_rule
== GOTO_INBOX_FIRST_CLCTN
){
356 tc
= (ps_global
->context_list
->use
& CNTXT_INCMNG
)
357 ? ps_global
->context_list
->next
: ps_global
->context_list
;
358 ps_global
->last_unambig_folder
[0] = '\0';
361 tc
= context
? context
: ps_global
->context_current
;
366 *folder
= ps_global
->inbox_name
;
369 *folder
= (ps_global
->last_unambig_folder
[0])
370 ? ps_global
->last_unambig_folder
371 : ((tc
->last_folder
[0]) ? tc
->last_folder
: NULL
);
378 /*----------------------------------------------------------------------
379 Actually attempt to open given folder
381 Args: newfolder -- The folder name to open
382 streamp -- Candidate stream for recycling. This stream will either
383 be re-used, or it will be closed.
385 Result: 1 if the folder was successfully opened
386 0 if the folder open failed and went back to old folder
387 -1 if open failed and no folder is left open
389 Attempt to open the folder name given. If the open of the new folder
390 fails then the previously open folder will remain open, unless
391 something really bad has happened. The designate inbox will always be
392 kept open, and when a request to open it is made the already open
396 do_broach_folder(char *newfolder
, CONTEXT_S
*new_context
, MAILSTREAM
**streamp
,
397 long unsigned int flags
)
399 MAILSTREAM
*m
, *strm
, *stream
= streamp
? *streamp
: NULL
;
400 int open_inbox
, rv
, old_tros
, we_cancel
= 0,
401 do_reopen
= 0, n
, was_dead
= 0, cur_already_set
= 0;
402 char expanded_file
[MAX(MAXPATH
,MAILTMPLEN
)+1],
403 *old_folder
, *old_path
, *p
, *report
;
404 unsigned char *fname
;
405 long openmode
, rflags
= 0L, pc
= 0L, cur
, raw
;
406 ENVELOPE
*env
= NULL
;
409 unsigned perfolder_startup_rule
;
410 char tmp1
[MAILTMPLEN
], tmp2
[MAILTMPLEN
], *lname
, *mname
;
412 openmode
= SP_USERFLDR
;
414 dprint((1, "About to open folder \"%s\" inbox is: \"%s\"\n",
415 newfolder
? newfolder
: "?",
416 ps_global
->inbox_name
? ps_global
->inbox_name
: "?"));
419 *--- Set flag that we're opening the inbox, a special case.
421 * We want to know if inbox is being opened either by name OR
422 * fully qualified path...
424 if(strucmp(newfolder
, ps_global
->inbox_name
) == 0)
425 open_inbox
= (flags
& DB_INBOXWOCNTXT
|| new_context
== ps_global
->context_list
);
427 open_inbox
= (strcmp(newfolder
, ps_global
->VAR_INBOX_PATH
) == 0
428 || same_remote_mailboxes(newfolder
, ps_global
->VAR_INBOX_PATH
)
429 || (!IS_REMOTE(newfolder
)
430 && (lname
=mailboxfile(tmp1
,newfolder
))
431 && (mname
=mailboxfile(tmp2
,ps_global
->VAR_INBOX_PATH
))
432 && !strcmp(lname
,mname
)));
434 /* further checking for inbox open */
435 if(!open_inbox
&& new_context
&& context_isambig(newfolder
)){
436 if((p
= folder_is_nick(newfolder
, FOLDERS(new_context
), FN_WHOLE_NAME
)) != NULL
){
438 * Check for an incoming folder other
439 * than INBOX that also point to INBOX.
441 open_inbox
= (strucmp(p
, ps_global
->inbox_name
) == 0
442 || strcmp(p
, ps_global
->VAR_INBOX_PATH
) == 0
443 || same_remote_mailboxes(p
, ps_global
->VAR_INBOX_PATH
)
445 && (lname
=mailboxfile(tmp1
,p
))
446 && (mname
=mailboxfile(tmp2
,ps_global
->VAR_INBOX_PATH
))
447 && !strcmp(lname
,mname
)));
449 else if(!(new_context
->use
& CNTXT_INCMNG
)){
450 char tmp3
[MAILTMPLEN
];
453 * Check to see if we are opening INBOX using the folder name
454 * and a context. We won't have recognized this is the
455 * same as INBOX without applying the context first.
457 context_apply(tmp3
, new_context
, newfolder
, sizeof(tmp3
));
458 open_inbox
= (strucmp(tmp3
, ps_global
->inbox_name
) == 0
459 || strcmp(tmp3
, ps_global
->VAR_INBOX_PATH
) == 0
460 || same_remote_mailboxes(tmp3
, ps_global
->VAR_INBOX_PATH
)
462 && (lname
=mailboxfile(tmp1
,tmp3
))
463 && (mname
=mailboxfile(tmp2
,ps_global
->VAR_INBOX_PATH
))
464 && !strcmp(lname
,mname
)));
470 new_context
= ps_global
->context_list
; /* restore first context */
472 was_dead
= sp_a_locked_stream_is_dead();
474 /*----- Little to do to if reopening same folder -----*/
475 if(new_context
== ps_global
->context_current
&& ps_global
->mail_stream
476 && (strcmp(newfolder
, ps_global
->cur_folder
) == 0
477 || (open_inbox
&& sp_flagged(ps_global
->mail_stream
, SP_INBOX
)))){
479 pine_mail_close(stream
); /* don't need it */
483 if(sp_dead_stream(ps_global
->mail_stream
))
487 * If it is a stream which could probably discover newmail by
488 * reopening and user has YES set for those streams, or it
489 * is a stream which may discover newmail by reopening and
490 * user has YES set for those stream, then do_reopen.
494 (((ps_global
->mail_stream
->dtb
495 && ((ps_global
->mail_stream
->dtb
->flags
& DR_NONEWMAIL
)
496 || (ps_global
->mail_stream
->rdonly
497 && ps_global
->mail_stream
->dtb
->flags
498 & DR_NONEWMAILRONLY
)))
499 && (ps_global
->reopen_rule
== REOPEN_YES_YES
500 || ps_global
->reopen_rule
== REOPEN_YES_ASK_Y
501 || ps_global
->reopen_rule
== REOPEN_YES_ASK_N
502 || ps_global
->reopen_rule
== REOPEN_YES_NO
))
504 ((ps_global
->mail_stream
->dtb
505 && ps_global
->mail_stream
->rdonly
506 && !(ps_global
->mail_stream
->dtb
->flags
& DR_LOCAL
))
507 && (ps_global
->reopen_rule
== REOPEN_YES_YES
))))
511 * If it is a stream which could probably discover newmail by
512 * reopening and user has ASK set for those streams, or it
513 * is a stream which may discover newmail by reopening and
514 * user has ASK set for those stream, then ask.
517 && pith_opt_reopen_folder
518 && (*pith_opt_reopen_folder
)(ps_global
, &do_reopen
) < 0){
525 * If it's not healthy or if the user explicitly wants to
526 * do a reopen, we reset things and fall thru
527 * to actually reopen it.
529 if(sp_dead_stream(ps_global
->mail_stream
)){
530 dprint((2, "Stream was dead, reopening \"%s\"\n",
531 newfolder
? newfolder
: "?"));
535 pine_mail_actually_close(ps_global
->mail_stream
);
536 ps_global
->mangled_header
= 1;
537 clear_index_cache(ps_global
->mail_stream
, 0);
540 if(!(flags
& DB_NOVISIT
))
541 sp_set_recent_since_visited(ps_global
->mail_stream
, 0L);
543 return(1); /* successful open of same folder! */
548 * If ambiguous foldername (not fully qualified), make sure it's
549 * not a nickname for a folder in the given context...
552 /* might get reset below */
553 strncpy(expanded_file
, newfolder
, sizeof(expanded_file
));
554 expanded_file
[sizeof(expanded_file
)-1] = '\0';
556 if(!open_inbox
&& new_context
&& context_isambig(newfolder
)){
557 if((p
= folder_is_nick(newfolder
, FOLDERS(new_context
), FN_WHOLE_NAME
)) != NULL
){
558 strncpy(expanded_file
, p
, sizeof(expanded_file
));
559 expanded_file
[sizeof(expanded_file
)-1] = '\0';
560 dprint((2, "broach_folder: nickname for %s is %s\n",
561 expanded_file
? expanded_file
: "?",
562 newfolder
? newfolder
: "?"));
564 else if((new_context
->use
& CNTXT_INCMNG
)
565 && (folder_index(newfolder
, new_context
, FI_FOLDER
) < 0)
566 && !is_absolute_path(newfolder
)){
567 fname
= folder_name_decoded((unsigned char *)newfolder
);
568 q_status_message1(SM_ORDER
, 3, 4,
569 _("Can't find Incoming Folder %s."), fname
? (char *) fname
: newfolder
);
571 pine_mail_close(stream
);
574 fs_give((void **)&fname
);
580 /*--- Opening inbox, inbox has been already opened, the easy case ---*/
582 * [ It is probably true that we could eliminate most of this special ]
583 * [ inbox stuff and just get the inbox stream back when we do the ]
584 * [ context_open below, but figuring that out hasn't been done. ]
586 if(open_inbox
&& (strm
=sp_inbox_stream())){
587 if(sp_dead_stream(strm
)){
589 * if dead INBOX, just close it and let it be reopened.
590 * This is different from the do_reopen case above,
591 * because we're going from another open mail folder to the
594 dprint((2, "INBOX was dead, closing before reopening\n"));
595 pine_mail_actually_close(strm
);
599 * Clean up the mail_stream we're leaving.
601 if(ps_global
->mail_stream
602 && (!sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)
603 || (sp_flagged(ps_global
->mail_stream
, SP_INBOX
)
604 && F_ON(F_EXPUNGE_INBOX
, ps_global
))
605 || (!sp_flagged(ps_global
->mail_stream
, SP_INBOX
)
606 && sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)
607 && F_ON(F_EXPUNGE_STAYOPENS
, ps_global
))))
608 expunge_and_close(ps_global
->mail_stream
, NULL
,
609 sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)
610 ? EC_NO_CLOSE
: EC_NONE
);
611 else if(!sp_flagged(ps_global
->mail_stream
, SP_INBOX
)
612 && sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)){
614 * We want to save our position in the folder so that when we
615 * come back to this folder again, we can place the cursor on
616 * a reasonable message number.
618 sp_set_saved_cur_msg_id(ps_global
->mail_stream
, NULL
);
620 if(ps_global
->mail_stream
->nmsgs
> 0L){
621 cur
= mn_get_cur(sp_msgmap(ps_global
->mail_stream
));
622 raw
= mn_m2raw(sp_msgmap(ps_global
->mail_stream
), cur
);
623 if(raw
> 0L && raw
<= ps_global
->mail_stream
->nmsgs
)
624 env
= pine_mail_fetchstructure(ps_global
->mail_stream
,
627 if(env
&& env
->message_id
&& env
->message_id
[0])
628 sp_set_saved_cur_msg_id(ps_global
->mail_stream
,
634 * Make the already open inbox the current mailbox.
636 ps_global
->mail_stream
= strm
;
637 ps_global
->msgmap
= sp_msgmap(strm
);
639 if(was_dead
&& pith_opt_icon_text
)
640 (*pith_opt_icon_text
)(NULL
, IT_MCLOSED
);
642 dprint((7, "%ld %ld %x\n",
643 mn_get_cur(ps_global
->msgmap
),
644 mn_get_total(ps_global
->msgmap
),
645 ps_global
->mail_stream
));
647 * remember last context and folder
649 if(context_isambig(ps_global
->cur_folder
)){
650 ps_global
->context_last
= ps_global
->context_current
;
651 snprintf(ps_global
->context_current
->last_folder
,
652 sizeof(ps_global
->context_current
->last_folder
),
653 "%s", ps_global
->cur_folder
);
654 ps_global
->last_unambig_folder
[0] = '\0';
657 ps_global
->context_last
= NULL
;
658 snprintf(ps_global
->last_unambig_folder
,
659 sizeof(ps_global
->last_unambig_folder
),
660 "%s", ps_global
->cur_folder
);
663 p
= sp_fldr(ps_global
->mail_stream
) ? sp_fldr(ps_global
->mail_stream
)
664 : ps_global
->inbox_name
;
665 strncpy(ps_global
->cur_folder
, p
, sizeof(ps_global
->cur_folder
)-1);
666 ps_global
->cur_folder
[sizeof(ps_global
->cur_folder
)-1] = '\0';
667 ps_global
->context_current
= ps_global
->context_list
;
668 reset_index_format();
669 clear_index_cache(ps_global
->mail_stream
, 0);
670 /* MUST sort before restoring msgno! */
671 refresh_sort(ps_global
->mail_stream
, ps_global
->msgmap
, SRT_NON
);
672 report
= new_messages_string(ps_global
->mail_stream
);
673 q_status_message3(SM_ORDER
, 0, 3,
674 (mn_get_total(ps_global
->msgmap
) > 1)
675 ? _("Opened folder \"%s\" with %s messages%s")
676 : _("Opened folder \"%s\" with %s message%s"),
677 ps_global
->inbox_name
,
678 long2string(mn_get_total(ps_global
->msgmap
)),
679 report
? report
: "");
681 fs_give((void **)&report
);
684 mswin_settitle(ps_global
->inbox_name
);
687 pine_mail_close(stream
);
689 if(!(flags
& DB_NOVISIT
))
690 sp_set_recent_since_visited(ps_global
->mail_stream
, 0L);
696 if(!new_context
&& !expand_foldername(expanded_file
,sizeof(expanded_file
))){
698 pine_mail_close(stream
);
704 * This is a safe time to clean up dead streams because nothing should
705 * be referencing them right now.
707 sp_cleanup_dead_streams();
711 old_sort
= SortArrival
; /* old sort */
712 old_tros
= 0; /* old reverse sort ? */
713 /*---- now close the old one we had open if there was one ----*/
714 if(ps_global
->mail_stream
!= NULL
){
715 old_folder
= cpystr(ps_global
->cur_folder
);
716 old_path
= cpystr(ps_global
->mail_stream
->original_mailbox
717 ? ps_global
->mail_stream
->original_mailbox
718 : ps_global
->mail_stream
->mailbox
);
719 old_sort
= mn_get_sort(ps_global
->msgmap
);
720 old_tros
= mn_get_revsort(ps_global
->msgmap
);
721 if(!sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)
722 || (sp_flagged(ps_global
->mail_stream
, SP_INBOX
)
723 && F_ON(F_EXPUNGE_INBOX
, ps_global
))
724 || (!sp_flagged(ps_global
->mail_stream
, SP_INBOX
)
725 && sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)
726 && F_ON(F_EXPUNGE_STAYOPENS
, ps_global
)))
727 expunge_and_close(ps_global
->mail_stream
, NULL
,
728 sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)
729 ? EC_NO_CLOSE
: EC_NONE
);
730 else if(!sp_flagged(ps_global
->mail_stream
, SP_INBOX
)
731 && sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
)){
733 * We want to save our position in the folder so that when we
734 * come back to this folder again, we can place the cursor on
735 * a reasonable message number.
738 sp_set_saved_cur_msg_id(ps_global
->mail_stream
, NULL
);
740 if(ps_global
->mail_stream
->nmsgs
> 0L){
741 cur
= mn_get_cur(sp_msgmap(ps_global
->mail_stream
));
742 raw
= mn_m2raw(sp_msgmap(ps_global
->mail_stream
), cur
);
743 if(raw
> 0L && raw
<= ps_global
->mail_stream
->nmsgs
)
744 env
= pine_mail_fetchstructure(ps_global
->mail_stream
,
747 if(env
&& env
->message_id
&& env
->message_id
[0])
748 sp_set_saved_cur_msg_id(ps_global
->mail_stream
,
753 ps_global
->mail_stream
= NULL
;
756 snprintf(status_msg
, sizeof(status_msg
), "%sOpening \"", do_reopen
? "Re-" : "");
757 fname
= folder_name_decoded((unsigned char *)newfolder
);
758 strncat(status_msg
, pretty_fn(fname
? (char *) fname
: newfolder
),
759 sizeof(status_msg
)-strlen(status_msg
) - 2);
760 if(fname
) fs_give((void **)&fname
);
761 status_msg
[sizeof(status_msg
)-2] = '\0';
762 strncat(status_msg
, "\"", sizeof(status_msg
)-strlen(status_msg
) - 1);
763 status_msg
[sizeof(status_msg
)-1] = '\0';
764 we_cancel
= busy_cue(status_msg
, NULL
, 0);
767 * if requested, make access to folder readonly (only once)
769 if(ps_global
->open_readonly_on_startup
){
770 openmode
|= OP_READONLY
;
771 ps_global
->open_readonly_on_startup
= 0;
774 if(!(flags
& DB_NOVISIT
))
775 ps_global
->first_open_was_attempted
= 1;
777 openmode
|= SP_USEPOOL
;
780 sp_set_first_unseen(stream
, 0L);
782 /* in case we closed the old stream by cancelling the connection, do
783 * not let that interfere with opening the new stream.
785 ps_global
->user_says_cancel
= 0;
787 m
= context_open((new_context
&& !open_inbox
) ? new_context
: NULL
,
789 open_inbox
? ps_global
->VAR_INBOX_PATH
: expanded_file
,
790 openmode
| (open_inbox
? SP_INBOX
: 0),
794 * We aren't in a situation where we want a single cancel to
795 * apply to multiple opens.
797 ps_global
->user_says_cancel
= 0;
803 dprint((8, "Opened folder %p \"%s\" (context: \"%s\")\n",
804 m
, (m
&& m
->mailbox
) ? m
->mailbox
: "nil",
805 (new_context
&& new_context
->context
)
806 ? new_context
->context
: "nil"));
809 /* Can get m != NULL if correct passwd for remote, but wrong name */
810 if(m
== NULL
|| m
->halfopen
){
811 /*-- non-existent local mailbox, or wrong passwd for remote mailbox--*/
812 /* fall back to currently open mailbox */
816 ps_global
->mail_stream
= NULL
;
818 pine_mail_actually_close(m
);
821 dprint((8, "Old folder: \"%s\"\n",
822 old_folder
== NULL
? "" : old_folder
));
823 if(old_folder
!= NULL
){
824 if(strcmp(old_folder
, ps_global
->inbox_name
) == 0){
825 ps_global
->mail_stream
= sp_inbox_stream();
826 ps_global
->msgmap
= sp_msgmap(ps_global
->mail_stream
);
828 dprint((8, "Reactivate inbox %ld %ld %p\n",
829 mn_get_cur(ps_global
->msgmap
),
830 mn_get_total(ps_global
->msgmap
),
831 ps_global
->mail_stream
));
832 p
= sp_fldr(ps_global
->mail_stream
)
833 ? sp_fldr(ps_global
->mail_stream
)
834 : ps_global
->inbox_name
;
835 strncpy(ps_global
->cur_folder
, p
,
836 sizeof(ps_global
->cur_folder
)-1);
837 ps_global
->cur_folder
[sizeof(ps_global
->cur_folder
)-1] = '\0';
840 ps_global
->mail_stream
= pine_mail_open(NULL
, old_path
,
842 /* mm_log will take care of error message here */
843 if(ps_global
->mail_stream
== NULL
){
847 ps_global
->msgmap
= sp_msgmap(ps_global
->mail_stream
);
848 mn_set_sort(ps_global
->msgmap
, old_sort
);
849 mn_set_revsort(ps_global
->msgmap
, old_tros
);
850 ps_global
->mangled_header
= 1;
851 reset_index_format();
852 clear_index_cache(ps_global
->mail_stream
, 0);
854 if(!(rflags
& SP_MATCH
)){
855 sp_set_expunge_count(ps_global
->mail_stream
, 0L);
856 sp_set_new_mail_count(ps_global
->mail_stream
, 0L);
857 sp_set_dead_stream(ps_global
->mail_stream
, 0);
858 sp_set_noticed_dead_stream(ps_global
->mail_stream
, 0);
860 reset_check_point(ps_global
->mail_stream
);
861 if(IS_NEWS(ps_global
->mail_stream
)
862 && ps_global
->mail_stream
->rdonly
)
863 msgno_exclude_deleted(ps_global
->mail_stream
,
864 sp_msgmap(ps_global
->mail_stream
),
867 if(mn_get_total(ps_global
->msgmap
) > 0)
868 mn_set_cur(ps_global
->msgmap
,
869 first_sorted_flagged(F_NONE
,
870 ps_global
->mail_stream
,
873 ? 0 : FSF_SKIP_CHID
));
875 if(!(mn_get_sort(ps_global
->msgmap
) == SortArrival
876 && !mn_get_revsort(ps_global
->msgmap
)))
877 refresh_sort(ps_global
->mail_stream
,
878 ps_global
->msgmap
, SRT_NON
);
881 fname
= folder_name_decoded((unsigned char *)old_folder
);
882 q_status_message1(SM_ORDER
, 0, 3,
883 "Folder \"%s\" reopened", fname
? (char *)fname
: old_folder
);
885 fs_give((void **)&fname
);
890 mn_set_cur(ps_global
->msgmap
,
891 MIN(mn_get_cur(ps_global
->msgmap
),
892 mn_get_total(ps_global
->msgmap
)));
894 fs_give((void **)&old_folder
);
895 fs_give((void **)&old_path
);
901 q_status_message(SM_ORDER
| SM_DING
, 0, 4, _("No folder opened"));
902 mn_set_total(ps_global
->msgmap
, 0L);
903 mn_set_nmsgs(ps_global
->msgmap
, 0L);
904 mn_set_cur(ps_global
->msgmap
, -1L);
905 ps_global
->cur_folder
[0] = '\0';
908 if(was_dead
&& !sp_a_locked_stream_is_dead() && pith_opt_icon_text
)
909 (*pith_opt_icon_text
)(NULL
, IT_MCLOSED
);
911 if(ps_global
->mail_stream
&& !(flags
& DB_NOVISIT
))
912 sp_set_recent_since_visited(ps_global
->mail_stream
, 0L);
914 if(ps_global
->mail_stream
)
915 sp_set_first_unseen(ps_global
->mail_stream
, 0L);
920 if(old_folder
!= NULL
){
921 fs_give((void **)&old_folder
);
922 fs_give((void **)&old_path
);
926 update_folder_unseen_by_stream(m
, UFU_NONE
);
928 /*----- success in opening the new folder ----*/
929 dprint((2, "Opened folder \"%s\" with %ld messages\n",
930 m
->mailbox
? m
->mailbox
: "?", m
->nmsgs
));
933 /*--- A Little house keeping ---*/
935 ps_global
->mail_stream
= m
;
936 if(!(flags
& DB_NOVISIT
))
937 sp_set_recent_since_visited(ps_global
->mail_stream
, 0L);
939 ps_global
->msgmap
= sp_msgmap(m
);
940 if(!(rflags
& SP_MATCH
)){
941 sp_set_expunge_count(m
, 0L);
942 sp_set_new_mail_count(m
, 0L);
943 sp_set_dead_stream(m
, 0);
944 sp_set_noticed_dead_stream(m
, 0);
945 sp_set_mail_box_changed(m
, 0);
946 reset_check_point(m
);
949 if(was_dead
&& !sp_a_locked_stream_is_dead() && pith_opt_icon_text
)
950 (*pith_opt_icon_text
)(NULL
, IT_MCLOSED
);
952 ps_global
->last_unambig_folder
[0] = '\0';
955 * remember old folder and context...
957 if(context_isambig(ps_global
->cur_folder
)){
958 ps_global
->context_last
= ps_global
->context_current
;
959 snprintf(ps_global
->context_current
->last_folder
,
960 sizeof(ps_global
->context_current
->last_folder
),
961 "%s", ps_global
->cur_folder
);
962 ps_global
->last_unambig_folder
[0] = '\0';
965 ps_global
->context_last
= NULL
;
966 snprintf(ps_global
->last_unambig_folder
,
967 sizeof(ps_global
->last_unambig_folder
),
968 "%s", ps_global
->cur_folder
);
971 /* folder in a subdir of context? */
972 if(ps_global
->context_current
->dir
->prev
)
973 snprintf(ps_global
->cur_folder
, sizeof(ps_global
->cur_folder
), "%s%s",
974 ps_global
->context_current
->dir
->ref
, newfolder
);
976 strncpy(ps_global
->cur_folder
,
977 (open_inbox
) ? ps_global
->inbox_name
: newfolder
,
978 sizeof(ps_global
->cur_folder
)-1);
979 ps_global
->cur_folder
[sizeof(ps_global
->cur_folder
)-1] = '\0';
982 sp_set_fldr(ps_global
->mail_stream
, ps_global
->cur_folder
);
985 ps_global
->context_last
= ps_global
->context_current
;
986 ps_global
->context_current
= new_context
;
989 sp_set_context(ps_global
->mail_stream
, ps_global
->context_current
);
992 clear_index_cache(ps_global
->mail_stream
, 0);
993 reset_index_format();
996 * Start news reading with messages the user's marked deleted
997 * hidden from view...
999 if(IS_NEWS(ps_global
->mail_stream
) && ps_global
->mail_stream
->rdonly
)
1000 msgno_exclude_deleted(ps_global
->mail_stream
, ps_global
->msgmap
, NULL
);
1002 if(we_cancel
&& F_OFF(F_QUELL_FILTER_MSGS
, ps_global
))
1006 * If the stream we got from the open above was already opened earlier
1007 * for some temporary use, then it wouldn't have been filtered. That's
1008 * why we need this flag, so that we will filter if needed.
1010 if(!sp_flagged(ps_global
->mail_stream
, SP_FILTERED
))
1011 process_filter_patterns(ps_global
->mail_stream
, ps_global
->msgmap
, 0L);
1014 * If no filtering messages wait until here to cancel the busy cue
1015 * because the user will be waiting for that filtering with nothing
1016 * showing the activity otherwise.
1018 if(we_cancel
&& F_ON(F_QUELL_FILTER_MSGS
, ps_global
))
1021 if(!(rflags
& SP_MATCH
) || !(rflags
& SP_LOCKED
))
1022 reset_sort_order(SRT_VRB
);
1023 else if(sp_new_mail_count(ps_global
->mail_stream
) > 0L
1024 || sp_unsorted_newmail(ps_global
->mail_stream
)
1025 || sp_need_to_rethread(ps_global
->mail_stream
))
1026 refresh_sort(ps_global
->mail_stream
, ps_global
->msgmap
, SRT_NON
);
1028 report
= new_messages_string(ps_global
->mail_stream
);
1029 fname
= folder_name_decoded((unsigned char *)newfolder
);
1030 q_status_message7(SM_ORDER
, 0, 4,
1031 "%s \"%s\" opened with %s message%s%s%s%s",
1032 IS_NEWS(ps_global
->mail_stream
)
1033 ? "News group" : "Folder",
1034 open_inbox
? pretty_fn(fname
? (char *) fname
: newfolder
)
1035 : (fname
? (char *)fname
: newfolder
),
1036 comatose(mn_get_total(ps_global
->msgmap
)),
1037 plural(mn_get_total(ps_global
->msgmap
)),
1039 && sp_flagged(ps_global
->mail_stream
, SP_PERMLOCKED
))
1040 ? " (StayOpen)" : "",
1041 READONLY_FOLDER(ps_global
->mail_stream
)
1043 report
? report
: "");
1046 fs_give((void **)&fname
);
1049 fs_give((void **)&report
);
1052 mswin_settitle(pretty_fn(newfolder
));
1055 * Set current message number when re-opening Stay-Open or
1058 if(rflags
& SP_MATCH
){
1059 if(rflags
& SP_LOCKED
){
1060 if(F_OFF(F_STARTUP_STAYOPEN
, ps_global
)
1061 && (cur
= get_msgno_by_msg_id(ps_global
->mail_stream
,
1062 sp_saved_cur_msg_id(ps_global
->mail_stream
),
1063 ps_global
->msgmap
)) >= 1L
1064 && cur
<= mn_get_total(ps_global
->msgmap
)){
1066 mn_set_cur(ps_global
->msgmap
, (MsgNo
) cur
);
1067 if(flags
& DB_FROMTAB
){
1069 * When we TAB to a folder that is a StayOpen folder we try
1070 * to increment the current message # by one instead of doing
1071 * some search again. Some people probably won't like this
1072 * behavior, especially if the new message that has arrived
1073 * comes before where we are in the index. That's why we have
1074 * the F_STARTUP_STAYOPEN feature above.
1076 mn_inc_cur(m
, ps_global
->msgmap
, MH_NONE
);
1078 /* else leave it where it is */
1080 adjust_cur_to_visible(ps_global
->mail_stream
, ps_global
->msgmap
);
1085 * If we're reopening a cached open stream that wasn't explicitly
1086 * kept open by the user, then the user expects it to act pretty
1087 * much like we are re-opening the stream. A problem is that the
1088 * recent messages are still recent because we haven't closed the
1089 * stream, so we fake a quasi-recentness by remembering the last
1090 * uid assigned on the stream when we pine_mail_close. Then when
1091 * we come back messages with uids higher than that are recent.
1093 * If uid_validity has changed, then we don't use any special
1094 * treatment, but just do the regular search.
1096 if(m
->uid_validity
== sp_saved_uid_validity(m
)){
1100 * Because first_sorted_flagged uses sequence numbers, find the
1101 * sequence number of the first message after the old last
1102 * uid assigned. I.e., the first recent message.
1104 for(i
= m
->nmsgs
; i
> 0L; i
--)
1105 if(mail_uid(m
, i
) <= sp_saved_uid_last(m
))
1108 if(i
> 0L && i
< m
->nmsgs
)
1115 if(!cur_already_set
&& mn_get_total(ps_global
->msgmap
) > 0L){
1117 perfolder_startup_rule
= reset_startup_rule(ps_global
->mail_stream
);
1119 if(ps_global
->start_entry
> 0){
1120 mn_set_cur(ps_global
->msgmap
, mn_get_revsort(ps_global
->msgmap
)
1121 ? first_sorted_flagged(F_NONE
, m
,
1122 ps_global
->start_entry
,
1123 THREADING() ? 0 : FSF_SKIP_CHID
)
1124 : first_sorted_flagged(F_SRCHBACK
, m
,
1125 ps_global
->start_entry
,
1126 THREADING() ? 0 : FSF_SKIP_CHID
));
1127 ps_global
->start_entry
= 0;
1129 else if(perfolder_startup_rule
!= IS_NOTSET
||
1131 ps_global
->context_current
->use
& CNTXT_INCMNG
){
1132 unsigned use_this_startup_rule
;
1134 if(perfolder_startup_rule
!= IS_NOTSET
)
1135 use_this_startup_rule
= perfolder_startup_rule
;
1137 use_this_startup_rule
= ps_global
->inc_startup_rule
;
1139 switch(use_this_startup_rule
){
1141 * For news in incoming collection we're doing the same thing
1142 * for first-unseen and first-recent. In both those cases you
1143 * get first-unseen if FAKE_NEW is off and first-recent if
1144 * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the
1145 * same as first recent because all recent msgs are unseen
1146 * and all unrecent msgs are seen (see pine_mail_open).
1148 case IS_FIRST_UNSEEN
:
1150 mn_set_cur(ps_global
->msgmap
,
1152 && mn_get_sort(ps_global
->msgmap
) == SortArrival
1153 && !mn_get_revsort(ps_global
->msgmap
)
1154 && !get_lflag(ps_global
->mail_stream
, NULL
,
1155 sp_first_unseen(m
), MN_EXLD
)
1156 && (n
= mn_raw2m(ps_global
->msgmap
,
1157 sp_first_unseen(m
))))
1159 : first_sorted_flagged(F_UNSEEN
| F_UNDEL
, m
, pc
,
1160 THREADING() ? 0 : FSF_SKIP_CHID
));
1163 case IS_FIRST_RECENT
:
1166 * We could really use recent for news but this is the way
1167 * it has always worked, so we'll leave it. That is, if
1168 * the FAKE_NEW feature is on, recent and unseen are
1169 * equivalent, so it doesn't matter. If the feature isn't
1170 * on, all the undeleted messages are unseen and we start
1171 * at the first one. User controls with the FAKE_NEW feature.
1173 if(IS_NEWS(ps_global
->mail_stream
)){
1174 mn_set_cur(ps_global
->msgmap
,
1175 first_sorted_flagged(F_UNSEEN
|F_UNDEL
, m
, pc
,
1176 THREADING() ? 0 : FSF_SKIP_CHID
));
1179 mn_set_cur(ps_global
->msgmap
,
1180 first_sorted_flagged(F_RECENT
| F_UNSEEN
1183 THREADING() ? 0 : FSF_SKIP_CHID
));
1187 case IS_FIRST_IMPORTANT
:
1188 mn_set_cur(ps_global
->msgmap
,
1189 first_sorted_flagged(F_FLAG
|F_UNDEL
, m
, pc
,
1190 THREADING() ? 0 : FSF_SKIP_CHID
));
1193 case IS_FIRST_IMPORTANT_OR_UNSEEN
:
1195 if(IS_NEWS(ps_global
->mail_stream
))
1199 MsgNo flagged
, first_unseen
;
1201 flagged
= first_sorted_flagged(F_FLAG
|F_UNDEL
, m
, pc
,
1202 THREADING() ? 0 : FSF_SKIP_CHID
);
1203 first_unseen
= (sp_first_unseen(m
)
1204 && mn_get_sort(ps_global
->msgmap
) == SortArrival
1205 && !mn_get_revsort(ps_global
->msgmap
)
1206 && !get_lflag(ps_global
->mail_stream
, NULL
,
1207 sp_first_unseen(m
), MN_EXLD
)
1208 && (n
= mn_raw2m(ps_global
->msgmap
,
1209 sp_first_unseen(m
))))
1211 : first_sorted_flagged(F_UNSEEN
|F_UNDEL
, m
, pc
,
1212 THREADING() ? 0 : FSF_SKIP_CHID
);
1213 mn_set_cur(ps_global
->msgmap
,
1214 (MsgNo
) MIN((int) flagged
, (int) first_unseen
));
1220 case IS_FIRST_IMPORTANT_OR_RECENT
:
1222 if(IS_NEWS(ps_global
->mail_stream
))
1226 MsgNo flagged
, first_recent
;
1228 flagged
= first_sorted_flagged(F_FLAG
|F_UNDEL
, m
, pc
,
1229 THREADING() ? 0 : FSF_SKIP_CHID
);
1230 first_recent
= first_sorted_flagged(F_RECENT
| F_UNSEEN
1233 THREADING() ? 0 : FSF_SKIP_CHID
);
1234 mn_set_cur(ps_global
->msgmap
,
1235 (MsgNo
) MIN((int) flagged
, (int) first_recent
));
1241 mn_set_cur(ps_global
->msgmap
,
1242 first_sorted_flagged(F_UNDEL
, m
, pc
,
1243 THREADING() ? 0 : FSF_SKIP_CHID
));
1247 mn_set_cur(ps_global
->msgmap
,
1248 first_sorted_flagged(F_UNDEL
, m
, pc
,
1249 FSF_LAST
| (THREADING() ? 0 : FSF_SKIP_CHID
)));
1253 alpine_panic("Unexpected incoming startup case");
1258 else if(IS_NEWS(ps_global
->mail_stream
)){
1260 * This will go to two different places depending on the FAKE_NEW
1261 * feature (see pine_mail_open).
1263 mn_set_cur(ps_global
->msgmap
,
1264 first_sorted_flagged(F_UNSEEN
|F_UNDEL
, m
, pc
,
1265 THREADING() ? 0 : FSF_SKIP_CHID
));
1268 mn_set_cur(ps_global
->msgmap
,
1269 mn_get_revsort(ps_global
->msgmap
)
1271 : mn_get_total(ps_global
->msgmap
));
1274 adjust_cur_to_visible(ps_global
->mail_stream
, ps_global
->msgmap
);
1276 else if(!(rflags
& SP_MATCH
)){
1277 mn_set_cur(ps_global
->msgmap
, -1L);
1280 if(ps_global
->mail_stream
)
1281 sp_set_first_unseen(ps_global
->mail_stream
, 0L);
1287 /*----------------------------------------------------------------------
1288 Expand a folder name, taking account of the folders_dir and
1289 any home directory reference
1291 Args: filename -- The name of the file that is the folder
1293 Result: The folder name is expanded in place.
1294 Returns 0 and queues status message if unsuccessful.
1295 Input string is overwritten with expanded name.
1296 Returns 1 if successful.
1300 expand_foldername(char *filename
, size_t len
)
1302 char temp_filename
[MAXPATH
+1];
1304 dprint((5, "=== expand_foldername called (%s) ===\n",
1305 filename
? filename
: "?"));
1308 * We used to check for valid filename chars here if "filename"
1309 * didn't refer to a remote mailbox. This has been rethought
1312 strncpy(temp_filename
, filename
, sizeof(temp_filename
)-1);
1313 temp_filename
[sizeof(temp_filename
)-1] = '\0';
1314 if(strucmp(temp_filename
, "inbox") == 0) {
1315 strncpy(filename
, ps_global
->VAR_INBOX_PATH
== NULL
? "inbox" :
1316 ps_global
->VAR_INBOX_PATH
, len
-1);
1317 filename
[len
-1] = '\0';
1318 } else if(temp_filename
[0] == '{') {
1319 strncpy(filename
, temp_filename
, len
-1);
1320 filename
[len
-1] = '\0';
1321 } else if(ps_global
->restricted
&& filename_is_restricted(temp_filename
)){
1322 q_status_message(SM_ORDER
, 0, 3, "Can only open local folders");
1324 } else if(temp_filename
[0] == '*') {
1325 strncpy(filename
, temp_filename
, len
-1);
1326 filename
[len
-1] = '\0';
1327 } else if(ps_global
->VAR_OPER_DIR
&& filename_parent_ref(temp_filename
)){
1328 q_status_message(SM_ORDER
, 0, 3,
1329 "\"..\" not allowed in folder name");
1331 } else if (is_homedir_path(temp_filename
)){
1332 if(fnexpand(temp_filename
, sizeof(temp_filename
)) == NULL
) {
1333 q_status_message1(SM_ORDER
, 3, 3, "Error expanding folder \"%s\"", temp_filename
);
1336 strncpy(filename
, temp_filename
, len
-1);
1337 filename
[len
-1] = '\0';
1338 } else if(F_ON(F_USE_CURRENT_DIR
, ps_global
) || is_absolute_path(temp_filename
)){
1339 strncpy(filename
, temp_filename
, len
-1);
1340 filename
[len
-1] = '\0';
1341 } else if(ps_global
->VAR_OPER_DIR
){
1342 build_path(filename
, ps_global
->VAR_OPER_DIR
, temp_filename
, len
);
1344 build_path(filename
,
1346 ps_global
->folders_dir
,
1348 ps_global
->home_dir
,
1350 temp_filename
, len
);
1353 dprint((5, "returning \"%s\"\n", filename
? filename
: "?"));
1359 /*----------------------------------------------------------------------
1360 Expunge (if confirmed) and close a mail stream
1362 Args: stream -- The MAILSTREAM * to close
1363 final_msg -- If non-null, this should be set to point to a
1364 message to print out in the caller, it is allocated
1365 here and freed by the caller.
1367 Result: Mail box is expunged and closed. A message is displayed to
1371 expunge_and_close(MAILSTREAM
*stream
, char **final_msg
, long unsigned int flags
)
1373 long i
, delete_count
, seen_not_del
;
1374 char buff1
[MAX_SCREEN_COLS
+1], *moved_msg
= NULL
,
1375 buff2
[MAX_SCREEN_COLS
+1], *folder
;
1377 struct variable
*vars
= ps_global
->vars
;
1378 int ret
, expunge
= FALSE
, no_close
= 0;
1381 no_close
= (flags
& EC_NO_CLOSE
);
1383 if(!(stream
&& sp_flagged(stream
, SP_LOCKED
)))
1386 /* check for dead stream */
1387 if(stream
&& sp_dead_stream(stream
)){
1388 pine_mail_actually_close(stream
);
1393 context
= sp_context(stream
);
1394 folder
= STREAMNAME(stream
);
1396 dprint((2, "expunge_and_close: \"%s\"%s\n",
1397 folder
, no_close
? " (NO_CLOSE bit set)" : ""));
1399 update_folder_unseen_by_stream(stream
, UFU_NONE
);
1402 strncpy(ing
, "ed", sizeof(ing
));
1404 strncpy(ing
, "ing", sizeof(ing
));
1406 ing
[sizeof(ing
)-1] = '\0';
1411 if(!stream
->rdonly
){
1413 if(pith_opt_begin_closing
)
1414 (*pith_opt_begin_closing
)(flags
, folder
);
1416 mail_expunge_prefilter(stream
, MI_CLOSING
);
1419 * Be sure to expunge any excluded (filtered) msgs
1420 * Do it here so they're not copied into read/archived
1421 * folders *AND* to be sure we don't refilter them
1422 * next time the folder's opened.
1424 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
1425 if(get_lflag(stream
, NULL
, i
, MN_EXLD
)){ /* if there are any */
1426 delete_filtered_msgs(stream
); /* delete them all */
1431 /* Save read messages? */
1432 if(VAR_READ_MESSAGE_FOLDER
&& VAR_READ_MESSAGE_FOLDER
[0]
1433 && sp_flagged(stream
, SP_INBOX
)
1434 && (seen_not_del
= count_flagged(stream
, F_SEEN
| F_UNDEL
))){
1436 if(F_ON(F_AUTO_READ_MSGS
,ps_global
)
1437 || (pith_opt_read_msg_prompt
1438 && (*pith_opt_read_msg_prompt
)(seen_not_del
, VAR_READ_MESSAGE_FOLDER
)))
1439 /* move inbox's read messages */
1440 moved_msg
= move_read_msgs(stream
, VAR_READ_MESSAGE_FOLDER
,
1441 buff1
, sizeof(buff1
), -1L);
1443 else if(VAR_ARCHIVED_FOLDERS
)
1444 moved_msg
= move_read_incoming(stream
, context
, folder
,
1445 VAR_ARCHIVED_FOLDERS
,
1446 buff1
, sizeof(buff1
));
1449 * We need the count_flagged to be executed not only to set
1450 * delete_count, but also to set the searched bits in all of
1451 * the deleted messages. The searched bit is used in the monkey
1452 * business section below which undeletes deleted messages
1453 * before expunging. It determines which messages are deleted
1454 * by examining the searched bit, which had better be set or not
1455 * based on this count_flagged call rather than some random
1456 * search that happened earlier.
1458 delete_count
= count_flagged(stream
, F_DEL
);
1459 if(F_ON(F_EXPUNGE_MANUALLY
,ps_global
))
1463 if(!ps_global
->noexpunge_on_close
&& delete_count
){
1465 if(F_ON(F_FULL_AUTO_EXPUNGE
,ps_global
)
1466 || (F_ON(F_AUTO_EXPUNGE
, ps_global
)
1467 && ((!strucmp(folder
,ps_global
->inbox_name
))
1468 || (context
&& (context
->use
& CNTXT_INCMNG
)))
1469 && context_isambig(folder
))){
1472 else if(pith_opt_expunge_prompt
){
1473 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
1474 ret
= (*pith_opt_expunge_prompt
)(stream
, pretty_fn((char *)fname
), delete_count
);
1475 if(fname
) fs_give((void **) &fname
);
1478 /* get this message back in queue */
1480 q_status_message(SM_ORDER
,
1481 F_ON(F_AUTO_READ_MSGS
,ps_global
) ? 0 : 3, 5, moved_msg
);
1485 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
1487 filtered
= any_lflagged(sp_msgmap(stream
), MN_EXLD
);
1489 snprintf(buff2
, sizeof(buff2
),
1490 "%s%s%s%.30s%s%s %s message%s and remov%s %s.",
1491 no_close
? "" : "Clos",
1492 no_close
? "" : ing
,
1493 no_close
? "" : " \"",
1494 no_close
? "" : pretty_fn((char *)fname
),
1495 no_close
? "" : "\". ",
1496 final_msg
? "Kept" : "Keeping",
1497 comatose(stream
->nmsgs
- filtered
- delete_count
),
1498 plural(stream
->nmsgs
- filtered
- delete_count
),
1500 long2string(delete_count
));
1501 if(fname
) fs_give((void **)&fname
);
1503 *final_msg
= cpystr(buff2
);
1505 q_status_message(SM_ORDER
,
1507 (F_ON(F_AUTO_EXPUNGE
,ps_global
)
1508 || F_ON(F_FULL_AUTO_EXPUNGE
,ps_global
))
1512 flush_status_messages(1);
1513 ps_global
->mm_log_error
= 0;
1514 ps_global
->expunge_in_progress
= 1;
1515 mail_expunge(stream
);
1516 ps_global
->expunge_in_progress
= 0;
1517 if(ps_global
->mm_log_error
&& final_msg
&& *final_msg
){
1518 fs_give((void **)final_msg
);
1525 if(!ps_global
->noexpunge_on_close
&& expunge
){
1531 * filtered message monkey business.
1533 * 1) light sequence bits for legit deleted msgs
1534 * and store marker in local extension
1535 * 2) clear their deleted flag
1536 * 3) perform expunge to removed filtered msgs
1537 * 4) restore deleted flags for legit msgs
1538 * based on local extension bit
1540 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
1541 if(!get_lflag(stream
, NULL
, i
, MN_EXLD
)
1542 && (((mc
= mail_elt(stream
, i
)) && mc
->valid
&& mc
->deleted
)
1543 || (mc
&& !mc
->valid
&& mc
->searched
))){
1545 expbits
= MSG_EX_DELETE
;
1546 msgno_exceptions(stream
, i
, "0", &expbits
, TRUE
);
1548 else if((mc
= mail_elt(stream
, i
)) != NULL
)
1551 if((seq
= build_sequence(stream
, NULL
, NULL
)) != NULL
){
1552 mail_flag(stream
, seq
, "\\DELETED", ST_SILENT
);
1553 fs_give((void **) &seq
);
1556 ps_global
->mm_log_error
= 0;
1557 ps_global
->expunge_in_progress
= 1;
1558 mail_expunge(stream
);
1559 ps_global
->expunge_in_progress
= 0;
1561 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
1562 if((mc
= mail_elt(stream
, i
)) != NULL
)
1564 = (msgno_exceptions(stream
, i
, "0", &expbits
, FALSE
)
1565 && (expbits
& MSG_EX_DELETE
));
1567 if((seq
= build_sequence(stream
, NULL
, NULL
)) != NULL
){
1568 mail_flag(stream
, seq
, "\\DELETED", ST_SET
|ST_SILENT
);
1569 fs_give((void **) &seq
);
1573 if(!no_close
){ /* MAX_SCREEN_COLS+1 = sizeof(buff2) */
1574 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
1576 snprintf(buff2
, sizeof(buff2
),
1577 "Clos%s folder \"%.*s\". %s%s%s message%s.",
1579 MAX_SCREEN_COLS
+1-50, pretty_fn((char *) fname
),
1580 final_msg
? "Kept" : "Keeping",
1581 (stream
->nmsgs
== 1L) ? " single" : " all ",
1582 (stream
->nmsgs
> 1L)
1583 ? comatose(stream
->nmsgs
) : "",
1584 plural(stream
->nmsgs
));
1586 else{ /* MAX_SCREEN_COLS+1 = sizeof(buff2) */
1587 snprintf(buff2
, sizeof(buff2
), "Clos%s empty folder \"%.*s\"",
1588 ing
, MAX_SCREEN_COLS
+1-50, pretty_fn((char *) fname
));
1590 if(fname
) fs_give((void **)&fname
);
1593 *final_msg
= cpystr(buff2
);
1595 q_status_message(SM_ORDER
, 0, 3, buff2
);
1600 if(IS_NEWS(stream
)){
1602 * Mark the filtered messages deleted so they aren't
1603 * filtered next time.
1605 for(i
= 1L; i
<= stream
->nmsgs
; i
++){
1607 if(msgno_exceptions(stream
, i
, "0" , &exbits
, FALSE
)
1608 && (exbits
& MSG_EX_FILTERED
)){
1609 delete_filtered_msgs(stream
);
1613 /* first, look to archive read messages */
1614 if((moved_msg
= move_read_incoming(stream
, context
, folder
,
1615 VAR_ARCHIVED_FOLDERS
,
1616 buff1
, sizeof(buff1
))) != NULL
)
1617 q_status_message(SM_ORDER
,
1618 F_ON(F_AUTO_READ_MSGS
,ps_global
) ? 0 : 3, 5, moved_msg
);
1619 /* MAX_SCREEN_COLS+1 = sizeof(buff2) */
1620 snprintf(buff2
, sizeof(buff2
), "Clos%s news group \"%.*s\"",
1621 ing
, MAX_SCREEN_COLS
+1-50, pretty_fn(folder
));
1623 if(F_ON(F_NEWS_CATCHUP
, ps_global
)){
1626 /* count visible messages */
1627 (void) count_flagged(stream
, F_DEL
);
1628 for(i
= 1L, delete_count
= 0L; i
<= stream
->nmsgs
; i
++)
1629 if(!(get_lflag(stream
, NULL
, i
, MN_EXLD
)
1630 || ((mc
= mail_elt(stream
, i
)) && mc
->valid
1632 || (mc
&& !mc
->valid
&& mc
->searched
)))
1635 if(delete_count
&& pith_opt_expunge_prompt
){
1636 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
1637 ret
= (*pith_opt_expunge_prompt
)(stream
, pretty_fn((char *) fname
), delete_count
);
1638 if(fname
) fs_give((void **)&fname
);
1642 snprintf(seq
, sizeof(seq
), "1:%ld", stream
->nmsgs
);
1643 mail_flag(stream
, seq
, "\\DELETED", ST_SET
|ST_SILENT
);
1648 if(F_ON(F_NEWS_CROSS_DELETE
, ps_global
))
1649 cross_delete_crossposts(stream
);
1651 else{ /* MAX_SCREEN_COLS+1 = sizeof(buff2) */
1652 unsigned char *fname
= folder_name_decoded((unsigned char *)folder
);
1653 snprintf(buff2
, sizeof(buff2
),
1654 "Clos%s read-only folder \"%.*s\". No changes to save",
1655 ing
, MAX_SCREEN_COLS
+1-60, pretty_fn((char *) fname
));
1656 if(fname
) fs_give((void **)&fname
);
1660 *final_msg
= cpystr(buff2
);
1662 q_status_message(SM_ORDER
, 0, 2, buff2
);
1666 * Make darn sure any mm_log fallout caused above get's seen...
1669 flush_status_messages(1);
1670 pine_mail_close(stream
);
1677 agg_select_all(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int *diff
, int on
)
1680 int hidden
= any_lflagged(msgmap
, MN_HIDE
) > 0L;
1682 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++){
1683 if(on
){ /* mark 'em all */
1684 set_lflag(stream
, msgmap
, i
, MN_SLCT
, 1);
1686 else { /* unmark 'em all */
1687 if(get_lflag(stream
, msgmap
, i
, MN_SLCT
)){
1691 set_lflag(stream
, msgmap
, i
, MN_SLCT
, 0);
1694 set_lflag(stream
, msgmap
, i
, MN_HIDE
, 0);
1700 /*----------------------------------------------------------------------
1701 Move all read messages from srcfldr to dstfldr
1703 Args: stream -- stream to usr
1704 dstfldr -- folder to receive moved messages
1705 buf -- place to write success message
1707 Returns: success message or NULL for failure
1710 move_read_msgs(MAILSTREAM
*stream
, char *dstfldr
, char *buf
, size_t buflen
, long int searched
)
1714 MSGNO_S
*msgmap
= NULL
;
1715 CONTEXT_S
*save_context
= NULL
;
1719 if(!is_absolute_path(dstfldr
)
1720 && !(save_context
= default_save_context(ps_global
->context_list
)))
1721 save_context
= ps_global
->context_list
;
1724 * Use the "searched" bit to select the set of messages
1725 * we want to save. If searched is non-neg, the message
1726 * cache already has the necessary "searched" bits set.
1729 searched
= count_flagged(stream
, F_SEEN
| F_UNDEL
);
1733 * We're going to be messing with SLCT flags in order
1734 * to do our work. If this stream is a StayOpen stream
1735 * we want to restore those flags after we're done
1736 * using them. So copy them into STMP so we can put them
1739 msgmap
= sp_msgmap(stream
);
1740 if(sp_flagged(stream
, SP_PERMLOCKED
))
1741 copy_lflags(stream
, msgmap
, MN_SLCT
, MN_STMP
);
1743 set_lflags(stream
, msgmap
, MN_SLCT
, 0);
1745 /* select search results */
1746 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
1747 if((raw
= mn_m2raw(msgmap
, i
)) > 0L && stream
1748 && raw
<= stream
->nmsgs
1749 && (mc
= mail_elt(stream
,raw
))
1750 && ((mc
->valid
&& mc
->seen
&& !mc
->deleted
)
1751 || (!mc
->valid
&& mc
->searched
)))
1752 set_lflag(stream
, msgmap
, i
, MN_SLCT
, 1);
1754 pseudo_selected(stream
, msgmap
);
1755 snprintf(buf
, buflen
, "Moving %s read message%s to \"%s\"",
1756 comatose(searched
), plural(searched
), dstfldr
);
1757 we_cancel
= busy_cue(buf
, NULL
, 0);
1758 if(save(ps_global
, stream
, save_context
, dstfldr
, msgmap
,
1759 SV_DELETE
| SV_FIX_DELS
| SV_INBOXWOCNTXT
) == searched
)
1760 strncpy(bufp
= buf
+ 1, "Moved", MIN(5,buflen
)); /* change Moving to Moved */
1762 buf
[buflen
-1] = '\0';
1764 cancel_busy_cue(bufp
? 0 : -1);
1766 if(sp_flagged(stream
, SP_PERMLOCKED
)){
1767 restore_selected(msgmap
);
1768 copy_lflags(stream
, msgmap
, MN_STMP
, MN_SLCT
);
1776 /*----------------------------------------------------------------------
1777 Move read messages from folder if listed in archive
1783 move_read_incoming(MAILSTREAM
*stream
, CONTEXT_S
*context
, char *folder
,
1784 char **archive
, char *buf
, size_t buflen
)
1786 char *s
, *d
, *f
= folder
;
1789 if(buf
&& buflen
> 0)
1792 if(archive
&& !sp_flagged(stream
, SP_INBOX
)
1793 && context
&& (context
->use
& CNTXT_INCMNG
)
1794 && ((context_isambig(folder
)
1795 && folder_is_nick(folder
, FOLDERS(context
), 0))
1796 || folder_index(folder
, context
, FI_FOLDER
) > 0)
1797 && (seen_undel
= count_flagged(stream
, F_SEEN
| F_UNDEL
))){
1799 for(; f
&& *archive
; archive
++){
1802 get_pair(*archive
, &s
, &d
, 1, 0);
1804 && (!strcmp(s
, folder
)
1805 || (context_isambig(folder
)
1806 && (p
= folder_is_nick(folder
, FOLDERS(context
), 0))
1807 && !strcmp(s
, p
)))){
1808 if(F_ON(F_AUTO_READ_MSGS
,ps_global
)
1809 || (pith_opt_read_msg_prompt
1810 && (*pith_opt_read_msg_prompt
)(seen_undel
, d
)))
1811 buf
= move_read_msgs(stream
, d
, buf
, buflen
, seen_undel
);
1813 f
= NULL
; /* bust out after cleaning up */
1816 fs_give((void **)&s
);
1817 fs_give((void **)&d
);
1821 return((buf
&& *buf
) ? buf
: NULL
);
1825 /*----------------------------------------------------------------------
1826 Delete all references to a deleted news posting
1831 cross_delete_crossposts(MAILSTREAM
*stream
)
1833 if(count_flagged(stream
, F_DEL
)){
1834 static char *fields
[] = {"Xref", NULL
};
1835 MAILSTREAM
*tstream
;
1836 CONTEXT_S
*fake_context
;
1837 char *xref
, *p
, *group
, *uidp
,
1838 *newgrp
, newfolder
[MAILTMPLEN
];
1839 long i
, hostlatch
= 0L;
1844 strncpy(newfolder
, stream
->mailbox
, sizeof(newfolder
));
1845 newfolder
[sizeof(newfolder
)-1] = '\0';
1846 if(!(newgrp
= strstr(newfolder
, "#news.")))
1847 return; /* weird mailbox */
1851 we_cancel
= busy_cue("Busy deleting crosspostings", NULL
, 1);
1853 /* build subscribed list */
1854 strncpy(newgrp
, "[]", sizeof(newfolder
)-(newgrp
-newfolder
));
1855 newfolder
[sizeof(newfolder
)-1] = '\0';
1856 fake_context
= new_context(newfolder
, 0);
1857 build_folder_list(NULL
, fake_context
, "*", NULL
, BFL_LSUB
);
1859 for(i
= 1L; i
<= stream
->nmsgs
; i
++)
1860 if(!get_lflag(stream
, NULL
, i
, MN_EXLD
)
1861 && (mc
= mail_elt(stream
, i
)) && mc
->deleted
){
1863 if((xref
= pine_fetchheader_lines(stream
, i
, NULL
, fields
)) != NULL
){
1864 if((p
= strstr(xref
, ": ")) != NULL
){
1872 while(*++p
&& !isspace((unsigned char) *p
))
1878 /* tie off uid/host */
1884 * For the nonce, we're only deleting valid
1885 * uid's from outside the current newsgroup
1886 * and inside only subscribed newsgroups
1888 if(strcmp(group
, stream
->mailbox
1889 + (newgrp
- newfolder
))
1890 && folder_index(group
, fake_context
,
1892 if((uid
= strtoul(uidp
, NULL
, 10)) != 0L){
1893 strncpy(newgrp
, group
, sizeof(newfolder
)-(newgrp
-newfolder
));
1894 newfolder
[sizeof(newfolder
)-1] = '\0';
1895 if((tstream
= pine_mail_open(NULL
,
1899 mail_flag(tstream
, ulong2string(uid
),
1902 pine_mail_close(tstream
);
1906 break; /* bogus uid */
1909 else if(!hostlatch
++){
1912 if(stream
->mailbox
[0] == '{'
1913 && !((p
= strpbrk(stream
->mailbox
+1, "}:/"))
1914 && !struncmp(stream
->mailbox
+ 1,
1915 q
= canonical_name(group
),
1916 p
- (stream
->mailbox
+ 1))
1917 && q
[p
- (stream
->mailbox
+ 1)] == '\0'))
1918 break; /* different server? */
1921 break; /* bogus field! */
1925 fs_give((void **) &xref
);
1929 free_context(&fake_context
);
1938 * Original version from Eduardo Chappa.
1940 * Returns a string describing the number of new/unseen messages
1941 * for use in the status line. Can return NULL. Caller must free the memory.
1944 new_messages_string(MAILSTREAM
*stream
)
1946 char message
[80] = {'\0'};
1947 long new = 0L, uns
= 0L;
1948 int i
, imapstatus
= 0;
1950 for (i
= 0; ps_global
->index_disp_format
[i
].ctype
!= iNothing
1951 && ps_global
->index_disp_format
[i
].ctype
!= iIStatus
1952 && ps_global
->index_disp_format
[i
].ctype
!= iSIStatus
; i
++)
1955 imapstatus
= ps_global
->index_disp_format
[i
].ctype
== iIStatus
1956 || ps_global
->index_disp_format
[i
].ctype
== iSIStatus
;
1958 get_new_message_count(stream
, imapstatus
, &new, &uns
);
1961 snprintf(message
, sizeof(message
), " - %s%s%s%s%s%s%s",
1962 uns
!= 0L ? comatose((long) new) : "",
1963 uns
!= 0L ? " " : "",
1964 uns
!= 0L ? _("recent") : "",
1965 uns
> 0L ? ", " : "",
1966 uns
!= -1L ? comatose((long) uns
) : "",
1967 uns
!= -1L ? " " : "",
1968 uns
!= -1L ? _("unseen") : "");
1969 else if(!imapstatus
&& new > 0L)
1970 snprintf(message
, sizeof(message
), " - %s %s",
1971 comatose((long) new), _("new"));
1973 return(*message
? cpystr(message
) : NULL
);
1978 get_new_message_count(MAILSTREAM
*stream
, int imapstatus
,
1979 long *new, long *unseen
)
1989 *new = count_flagged(stream
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
1991 if(!IS_NEWS(stream
)){
1993 *unseen
= count_flagged(stream
, F_UNSEEN
| F_UNDEL
);
1999 if(IS_NEWS(stream
)){
2000 if(F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
)){
2002 *new = count_flagged(stream
, F_RECENT
| F_UNSEEN
| F_UNDEL
);
2007 *new = count_flagged(stream
, F_UNSEEN
| F_UNDEL
| F_UNANS
);
2013 /*----------------------------------------------------------------------
2014 ZOOM the message index (set any and all necessary hidden flag bits)
2016 Args: state -- usual pine state
2017 msgmap -- usual message mapping
2018 Returns: number of messages zoomed in on
2022 zoom_index(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
, int onflag
)
2024 long i
, count
= 0L, first
= 0L, msgno
;
2025 PINETHRD_S
*thrd
= NULL
, *topthrd
= NULL
, *nthrd
;
2027 if(any_lflagged(msgmap
, onflag
)){
2029 if(THREADING() && sp_viewing_a_thread(stream
)){
2030 /* get top of current thread */
2031 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
2032 if(thrd
&& thrd
->top
)
2033 topthrd
= fetch_thread(stream
, thrd
->top
);
2036 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++){
2037 if(!get_lflag(stream
, msgmap
, i
, onflag
)){
2038 set_lflag(stream
, msgmap
, i
, MN_HIDE
, 1);
2043 * Because subject lines depend on whether or not
2044 * other parts of the thread above us are visible or not.
2046 if(THREADING() && !THRD_INDX()
2047 && ps_global
->thread_disp_style
== THREAD_MUTTLIKE
)
2048 clear_index_cache_ent(stream
, i
, 0);
2051 * If a selected message is hidden beneath a collapsed
2052 * thread (not beneath a thread index line, but a collapsed
2053 * thread or subthread) then we make it visible. The user
2054 * should be able to see the selected messages when they
2055 * Zoom. We could get a bit fancier and re-collapse the
2056 * thread when the user unzooms, but we don't do that
2059 if(THREADING() && !THRD_INDX()
2060 && get_lflag(stream
, msgmap
, i
, MN_CHID
)){
2063 * What we need to do is to unhide this message and
2064 * uncollapse any parent above us.
2065 * Also, when we uncollapse a parent, we need to
2066 * trace back down the tree and unhide until we get
2067 * to a collapse point or the end. That's what
2068 * set_thread_subtree does.
2071 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, i
));
2073 if(thrd
&& thrd
->parent
)
2074 thrd
= fetch_thread(stream
, thrd
->parent
);
2078 /* unhide and uncollapse its parents */
2080 /* if this parent is collapsed */
2081 if(get_lflag(stream
, NULL
, thrd
->rawno
, MN_COLL
)){
2082 /* uncollapse this parent and unhide its subtree */
2083 msgno
= mn_raw2m(msgmap
, thrd
->rawno
);
2084 if(msgno
> 0L && msgno
<= mn_get_total(msgmap
)){
2085 set_lflag(stream
, msgmap
, msgno
,
2086 MN_COLL
| MN_CHID
, 0);
2088 (nthrd
= fetch_thread(stream
, thrd
->next
)))
2089 set_thread_subtree(stream
, nthrd
, msgmap
,
2093 /* collapse symbol will be wrong */
2094 clear_index_cache_ent(stream
, msgno
, 0);
2098 * Continue up tree to next parent looking for
2099 * more collapse points.
2102 thrd
= fetch_thread(stream
, thrd
->parent
);
2111 /* find msgno of top of thread for msg i */
2112 if((thrd
=fetch_thread(stream
, mn_m2raw(msgmap
, i
)))
2114 first
= mn_raw2m(msgmap
, thrd
->top
);
2116 else if(THREADING() && sp_viewing_a_thread(stream
)){
2117 /* want first selected message in this thread */
2119 && (thrd
=fetch_thread(stream
, mn_m2raw(msgmap
, i
)))
2121 && topthrd
->rawno
== thrd
->top
)
2131 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
2132 if(count_lflags_in_thread(stream
, thrd
, msgmap
, onflag
) == 0)
2133 mn_set_cur(msgmap
, first
);
2135 else if((THREADING() && sp_viewing_a_thread(stream
))
2136 || !get_lflag(stream
, msgmap
, mn_get_cur(msgmap
), onflag
)){
2141 * Nothing was selected in the thread we were in, so
2142 * drop back to the Thread Index instead. Set the current
2143 * thread to the first one that has a selection in it.
2146 unview_thread(state
, stream
, msgmap
);
2148 i
= next_sorted_flagged(F_UNDEL
, stream
, 1L, &flags
);
2150 if(flags
& NSF_FLAG_MATCH
2151 && (thrd
=fetch_thread(stream
, mn_m2raw(msgmap
, i
)))
2153 first
= mn_raw2m(msgmap
, thrd
->top
);
2155 first
= 1L; /* can't happen */
2157 mn_set_cur(msgmap
, first
);
2160 if(msgline_hidden(stream
, msgmap
, mn_get_cur(msgmap
), 0))
2161 mn_set_cur(msgmap
, first
);
2171 /*----------------------------------------------------------------------
2172 UnZOOM the message index (clear any and all hidden flag bits)
2174 Args: state -- usual pine state
2175 msgmap -- usual message mapping
2176 Returns: 1 if hidden bits to clear and they were, 0 if none to clear
2180 unzoom_index(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
)
2184 if(!any_lflagged(msgmap
, MN_HIDE
))
2187 for(i
= 1L; i
<= mn_get_total(msgmap
); i
++)
2188 set_lflag(stream
, msgmap
, i
, MN_HIDE
, 0);
2195 agg_text_select(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, char type
, char *namehdr
,
2196 int not, int check_for_my_addresses
,
2197 char *sstring
, char *charset
, SEARCHSET
**limitsrch
)
2199 int old_imap
, we_cancel
;
2200 int me_with_regex
= 0;
2202 SEARCHPGM
*srchpgm
, *pgm
, *secondpgm
= NULL
, *thirdpgm
= NULL
;
2203 SEARCHPGM
*mepgm
= NULL
;
2208 old_imap
= (is_imap_stream(stream
) && !modern_imap_stream(stream
));
2211 * Special case code for matching one of the user's addresses.
2213 if(check_for_my_addresses
){
2216 if(F_OFF(F_DISABLE_REGEX
, ps_global
)){
2217 for(t
= ps_global
->VAR_ALT_ADDRS
; !me_with_regex
&& t
&& t
[0] && t
[0][0]; t
++){
2219 if(contains_regex_special_chars(alt
))
2225 * In this case we can't use search because it doesn't support
2226 * regex. So we have to manually do the whole thing ourselves.
2227 * The searching is done in the subroutine and the searched bits
2228 * will be set on return.
2231 search_for_our_regex_addresses(stream
, type
, not, limitsrch
? *limitsrch
: NULL
);
2235 PATGRP_S
*patgrp
= NULL
;
2236 PATTERN_S
*p
= NULL
;
2237 PATTERN_S
*pattern
= NULL
, **nextp
;
2241 * We're going to use the pattern matching machinery to generate
2242 * a search program. We build a pattern whose only purpose is
2243 * to generate the program.
2247 /* add standard me addresses to list */
2248 if(ps_global
->VAR_USER_ID
){
2249 if(ps_global
->userdomain
&& ps_global
->userdomain
[0]){
2250 p
= (PATTERN_S
*) fs_get(sizeof(*p
));
2251 memset((void *) p
, 0, sizeof(*p
));
2252 snprintf(buf
, sizeof(buf
), "%s@%s", ps_global
->VAR_USER_ID
,
2253 ps_global
->userdomain
);
2254 p
->substring
= cpystr(buf
);
2259 if(!ps_global
->userdomain
&& ps_global
->localdomain
&& ps_global
->localdomain
[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
->localdomain
);
2264 p
->substring
= cpystr(buf
);
2269 if(!ps_global
->userdomain
&& ps_global
->hostname
&& ps_global
->hostname
[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
->hostname
);
2274 p
->substring
= cpystr(buf
);
2280 /* add user's alternate addresses */
2281 for(t
= ps_global
->VAR_ALT_ADDRS
; t
&& t
[0] && t
[0][0]; t
++){
2284 p
= (PATTERN_S
*) fs_get(sizeof(*p
));
2285 memset((void *) p
, 0, sizeof(*p
));
2286 p
->substring
= cpystr(alt
);
2292 patgrp
= (PATGRP_S
*) fs_get(sizeof(*patgrp
));
2293 memset((void *) patgrp
, 0, sizeof(*patgrp
));
2297 patgrp
->recip
= pattern
;
2300 patgrp
->partic
= pattern
;
2303 patgrp
->from
= pattern
;
2306 patgrp
->cc
= pattern
;
2309 patgrp
->to
= pattern
;
2312 q_status_message(SM_ORDER
, 3, 3, "Unhandled case in agg_text_select");
2316 mepgm
= match_pattern_srchpgm(patgrp
, stream
, NULL
);
2318 free_patgrp(&patgrp
);
2323 if(not && !old_imap
){
2324 srchpgm
= mail_newsearchpgm();
2325 srchpgm
->not = mail_newsearchpgmlist();
2326 srchpgm
->not->pgm
= mepgm
;
2334 /* create a search program and fill it in */
2335 srchpgm
= pgm
= mail_newsearchpgm();
2336 if(not && !old_imap
){
2337 srchpgm
->not = mail_newsearchpgmlist();
2338 srchpgm
->not->pgm
= mail_newsearchpgm();
2339 pgm
= srchpgm
->not->pgm
;
2345 case 'h' : /* Any header */
2346 pgm
->header
= mail_newsearchheader (namehdr
, sstring
);
2349 case 'r' : /* TO or CC */
2351 /* No OR on old servers */
2352 pgm
->to
= mail_newstringlist();
2353 pgm
->to
->text
.data
= (unsigned char *) cpystr(sstring
);
2354 pgm
->to
->text
.size
= strlen(sstring
);
2355 secondpgm
= mail_newsearchpgm();
2356 secondpgm
->cc
= mail_newstringlist();
2357 secondpgm
->cc
->text
.data
= (unsigned char *) cpystr(sstring
);
2358 secondpgm
->cc
->text
.size
= strlen(sstring
);
2361 pgm
->or = mail_newsearchor();
2362 pgm
->or->first
->to
= mail_newstringlist();
2363 pgm
->or->first
->to
->text
.data
= (unsigned char *) cpystr(sstring
);
2364 pgm
->or->first
->to
->text
.size
= strlen(sstring
);
2365 pgm
->or->second
->cc
= mail_newstringlist();
2366 pgm
->or->second
->cc
->text
.data
= (unsigned char *) cpystr(sstring
);
2367 pgm
->or->second
->cc
->text
.size
= strlen(sstring
);
2372 case 'p' : /* TO or CC or FROM */
2374 /* No OR on old servers */
2375 pgm
->to
= mail_newstringlist();
2376 pgm
->to
->text
.data
= (unsigned char *) cpystr(sstring
);
2377 pgm
->to
->text
.size
= strlen(sstring
);
2378 secondpgm
= mail_newsearchpgm();
2379 secondpgm
->cc
= mail_newstringlist();
2380 secondpgm
->cc
->text
.data
= (unsigned char *) cpystr(sstring
);
2381 secondpgm
->cc
->text
.size
= strlen(sstring
);
2382 thirdpgm
= mail_newsearchpgm();
2383 thirdpgm
->from
= mail_newstringlist();
2384 thirdpgm
->from
->text
.data
= (unsigned char *) cpystr(sstring
);
2385 thirdpgm
->from
->text
.size
= strlen(sstring
);
2388 pgm
->or = mail_newsearchor();
2389 pgm
->or->first
->to
= mail_newstringlist();
2390 pgm
->or->first
->to
->text
.data
= (unsigned char *) cpystr(sstring
);
2391 pgm
->or->first
->to
->text
.size
= strlen(sstring
);
2393 pgm
->or->second
->or = mail_newsearchor();
2394 pgm
->or->second
->or->first
->cc
= mail_newstringlist();
2395 pgm
->or->second
->or->first
->cc
->text
.data
=
2396 (unsigned char *) cpystr(sstring
);
2397 pgm
->or->second
->or->first
->cc
->text
.size
= strlen(sstring
);
2398 pgm
->or->second
->or->second
->from
= mail_newstringlist();
2399 pgm
->or->second
->or->second
->from
->text
.data
=
2400 (unsigned char *) cpystr(sstring
);
2401 pgm
->or->second
->or->second
->from
->text
.size
= strlen(sstring
);
2406 case 'f' : /* FROM */
2407 pgm
->from
= mail_newstringlist();
2408 pgm
->from
->text
.data
= (unsigned char *) cpystr(sstring
);
2409 pgm
->from
->text
.size
= strlen(sstring
);
2413 pgm
->cc
= mail_newstringlist();
2414 pgm
->cc
->text
.data
= (unsigned char *) cpystr(sstring
);
2415 pgm
->cc
->text
.size
= strlen(sstring
);
2419 pgm
->to
= mail_newstringlist();
2420 pgm
->to
->text
.data
= (unsigned char *) cpystr(sstring
);
2421 pgm
->to
->text
.size
= strlen(sstring
);
2424 case 's' : /* SUBJECT */
2425 pgm
->subject
= mail_newstringlist();
2426 pgm
->subject
->text
.data
= (unsigned char *) cpystr(sstring
);
2427 pgm
->subject
->text
.size
= strlen(sstring
);
2430 case 'a' : /* ALL TEXT */
2431 pgm
->text
= mail_newstringlist();
2432 pgm
->text
->text
.data
= (unsigned char *) cpystr(sstring
);
2433 pgm
->text
->text
.size
= strlen(sstring
);
2436 case 'b' : /* ALL BODY TEXT */
2437 pgm
->body
= mail_newstringlist();
2438 pgm
->body
->text
.data
= (unsigned char *) cpystr(sstring
);
2439 pgm
->body
->text
.size
= strlen(sstring
);
2443 dprint((1,"\n - BOTCH: select_text unrecognized type\n"));
2448 * If we happen to have any messages excluded, make sure we
2449 * don't waste time searching their text...
2451 srchpgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
2453 /* TRANSLATORS: warning to user that we're busy selecting messages */
2454 we_cancel
= busy_cue(_("Busy Selecting"), NULL
, 1);
2456 searchflags
= SE_NOPREFETCH
| (secondpgm
? 0 : SE_FREE
);
2458 pine_mail_search_full(stream
, !old_imap
? charset
: NULL
, srchpgm
,
2461 /* search for To or Cc; or To or Cc or From on old imap server */
2464 srchpgm
->msgno
= NULL
;
2465 mail_free_searchpgm(&srchpgm
);
2468 secondpgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
2469 searchflags
|= (SE_RETAIN
| (thirdpgm
? 0 : SE_FREE
));
2471 pine_mail_search_full(stream
, NULL
, secondpgm
, searchflags
);
2475 secondpgm
->msgno
= NULL
;
2476 mail_free_searchpgm(&secondpgm
);
2479 thirdpgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
2480 searchflags
|= SE_FREE
;
2481 pine_mail_search_full(stream
, NULL
, thirdpgm
, searchflags
);
2485 /* we know this was freed in mail_search, let caller know */
2489 if(old_imap
&& not){
2494 * Old imap server doesn't have a NOT, so we actually searched for
2495 * the subject (or whatever) instead of !subject. Flip the searched
2498 for(msgno
= 1L; msgno
<= mn_get_total(msgmap
); msgno
++)
2499 if(stream
&& msgno
<= stream
->nmsgs
2500 && (mc
=mail_elt(stream
, msgno
)) && mc
->searched
)
2514 search_for_our_regex_addresses(MAILSTREAM
*stream
, char type
, int not,
2515 SEARCHSET
*searchset
)
2517 long rawno
, count
= 0L;
2519 ADDRESS
*addr1
= NULL
, *addr2
= NULL
, *addr3
= NULL
;
2521 SEARCHSET
*s
, *ss
= NULL
;
2522 extern MAILSTREAM
*mm_search_stream
;
2523 extern long mm_search_count
;
2525 mm_search_count
= 0L;
2526 mm_search_stream
= stream
;
2531 /* set searched bits to zero */
2532 for(rawno
= 1L; rawno
<= stream
->nmsgs
; rawno
++)
2533 if((mc
=mail_elt(stream
, rawno
)) != NULL
)
2536 /* set sequence bits for envelopes we need */
2537 for(rawno
= 1L; rawno
<= stream
->nmsgs
; rawno
++){
2538 if((mc
= mail_elt(stream
, rawno
)) != NULL
){
2539 if((!searchset
|| in_searchset(searchset
, (unsigned long) rawno
))
2540 && !mc
->private.msg
.env
){
2550 * Set up a searchset that will control the fetch ahead.
2553 ss
= build_searchset(stream
);
2555 SEARCHSET
**sset
= NULL
;
2557 mail_parameters(NULL
, SET_FETCHLOOKAHEADLIMIT
, (void *) count
);
2559 /* this resets automatically after the first fetch */
2560 sset
= (SEARCHSET
**) mail_parameters(stream
,
2568 for(s
= searchset
; s
; s
= s
->next
){
2569 for(rawno
= s
->first
; rawno
<= s
->last
; rawno
++){
2570 env
= pine_mail_fetchenvelope(stream
, rawno
);
2571 addr1
= addr2
= addr3
= NULL
;
2574 addr1
= env
? env
->to
: NULL
;
2575 addr2
= env
? env
->cc
: NULL
;
2578 addr1
= env
? env
->to
: NULL
;
2579 addr2
= env
? env
->cc
: NULL
;
2580 addr3
= env
? env
->from
: NULL
;
2583 addr1
= env
? env
->from
: NULL
;
2586 addr1
= env
? env
->cc
: NULL
;
2590 addr1
= env
? env
->to
: NULL
;
2593 q_status_message(SM_ORDER
, 3, 3, "Unhandled case2 in agg_text_select");
2597 if(addr1
&& address_is_us(addr1
, ps_global
)){
2598 if(rawno
> 0L && rawno
<= stream
->nmsgs
2599 && (mc
=mail_elt(stream
, rawno
)) != NULL
)
2600 mm_searched(stream
, rawno
);
2602 else if(addr2
&& address_is_us(addr2
, ps_global
)){
2603 if(rawno
> 0L && rawno
<= stream
->nmsgs
2604 && (mc
=mail_elt(stream
, rawno
)) != NULL
)
2605 mm_searched(stream
, rawno
);
2607 else if(addr3
&& address_is_us(addr3
, ps_global
)){
2608 if(rawno
> 0L && rawno
<= stream
->nmsgs
2609 && (mc
=mail_elt(stream
, rawno
)) != NULL
)
2610 mm_searched(stream
, rawno
);
2616 mail_free_searchset(&ss
);
2619 for(rawno
= 1L; rawno
<= stream
->nmsgs
; rawno
++){
2620 if((mc
=mail_elt(stream
, rawno
)) && mc
->searched
)
2630 agg_flag_select(MAILSTREAM
*stream
, int not, int crit
, SEARCHSET
**limitsrch
)
2634 pgm
= mail_newsearchpgm();
2640 /* this is the same as seen or deleted or answered */
2641 pgm
->not = mail_newsearchpgmlist();
2642 notpgm
= pgm
->not->pgm
= mail_newsearchpgm();
2643 notpgm
->unseen
= notpgm
->undeleted
= notpgm
->unanswered
= 1;
2646 pgm
->unseen
= pgm
->undeleted
= pgm
->unanswered
= 1;
2676 * Not a true "not", we are implicitly only interested in undeleted.
2679 pgm
->unanswered
= pgm
->undeleted
= 1;
2681 pgm
->answered
= pgm
->undeleted
= 1;
2688 for(slpp
= (not) ? &pgm
->unkeyword
: &pgm
->keyword
;
2690 slpp
= &(*slpp
)->next
)
2693 *slpp
= mail_newstringlist();
2694 (*slpp
)->text
.data
= (unsigned char *) cpystr(FORWARDED_FLAG
);
2695 (*slpp
)->text
.size
= (unsigned long) strlen(FORWARDED_FLAG
);
2713 pgm
->msgno
= (limitsrch
? *limitsrch
: NULL
);
2714 pine_mail_search_full(stream
, NULL
, pgm
, SE_NOPREFETCH
| SE_FREE
);
2715 /* we know this was freed in mail_search, let caller know */
2724 * Get the user name from the mailbox portion of an address.
2726 * Args: mailbox -- the mailbox portion of an address (lhs of address)
2727 * target -- a buffer to put the result in
2728 * len -- length of the target buffer
2730 * Returns the left most portion up to the first '%', ':' or '@',
2731 * and to the right of any '!' (as if c-client would give us such a mailbox).
2732 * Returns NULL if it can't find a username to point to.
2735 get_uname(char *mailbox
, char *target
, int len
)
2739 if(!mailbox
|| !*mailbox
)
2742 end
= strlen(mailbox
) - 1;
2743 for(start
= end
; start
> -1 && mailbox
[start
] != '!'; start
--)
2744 if(strindex("%:@", mailbox
[start
]))
2747 start
++; /* compensate for either case above */
2749 for(i
= start
; i
<= end
&& (i
-start
) < (len
-1); i
++) /* copy name */
2750 target
[i
-start
] = isupper((unsigned char)mailbox
[i
])
2751 ? tolower((unsigned char)mailbox
[i
])
2754 target
[i
-start
] = '\0'; /* tie it off */
2756 return(*target
? target
: NULL
);