* For mailing lists, Alpine adds a description of the type of link
[alpine.git] / pith / stream.c
blob301c303775677d6b1df534ee94d9a2d49bde2bf1
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 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 /*======================================================================
16 stream.c
17 Implements the Pine mail stream management routines
18 and c-client wrapper functions
19 ====*/
22 #include "../pith/headers.h"
23 #include "../pith/stream.h"
24 #include "../pith/state.h"
25 #include "../pith/conf.h"
26 #include "../pith/flag.h"
27 #include "../pith/msgno.h"
28 #include "../pith/adrbklib.h"
29 #include "../pith/status.h"
30 #include "../pith/newmail.h"
31 #include "../pith/detach.h"
32 #include "../pith/folder.h"
33 #include "../pith/mailcmd.h"
34 #include "../pith/util.h"
35 #include "../pith/news.h"
36 #include "../pith/sequence.h"
37 #include "../pith/options.h"
38 #include "../pith/mimedesc.h"
41 void (*pith_opt_closing_stream)(MAILSTREAM *);
45 * Internal prototypes
47 void reset_stream_view_state(MAILSTREAM *);
48 void carefully_reset_sp_flags(MAILSTREAM *, unsigned long);
49 char *partial_text_gets(readfn_t, void *, unsigned long, GETS_DATA *);
50 void mail_list_internal(MAILSTREAM *, char *, char *);
51 int recent_activity(MAILSTREAM *);
52 int hibit_in_searchpgm(SEARCHPGM *);
53 int hibit_in_strlist(STRINGLIST *);
54 int hibit_in_header(SEARCHHEADER *);
55 int hibit_in_sizedtext(SIZEDTEXT *);
56 int sp_nusepool_notperm(void);
57 int sp_add(MAILSTREAM *, int);
58 void sp_delete(MAILSTREAM *);
59 void sp_free(PER_STREAM_S **);
62 static FETCH_READC_S *g_pft_desc;
65 MAILSTATUS *pine_cached_status; /* implement status for #move folder */
70 * Pine wrapper around mail_open. If we have the PREFER_ALT_AUTH flag turned
71 * on, we need to set the TRYALT flag before trying the open.
72 * This routine manages the stream pool, too. It tries to re-use existing
73 * streams instead of opening new ones, or maybe it will leave one open and
74 * use a new one if that seems to make more sense. Pine_mail_close leaves
75 * streams open so that they may be re-used. Each pine_mail_open should have
76 * a matching pine_mail_close (or possible pine_mail_actually_close) somewhere
77 * that goes with it.
79 * Args:
80 * stream -- A possible stream for recycling. This isn't usually the
81 * way recycling happens. Usually it is automatic.
82 * mailbox -- The mailbox to be opened.
83 * openflags -- Flags passed here to modify the behavior.
84 * retflags -- Flags returned from here. SP_MATCH will be lit if that is
85 * what happened. If SP_MATCH is lit then SP_LOCKED may also
86 * be lit if the matched stream was already locked when
87 * we got here.
89 MAILSTREAM *
90 pine_mail_open(MAILSTREAM *stream, char *mailbox, long int openflags, long int *retflags)
92 MAILSTREAM *retstream = NULL;
93 DRIVER *d;
94 int permlocked = 0, is_inbox = 0, usepool = 0, tempuse = 0, uf = 0;
95 unsigned long flags;
96 char **lock_these, *target = NULL;
97 static unsigned long streamcounter = 0;
99 dprint((7,
100 "pine_mail_open: opening \"%s\"%s openflags=0x%x %s%s%s%s%s%s%s%s%s (%s)\n",
101 mailbox ? mailbox : "(NULL)",
102 stream ? "" : " (stream was NULL)",
103 openflags,
104 openflags & OP_HALFOPEN ? " OP_HALFOPEN" : "",
105 openflags & OP_READONLY ? " OP_READONLY" : "",
106 openflags & OP_SILENT ? " OP_SILENT" : "",
107 openflags & OP_DEBUG ? " OP_DEBUG" : "",
108 openflags & SP_PERMLOCKED ? " SP_PERMLOCKED" : "",
109 openflags & SP_INBOX ? " SP_INBOX" : "",
110 openflags & SP_USERFLDR ? " SP_USERFLDR" : "",
111 openflags & SP_USEPOOL ? " SP_USEPOOL" : "",
112 openflags & SP_TEMPUSE ? " SP_TEMPUSE" : "",
113 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
115 if(retflags)
116 *retflags = 0L;
118 if(ps_global->user_says_cancel){
119 dprint((7, "pine_mail_open: cancelled by user\n"));
120 return(retstream);
123 is_inbox = openflags & SP_INBOX;
124 uf = openflags & SP_USERFLDR;
126 /* inbox is still special, assume that we want to permlock it */
127 permlocked = (is_inbox || openflags & SP_PERMLOCKED) ? 1 : 0;
129 /* check to see if user wants this folder permlocked */
130 for(lock_these = ps_global->VAR_PERMLOCKED;
131 uf && !permlocked && lock_these && *lock_these; lock_these++){
132 char *p = NULL, *dummy = NULL, *lt, *lname, *mname;
133 char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN];
135 /* there isn't really a pair, it just dequotes for us */
136 get_pair(*lock_these, &dummy, &p, 0, 0);
139 * Check to see if this is an incoming nickname and replace it
140 * with the full name.
142 if(!(p && ps_global->context_list
143 && ps_global->context_list->use & CNTXT_INCMNG
144 && (lt=folder_is_nick(p, FOLDERS(ps_global->context_list), 0))))
145 lt = p;
147 if(dummy)
148 fs_give((void **) &dummy);
150 if(lt && mailbox
151 && (same_remote_mailboxes(mailbox, lt)
153 (!IS_REMOTE(mailbox)
154 && (lname=mailboxfile(tmp1, lt))
155 && (mname=mailboxfile(tmp2, mailbox))
156 && !strcmp(lname, mname))))
157 permlocked++;
159 if(p)
160 fs_give((void **) &p);
164 * Only cache if remote, not nntp, not pop, and caller asked us to.
165 * It might make sense to do some caching for nntp and pop, as well, but
166 * we aren't doing it right now. For example, an nntp stream open to
167 * one group could be reused for another group. An open pop stream could
168 * be used for mail_copy_full.
170 * An implication of doing only imap here is that sp_stream_get will only
171 * be concerned with imap streams.
173 if((d = mail_valid (NIL, mailbox, (char *) NIL)) && !strcmp(d->name, "imap")){
174 usepool = openflags & SP_USEPOOL;
175 tempuse = openflags & SP_TEMPUSE;
177 else{
178 if(IS_REMOTE(mailbox)){
179 dprint((9, "pine_mail_open: not cacheable: %s\n", !d ? "no driver?" : d->name ? d->name : "?" ));
181 else{
182 if(permlocked || (openflags & OP_READONLY)){
184 * This is a strange case. We want to allow stay-open local
185 * folders, but they don't fit into the rest of the framework
186 * well. So we'll look for it being already open in this case
187 * and special-case it (the already_open_stream() case
188 * below).
190 dprint((9,
191 "pine_mail_open: not cacheable: not remote, but check for local stream\n"));
193 else{
194 dprint((9,
195 "pine_mail_open: not cacheable: not remote\n"));
200 /* If driver doesn't support halfopen, just open it. */
201 if(d && (openflags & OP_HALFOPEN) && !(d->flags & DR_HALFOPEN)){
202 openflags &= ~OP_HALFOPEN;
203 dprint((9,
204 "pine_mail_open: turning off OP_HALFOPEN flag\n"));
208 * Some of the flags are pine's, the rest are meant for mail_open.
209 * We've noted the pine flags, now remove them before we call mail_open.
211 openflags &= ~(SP_USEPOOL | SP_TEMPUSE | SP_INBOX
212 | SP_PERMLOCKED | SP_USERFLDR);
214 #ifdef DEBUG
215 if(ps_global->debug_imap > 3 || ps_global->debugmem)
216 openflags |= OP_DEBUG;
217 #endif
219 if(F_ON(F_PREFER_ALT_AUTH, ps_global)){
220 if((d = mail_valid (NIL, mailbox, (char *) NIL))
221 && !strcmp(d->name, "imap"))
222 openflags |= OP_TRYALT;
225 if(F_ON(F_ENABLE_MULNEWSRCS, ps_global)){
226 char source[MAILTMPLEN];
227 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
228 DRIVER *d;
229 if((d = mail_valid(NIL, source, (char *) NIL))
230 && (!strcmp(d->name, "news")
231 || !strcmp(d->name, "nntp")))
232 openflags |= OP_MULNEWSRC;
234 else if((d = mail_valid(NIL, mailbox, (char *) NIL))
235 && !strcmp(d->name, "nntp"))
236 openflags |= OP_MULNEWSRC;
240 * One of the problems is that the new-style stream caching (the
241 * sp_stream_get stuff) may conflict with some of the old-style caching
242 * (the passed in argument stream) that is still in the code. We should
243 * probably eliminate the old-style caching, but some of it is still useful,
244 * especially if it deals with something other than IMAP. We want to prevent
245 * mistakes caused by conflicts between the two styles. In particular, we
246 * don't want to have a new-style cached stream re-opened because of the
247 * old-style caching code. This can happen if a stream is passed in that
248 * is not usable, and then a new stream is opened because the passed in
249 * stream causes us to bypass the new caching code. Play it safe. If it
250 * is an IMAP stream, just close it. This should leave it in the new-style
251 * cache anyway, causing no loss. Maybe not if the cache wasn't large
252 * enough to have it in there in the first place, in which case we get
253 * a possibly unnecessary close and open. If it isn't IMAP we still have
254 * to worry about it because it will cause us to bypass the caching code.
255 * So if the stream isn't IMAP but the mailbox we're opening is, close it.
256 * The immediate alternative would be to try to emulate the code in
257 * mail_open that checks whether it is re-usable or not, but that is
258 * dangerous if that code changes on us.
260 if(stream){
261 if(is_imap_stream(stream)
262 || ((d = mail_valid (NIL, mailbox, (char *) NIL))
263 && !strcmp(d->name, "imap"))){
264 if(is_imap_stream(stream)){
265 dprint((7,
266 "pine_mail_open: closing passed in IMAP stream %s\n",
267 stream->mailbox ? stream->mailbox : "?"));
269 else{
270 dprint((7,
271 "pine_mail_open: closing passed in non-IMAP stream %s\n",
272 stream->mailbox ? stream->mailbox : "?"));
275 pine_mail_close(stream);
276 stream = NULL;
281 * Maildrops are special. The mailbox name will be a #move name. If the
282 * target of the maildrop is an IMAP folder we want to be sure it isn't
283 * already open in another cached stream, to avoid double opens. This
284 * could have happened if the user opened it manually as the target
285 * instead of as a maildrop. It could also be a side-effect of marking
286 * an answered flag after a reply.
288 target = NULL;
289 if(check_for_move_mbox(mailbox, NULL, 0, &target)){
290 MAILSTREAM *targetstream = NULL;
292 if((d = mail_valid (NIL, target, (char *) NIL)) && !strcmp(d->name, "imap")){
293 targetstream = sp_stream_get(target, SP_MATCH | SP_RO_OK);
294 if(targetstream){
295 dprint((9, "pine_mail_open: close previously opened target of maildrop\n"));
296 pine_mail_actually_close(targetstream);
301 if((usepool && !stream && permlocked)
302 || (!usepool && (permlocked || (openflags & OP_READONLY))
303 && (retstream = already_open_stream(mailbox, AOS_NONE)))){
304 if(retstream)
305 stream = retstream;
306 else
307 stream = sp_stream_get(mailbox,
308 SP_MATCH | ((openflags & OP_READONLY) ? SP_RO_OK : 0));
309 if(stream){
310 flags = SP_LOCKED
311 | (usepool ? SP_USEPOOL : 0)
312 | (permlocked ? SP_PERMLOCKED : 0)
313 | (is_inbox ? SP_INBOX : 0)
314 | (uf ? SP_USERFLDR : 0)
315 | (tempuse ? SP_TEMPUSE : 0);
318 * If the stream wasn't already locked, then we reset it so it
319 * looks like we are reopening it. We have to worry about recent
320 * messages since they will still be recent, if that affects us.
322 if(!(sp_flags(stream) & SP_LOCKED))
323 reset_stream_view_state(stream);
325 if(retflags){
326 *retflags |= SP_MATCH;
327 if(sp_flags(stream) & SP_LOCKED)
328 *retflags |= SP_LOCKED;
331 if(sp_flags(stream) & SP_LOCKED
332 && sp_flags(stream) & SP_USERFLDR
333 && !(flags & SP_USERFLDR)){
334 sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
335 dprint((7,
336 "pine_mail_open: permlocked: ref cnt up to %d\n",
337 sp_ref_cnt(stream)));
339 else if(sp_ref_cnt(stream) <= 0){
340 sp_set_ref_cnt(stream, 1);
341 dprint((7,
342 "pine_mail_open: permexact: ref cnt set to %d\n",
343 sp_ref_cnt(stream)));
346 carefully_reset_sp_flags(stream, flags);
348 if(stream->silent && !(openflags & OP_SILENT))
349 stream->silent = NIL;
351 dprint((9, "pine_mail_open: stream was already open\n"));
352 if(stream && stream->dtb && stream->dtb->name
353 && !strcmp(stream->dtb->name, "imap")){
354 dprint((7, "pine_mail_open: next TAG %08lx\n",
355 stream->gensym));
358 return(stream);
362 if(usepool && !stream){
364 * First, we look for an exact match, a stream which is already
365 * open to the mailbox we are trying to re-open, and we use that.
366 * Skip permlocked only because we did it above already.
368 if(!permlocked)
369 stream = sp_stream_get(mailbox,
370 SP_MATCH | ((openflags & OP_READONLY) ? SP_RO_OK : 0));
372 if(stream){
373 flags = SP_LOCKED
374 | (usepool ? SP_USEPOOL : 0)
375 | (permlocked ? SP_PERMLOCKED : 0)
376 | (is_inbox ? SP_INBOX : 0)
377 | (uf ? SP_USERFLDR : 0)
378 | (tempuse ? SP_TEMPUSE : 0);
381 * If the stream wasn't already locked, then we reset it so it
382 * looks like we are reopening it. We have to worry about recent
383 * messages since they will still be recent, if that affects us.
385 if(!(sp_flags(stream) & SP_LOCKED))
386 reset_stream_view_state(stream);
388 if(retflags){
389 *retflags |= SP_MATCH;
390 if(sp_flags(stream) & SP_LOCKED)
391 *retflags |= SP_LOCKED;
394 if(sp_flags(stream) & SP_LOCKED
395 && sp_flags(stream) & SP_USERFLDR
396 && !(flags & SP_USERFLDR)){
397 sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
398 dprint((7,
399 "pine_mail_open: matched: ref cnt up to %d\n",
400 sp_ref_cnt(stream)));
402 else if(sp_ref_cnt(stream) <= 0){
403 sp_set_ref_cnt(stream, 1);
404 dprint((7,
405 "pine_mail_open: exact: ref cnt set to %d\n",
406 sp_ref_cnt(stream)));
409 carefully_reset_sp_flags(stream, flags);
412 * We may be re-using a stream that was previously open
413 * with OP_SILENT and now we don't want OP_SILENT.
414 * We don't turn !silent into silent because the !silentness
415 * could be important in the original context (for example,
416 * silent suppresses mm_expunged calls).
418 * WARNING: we're messing with c-client internals.
420 if(stream->silent && !(openflags & OP_SILENT))
421 stream->silent = NIL;
423 dprint((9, "pine_mail_open: stream already open\n"));
424 if(stream && stream->dtb && stream->dtb->name
425 && !strcmp(stream->dtb->name, "imap")){
426 dprint((7, "pine_mail_open: next TAG %08lx\n",
427 stream->gensym));
430 return(stream);
434 * No exact match, look for a stream which is open to the same
435 * server and marked for TEMPUSE.
437 stream = sp_stream_get(mailbox, SP_SAME | SP_TEMPUSE);
438 if(stream){
439 dprint((9,
440 "pine_mail_open: attempting to re-use TEMP stream\n"));
443 * No SAME/TEMPUSE stream so we look to see if there is an
444 * open slot available (we're not yet at max_remstream). If there
445 * is an open slot, we'll just open a new stream and put it there.
446 * If not, we'll go inside this conditional.
448 else if(!permlocked
449 && sp_nusepool_notperm() >= ps_global->s_pool.max_remstream){
450 dprint((9,
451 "pine_mail_open: no empty slots\n"));
453 * No empty remote slots available. See if there is a
454 * TEMPUSE stream that is open but to the wrong server.
456 stream = sp_stream_get(mailbox, SP_TEMPUSE);
457 if(stream){
459 * We will close this stream and use the empty slot
460 * that that creates.
462 dprint((9,
463 "pine_mail_open: close a TEMPUSE stream and re-use that slot\n"));
464 pine_mail_actually_close(stream);
465 stream = NULL;
467 else{
470 * Still no luck. Look for a stream open to the same
471 * server that is just not locked. This would be a
472 * stream that might be reusable in the future, but we
473 * need it now instead.
475 stream = sp_stream_get(mailbox, SP_SAME | SP_UNLOCKED);
476 if(stream){
477 dprint((9,
478 "pine_mail_open: attempting to re-use stream\n"));
480 else{
482 * We'll take any UNLOCKED stream and re-use it.
484 stream = sp_stream_get(mailbox, 0);
485 if(stream){
487 * We will close this stream and use the empty slot
488 * that that creates.
490 dprint((9,
491 "pine_mail_open: close an UNLOCKED stream and re-use the slot\n"));
492 pine_mail_actually_close(stream);
493 stream = NULL;
495 else{
496 if(ps_global->s_pool.max_remstream){
497 dprint((9, "pine_mail_open: all USEPOOL slots full of LOCKED streams, nothing to use\n"));
499 else{
500 dprint((9, "pine_mail_open: no caching, max_remstream == 0\n"));
503 usepool = 0;
504 tempuse = 0;
505 if(permlocked){
506 permlocked = 0;
507 dprint((2,
508 "pine_mail_open5: Can't mark folder Stay-Open: at max-remote limit\n"));
509 q_status_message1(SM_ORDER, 3, 5,
510 "Can't mark folder Stay-Open: reached max-remote limit (%s)",
511 comatose((long) ps_global->s_pool.max_remstream));
517 else{
518 dprint((9,
519 "pine_mail_open: there is an empty slot to use\n"));
523 * We'll make an assumption here. If we were asked to halfopen a
524 * stream then we'll assume that the caller doesn't really care if
525 * the stream is halfopen or if it happens to be open to some mailbox
526 * already. They are just saying halfopen because they don't need to
527 * SELECT a mailbox. That's the assumption, anyway.
529 if(openflags & OP_HALFOPEN && stream){
530 dprint((9,
531 "pine_mail_open: asked for HALFOPEN so returning stream as is\n"));
532 sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
533 dprint((7,
534 "pine_mail_open: halfopen: ref cnt up to %d\n",
535 sp_ref_cnt(stream)));
536 if(stream && stream->dtb && stream->dtb->name
537 && !strcmp(stream->dtb->name, "imap")){
538 dprint((7, "pine_mail_open: next TAG %08lx\n",
539 stream->gensym));
542 if(stream->silent && !(openflags & OP_SILENT))
543 stream->silent = NIL;
545 return(stream);
549 * We're going to SELECT another folder with this open stream.
551 if(stream){
553 * We will have just pinged the stream to make sure it is
554 * still alive. That ping may have discovered some new mail.
555 * Before unselecting the folder, we should process the filters
556 * for that new mail.
558 if(!sp_flagged(stream, SP_LOCKED)
559 && !sp_flagged(stream, SP_USERFLDR)
560 && sp_new_mail_count(stream))
561 process_filter_patterns(stream, sp_msgmap(stream),
562 sp_new_mail_count(stream));
564 if(stream && stream->dtb && stream->dtb->name
565 && !strcmp(stream->dtb->name, "imap")){
566 dprint((7,
567 "pine_mail_open: cancel idle timer: TAG %08lx (%s)\n",
568 stream->gensym, debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
572 * We need to reset the counters and everything.
573 * The easiest way to do that is just to delete all of the
574 * sp_s data and let the open fill it in correctly for
575 * the new folder.
577 sp_free((PER_STREAM_S **) &stream->sparep);
582 * When we pass a stream to mail_open, it will either re-use it or
583 * close it.
585 retstream = mail_open(stream, mailbox, openflags);
587 if(retstream){
589 dprint((7, "pine_mail_open: mail_open returns stream:\n original_mailbox=%s\n mailbox=%s\n driver=%s rdonly=%d halfopen=%d secure=%d nmsgs=%ld recent=%ld\n", retstream->original_mailbox ? retstream->original_mailbox : "?", retstream->mailbox ? retstream->mailbox : "?", (retstream->dtb && retstream->dtb->name) ? retstream->dtb->name : "?", retstream->rdonly, retstream->halfopen, retstream->secure, retstream->nmsgs, retstream->recent));
592 * So it is easier to figure out which command goes with which
593 * stream when debugging, change the tag associated with each stream.
594 * Of course, this will come after the SELECT, so the startup IMAP
595 * commands will have confusing tags.
597 if(retstream != stream && retstream->dtb && retstream->dtb->name
598 && !strcmp(retstream->dtb->name, "imap")){
599 retstream->gensym += (streamcounter * 0x1000000);
600 streamcounter = (streamcounter + 1) % (8 * 16);
603 if(retstream && retstream->dtb && retstream->dtb->name
604 && !strcmp(retstream->dtb->name, "imap")){
605 dprint((7, "pine_mail_open: next TAG %08lx\n",
606 retstream->gensym));
610 * Catch the case where our test up above (where usepool was set)
611 * did not notice that this was news, but that we can tell once
612 * we've opened the stream. (One such case would be an imap proxy
613 * to an nntp server.) Remove it from being cached here. There was
614 * a possible penalty for not noticing sooner. If all the usepool
615 * slots were full, we will have closed one of the UNLOCKED streams
616 * above, preventing us from future re-use of that stream.
617 * We could figure out how to do the test better with just the
618 * name. We could open the stream and then close the other one
619 * after the fact. Or we could just not worry about it since it is
620 * rare.
622 if(IS_NEWS(retstream)){
623 usepool = 0;
624 tempuse = 0;
627 /* make sure the message map has been instantiated */
628 (void) sp_msgmap(retstream);
631 * If a referral during the mail_open above causes a stream
632 * re-use which involves a BYE on an IMAP stream, then that
633 * BYE will have caused an mm_notify which will have
634 * instantiated the sp_data and set dead_stream! Trust that
635 * it is alive up to here and reset it to zero.
637 sp_set_dead_stream(retstream, 0);
639 flags = SP_LOCKED
640 | (usepool ? SP_USEPOOL : 0)
641 | (permlocked ? SP_PERMLOCKED : 0)
642 | (is_inbox ? SP_INBOX : 0)
643 | (uf ? SP_USERFLDR : 0)
644 | (tempuse ? SP_TEMPUSE : 0);
646 sp_flag(retstream, flags);
647 sp_set_recent_since_visited(retstream, retstream->recent);
649 /* initialize the reference count */
650 sp_set_ref_cnt(retstream, 1);
651 dprint((7, "pine_mail_open: reset: ref cnt set to %d\n",
652 sp_ref_cnt(retstream)));
654 if(sp_add(retstream, usepool) != 0 && usepool){
655 usepool = 0;
656 tempuse = 0;
657 flags = SP_LOCKED
658 | (usepool ? SP_USEPOOL : 0)
659 | (permlocked ? SP_PERMLOCKED : 0)
660 | (is_inbox ? SP_INBOX : 0)
661 | (uf ? SP_USERFLDR : 0)
662 | (tempuse ? SP_TEMPUSE : 0);
664 sp_flag(retstream, flags);
665 (void) sp_add(retstream, usepool);
670 * When opening a newsgroup, c-client marks the messages up to the
671 * last Deleted as Unseen. If the feature news-approximates-new-status
672 * is on, we'd rather they be treated as Seen. That way, selecting New
673 * messages will give us the ones past the last Deleted. So we're going
674 * to change them to Seen. Since Seen is a session flag for news, making
675 * this change won't have any permanent effect. C-client also marks the
676 * messages after the last deleted Recent, which is the bit of
677 * information we'll use to find the messages we want to change.
679 if(F_ON(F_FAKE_NEW_IN_NEWS, ps_global) &&
680 retstream->nmsgs > 0 && IS_NEWS(retstream)){
681 char *seq;
682 long i, mflags = ST_SET;
683 MESSAGECACHE *mc;
686 * Search for !recent messages to set the searched bit for
687 * those messages we want to change. Then we'll flip the bits.
689 (void)count_flagged(retstream, F_UNRECENT);
691 for(i = 1L; i <= retstream->nmsgs; i++)
692 if((mc = mail_elt(retstream,i)) && mc->searched)
693 mc->sequence = 1;
694 else
695 mc->sequence = 0;
697 if(!is_imap_stream(retstream))
698 mflags |= ST_SILENT;
699 if((seq = build_sequence(retstream, NULL, NULL)) != NULL){
700 mail_flag(retstream, seq, "\\SEEN", mflags);
701 fs_give((void **)&seq);
706 return(retstream);
710 void
711 reset_stream_view_state(MAILSTREAM *stream)
713 MSGNO_S *mm;
715 if(!stream)
716 return;
718 mm = sp_msgmap(stream);
720 if(!mm)
721 return;
723 sp_set_viewing_a_thread(stream, 0);
724 sp_set_need_to_rethread(stream, 0);
726 mn_reset_cur(mm, stream->nmsgs > 0L ? stream->nmsgs : 0L); /* default */
728 mm->visible_threads = -1L;
729 mm->top = 0L;
730 mm->max_thrdno = 0L;
731 mm->top_after_thrd = 0L;
733 mn_set_mansort(mm, 0);
736 * Get rid of zooming and selections, but leave filtering flags. All the
737 * flag counts and everything should still be correct because set_lflag
738 * preserves them correctly.
740 if(any_lflagged(mm, MN_SLCT | MN_HIDE)){
741 long i;
743 for(i = 1L; i <= mn_get_total(mm); i++)
744 set_lflag(stream, mm, i, MN_SLCT | MN_HIDE, 0);
748 * We could try to set up a default sort order, but the caller is going
749 * to re-sort anyway if they are interested in sorting. So we won't do
750 * it here.
756 * We have to be careful when we change the flags of an already
757 * open stream, because there may be more than one section of
758 * code actively using the stream.
759 * We allow turning on (but not off) SP_LOCKED
760 * SP_PERMLOCKED
761 * SP_USERFLDR
762 * SP_INBOX
763 * We allow turning off (but not on) SP_TEMPUSE
765 void
766 carefully_reset_sp_flags(MAILSTREAM *stream, long unsigned int flags)
768 if(sp_flags(stream) != flags){
769 /* allow turning on but not off */
770 if(sp_flags(stream) & SP_LOCKED && !(flags & SP_LOCKED))
771 flags |= SP_LOCKED;
773 if(sp_flags(stream) & SP_PERMLOCKED && !(flags & SP_PERMLOCKED))
774 flags |= SP_PERMLOCKED;
776 if(sp_flags(stream) & SP_USERFLDR && !(flags & SP_USERFLDR))
777 flags |= SP_USERFLDR;
779 if(sp_flags(stream) & SP_INBOX && !(flags & SP_INBOX))
780 flags |= SP_INBOX;
783 /* allow turning off but not on */
784 if(!(sp_flags(stream) & SP_TEMPUSE) && flags & SP_TEMPUSE)
785 flags &= ~SP_TEMPUSE;
788 /* leave the way they already are */
789 if((sp_flags(stream) & SP_FILTERED) != (flags & SP_FILTERED))
790 flags = (flags & ~SP_FILTERED) | (sp_flags(stream) & SP_FILTERED);
793 if(sp_flags(stream) != flags)
794 sp_flag(stream, flags);
800 * Pine wrapper around mail_create. If we have the PREFER_ALT_AUTH flag turned
801 * on we don't want to pass a NULL stream to c-client because it will open
802 * a non-ssl connection when we want it to be ssl.
804 long
805 pine_mail_create(MAILSTREAM *stream, char *mailbox)
807 MAILSTREAM *ourstream = NULL;
808 long return_val;
809 long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
810 char source[MAILTMPLEN], *target = NULL;
811 DRIVER *d;
813 dprint((7, "pine_mail_create: creating \"%s\"%s\n",
814 mailbox ? mailbox : "(NULL)",
815 stream ? "" : " (stream was NULL)"));
817 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
818 mailbox = target;
819 dprint((7,
820 "pine_mail_create: #move special case, creating \"%s\"\n",
821 mailbox ? mailbox : "(NULL)"));
825 * We don't really need this anymore, since we are now using IMAPTRYALT.
826 * We'll leave it since it works.
828 if((F_ON(F_PREFER_ALT_AUTH, ps_global)
829 || (ps_global->debug_imap > 3 || ps_global->debugmem))){
831 if((d = mail_valid (NIL, mailbox, (char *) NIL))
832 && !strcmp(d->name, "imap")){
834 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
835 openflags |= OP_TRYALT;
839 if(!stream)
840 stream = sp_stream_get(mailbox, SP_MATCH);
841 if(!stream)
842 stream = sp_stream_get(mailbox, SP_SAME);
844 if(!stream){
846 * It is only useful to open a stream in the imap case.
848 if((d = mail_valid (NIL, mailbox, (char *) NIL))
849 && !strcmp(d->name, "imap")){
851 stream = pine_mail_open(NULL, mailbox, openflags, NULL);
852 ourstream = stream;
856 return_val = mail_create(stream, mailbox);
858 if(ourstream)
859 pine_mail_close(ourstream);
861 return(return_val);
866 * Pine wrapper around mail_delete.
868 long
869 pine_mail_delete(MAILSTREAM *stream, char *mailbox)
871 MAILSTREAM *ourstream = NULL;
872 long return_val;
873 long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
874 char source[MAILTMPLEN], *target = NULL;
875 DRIVER *d;
877 dprint((7, "pine_mail_delete: deleting \"%s\"%s\n",
878 mailbox ? mailbox : "(NULL)",
879 stream ? "" : " (stream was NULL)"));
881 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
882 mailbox = target;
883 dprint((7,
884 "pine_mail_delete: #move special case, deleting \"%s\"\n",
885 mailbox ? mailbox : "(NULL)"));
889 * We don't really need this anymore, since we are now using IMAPTRYALT.
891 if((F_ON(F_PREFER_ALT_AUTH, ps_global)
892 || (ps_global->debug_imap > 3 || ps_global->debugmem))){
894 if((d = mail_valid (NIL, mailbox, (char *) NIL))
895 && !strcmp(d->name, "imap")){
897 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
898 openflags |= OP_TRYALT;
902 /* oops, we seem to be deleting a selected stream */
903 if(!stream && (stream = sp_stream_get(mailbox, SP_MATCH))){
904 pine_mail_actually_close(stream);
905 stream = NULL;
908 if(!stream)
909 stream = sp_stream_get(mailbox, SP_SAME);
911 if(!stream){
913 * It is only useful to open a stream in the imap case.
915 if((d = mail_valid (NIL, mailbox, (char *) NIL))
916 && !strcmp(d->name, "imap")){
918 stream = pine_mail_open(NULL, mailbox, openflags, NULL);
919 ourstream = stream;
923 return_val = mail_delete(stream, mailbox);
925 if(ourstream)
926 pine_mail_close(ourstream);
928 return(return_val);
933 * Pine wrapper around mail_append.
935 long
936 pine_mail_append_full(MAILSTREAM *stream, char *mailbox, char *flags, char *date, STRING *message)
938 MAILSTREAM *ourstream = NULL;
939 long return_val;
940 long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
941 char source[MAILTMPLEN], *target = NULL;
942 char mailbox_nodelim[MAILTMPLEN];
943 int delim;
944 DRIVER *d;
946 dprint((7, "pine_mail_append_full: appending to \"%s\"%s\n",
947 mailbox ? mailbox : "(NULL)",
948 stream ? "" : " (stream was NULL)"));
950 /* strip delimiter, it has no meaning in an APPEND and could cause trouble */
951 if(mailbox && (delim = get_folder_delimiter(mailbox)) != '\0'){
952 size_t len;
954 len = strlen(mailbox);
955 if(mailbox[len-1] == delim){
956 strncpy(mailbox_nodelim, mailbox, MIN(len-1,sizeof(mailbox_nodelim)-1));
957 mailbox_nodelim[MIN(len-1,sizeof(mailbox_nodelim)-1)] = '\0';
958 mailbox = mailbox_nodelim;
962 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
963 mailbox = target;
964 dprint((7,
965 "pine_mail_append_full: #move special case, appending to \"%s\"\n",
966 mailbox ? mailbox : "(NULL)"));
970 * We don't really need this anymore, since we are now using IMAPTRYALT.
972 if((F_ON(F_PREFER_ALT_AUTH, ps_global)
973 || (ps_global->debug_imap > 3 || ps_global->debugmem))){
975 if((d = mail_valid (NIL, mailbox, (char *) NIL))
976 && !strcmp(d->name, "imap")){
978 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
979 openflags |= OP_TRYALT;
983 if(!stream)
984 stream = sp_stream_get(mailbox, SP_MATCH);
985 if(!stream)
986 stream = sp_stream_get(mailbox, SP_SAME);
988 if(!stream){
990 * It is only useful to open a stream in the imap case.
992 if((d = mail_valid (NIL, mailbox, (char *) NIL))
993 && !strcmp(d->name, "imap")){
995 stream = pine_mail_open(NULL, mailbox, openflags, NULL);
996 ourstream = stream;
1000 return_val = mail_append_full(stream, mailbox, flags, date, message);
1002 if(ourstream)
1003 pine_mail_close(ourstream);
1005 return(return_val);
1010 * Pine wrapper around mail_append.
1012 long
1013 pine_mail_append_multiple(MAILSTREAM *stream, char *mailbox, append_t af,
1014 APPENDPACKAGE *data, MAILSTREAM *not_this_stream)
1016 MAILSTREAM *ourstream = NULL;
1017 long return_val;
1018 long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
1019 char source[MAILTMPLEN], *target = NULL;
1020 DRIVER *d;
1021 int we_blocked_reuse = 0;
1023 dprint((7, "pine_mail_append_multiple: appending to \"%s\"%s\n",
1024 mailbox ? mailbox : "(NULL)",
1025 stream ? "" : " (stream was NULL)"));
1027 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
1028 mailbox = target;
1029 dprint((7,
1030 "pine_mail_append_multiple: #move special case, appending to \"%s\"\n",
1031 mailbox ? mailbox : "(NULL)"));
1034 if((F_ON(F_PREFER_ALT_AUTH, ps_global)
1035 || (ps_global->debug_imap > 3 || ps_global->debugmem))){
1037 if((d = mail_valid (NIL, mailbox, (char *) NIL))
1038 && !strcmp(d->name, "imap")){
1040 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1041 openflags |= OP_TRYALT;
1046 * We have to be careful re-using streams for multiappend, because of
1047 * the way it works. We call into c-client below but part of the call
1048 * is data containing a callback function to us to supply the data to
1049 * be appended. That function may need to get the data from the server.
1050 * If that uses the same stream as we're trying to append on, we're
1051 * in trouble. We can't call back into c-client from c-client on the same
1052 * stream. (Just think about it, we're in the middle of an APPEND command.
1053 * We can't issue a FETCH before the APPEND completes in order to complete
1054 * the APPEND.) We can re-use a stream if it is a different stream from
1055 * the one we are reading from, so that's what the not_this_stream
1056 * stuff is for. If we mark it !SP_USEPOOL, it won't get reused.
1058 if(sp_flagged(not_this_stream, SP_USEPOOL)){
1059 we_blocked_reuse++;
1060 sp_unflag(not_this_stream, SP_USEPOOL);
1063 if(!stream)
1064 stream = sp_stream_get(mailbox, SP_MATCH);
1065 if(!stream)
1066 stream = sp_stream_get(mailbox, SP_SAME);
1068 if(!stream){
1070 * It is only useful to open a stream in the imap case.
1072 if((d = mail_valid (NIL, mailbox, (char *) NIL))
1073 && !strcmp(d->name, "imap")){
1075 stream = pine_mail_open(NULL, mailbox, openflags, NULL);
1076 ourstream = stream;
1080 if(we_blocked_reuse)
1081 sp_set_flags(not_this_stream, sp_flags(not_this_stream) | SP_USEPOOL);
1083 return_val = mail_append_multiple(stream, mailbox, af, (void *) data);
1085 if(ourstream)
1086 pine_mail_close(ourstream);
1088 return(return_val);
1093 * Pine wrapper around mail_copy.
1095 long
1096 pine_mail_copy_full(MAILSTREAM *stream, char *sequence, char *mailbox, long int options)
1098 MAILSTREAM *ourstream = NULL;
1099 long return_val;
1100 long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
1101 char source[MAILTMPLEN], *target = NULL;
1102 char mailbox_nodelim[MAILTMPLEN];
1103 int delim;
1104 DRIVER *d;
1106 dprint((7, "pine_mail_copy_full: copying to \"%s\"%s\n",
1107 mailbox ? mailbox : "(NULL)",
1108 stream ? "" : " (stream was NULL)"));
1110 /* strip delimiter, it has no meaning in a COPY and could cause trouble */
1111 if(mailbox && (delim = get_folder_delimiter(mailbox)) != '\0'){
1112 size_t len;
1114 len = strlen(mailbox);
1115 if(mailbox[len-1] == delim){
1116 strncpy(mailbox_nodelim, mailbox, MIN(len-1,sizeof(mailbox_nodelim)-1));
1117 mailbox_nodelim[MIN(len-1,sizeof(mailbox_nodelim)-1)] = '\0';
1118 mailbox = mailbox_nodelim;
1122 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
1123 mailbox = target;
1124 dprint((7,
1125 "pine_mail_copy_full: #move special case, copying to \"%s\"\n",
1126 mailbox ? mailbox : "(NULL)"));
1130 * We don't really need this anymore, since we are now using IMAPTRYALT.
1132 if((F_ON(F_PREFER_ALT_AUTH, ps_global)
1133 || (ps_global->debug_imap > 3 || ps_global->debugmem))){
1135 if((d = mail_valid (NIL, mailbox, (char *) NIL))
1136 && !strcmp(d->name, "imap")){
1138 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1139 openflags |= OP_TRYALT;
1143 if(!stream)
1144 stream = sp_stream_get(mailbox, SP_MATCH);
1145 if(!stream)
1146 stream = sp_stream_get(mailbox, SP_SAME);
1148 if(!stream){
1150 * It is only useful to open a stream in the imap case.
1151 * Actually, mail_copy_full is the case where it might also be
1152 * useful to provide a stream in the nntp and pop3 cases. If we
1153 * cache such streams, then we will probably want to open one
1154 * here so that it gets cached.
1156 if((d = mail_valid (NIL, mailbox, (char *) NIL))
1157 && !strcmp(d->name, "imap")){
1159 stream = pine_mail_open(NULL, mailbox, openflags, NULL);
1160 ourstream = stream;
1164 return_val = mail_copy_full(stream, sequence, mailbox, options);
1166 if(ourstream)
1167 pine_mail_close(ourstream);
1169 return(return_val);
1174 * Pine wrapper around mail_rename.
1176 long
1177 pine_mail_rename(MAILSTREAM *stream, char *old, char *new)
1179 MAILSTREAM *ourstream = NULL;
1180 long return_val;
1181 long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
1182 DRIVER *d;
1184 dprint((7, "pine_mail_rename(%s,%s)\n", old ? old : "",
1185 new ? new : ""));
1188 * We don't really need this anymore, since we are now using IMAPTRYALT.
1190 if((F_ON(F_PREFER_ALT_AUTH, ps_global)
1191 || (ps_global->debug_imap > 3 || ps_global->debugmem))){
1193 if((d = mail_valid (NIL, old, (char *) NIL))
1194 && !strcmp(d->name, "imap")){
1196 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1197 openflags |= OP_TRYALT;
1201 /* oops, we seem to be renaming a selected stream */
1202 if(!stream && (stream = sp_stream_get(old, SP_MATCH))){
1203 pine_mail_actually_close(stream);
1204 stream = NULL;
1207 if(!stream)
1208 stream = sp_stream_get(old, SP_SAME);
1210 if(!stream){
1212 * It is only useful to open a stream in the imap case.
1214 if((d = mail_valid (NIL, old, (char *) NIL))
1215 && !strcmp(d->name, "imap")){
1217 stream = pine_mail_open(NULL, old, openflags, NULL);
1218 ourstream = stream;
1222 return_val = mail_rename(stream, old, new);
1224 if(ourstream)
1225 pine_mail_close(ourstream);
1227 return(return_val);
1231 /*----------------------------------------------------------------------
1232 Our mail_close wrapper to clean up anything on the mailstream we may have
1233 added to it. mostly in the unused bits of the elt's.
1234 ----*/
1235 void
1236 pine_mail_close(MAILSTREAM *stream)
1238 unsigned long uid_last, last_uid;
1239 int refcnt;
1241 if(!stream)
1242 return;
1244 dprint((7, "pine_mail_close: %s (%s)\n",
1245 stream && stream->mailbox ? stream->mailbox : "(NULL)",
1246 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
1248 if(sp_flagged(stream, SP_USEPOOL) && !sp_dead_stream(stream)){
1250 refcnt = sp_ref_cnt(stream);
1251 dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
1254 * Instead of checkpointing here, which takes time that the user
1255 * definitely notices, we checkpoint in new_mail at the next
1256 * opportune time, hopefully when the user is idle.
1258 #if 0
1259 if(sp_flagged(stream, SP_LOCKED) && sp_flagged(stream, SP_USERFLDR)
1260 && !stream->halfopen && refcnt <= 1){
1261 if(changes_to_checkpoint(stream))
1262 pine_mail_check(stream);
1263 else{
1264 dprint((7,
1265 "pine_mail_close: dont think we need to checkpoint\n"));
1268 #endif
1271 * Uid_last is valid when we first open a stream, but not always
1272 * valid after that. So if we know the last uid should be higher
1273 * than uid_last (!#%) use that instead.
1275 uid_last = stream->uid_last;
1276 if(stream->nmsgs > 0L
1277 && (last_uid=mail_uid(stream,stream->nmsgs)) > uid_last)
1278 uid_last = last_uid;
1280 sp_set_saved_uid_validity(stream, stream->uid_validity);
1281 sp_set_saved_uid_last(stream, uid_last);
1284 * If the reference count is down to 0, unlock it.
1285 * In any case, don't actually do any real closing.
1287 if(refcnt > 0)
1288 sp_set_ref_cnt(stream, refcnt-1);
1290 refcnt = sp_ref_cnt(stream);
1291 dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
1292 if(refcnt <= 0){
1293 dprint((7,
1294 "pine_mail_close: unlocking: start idle timer: TAG %08lx (%s)\n",
1295 stream->gensym, debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
1296 sp_set_last_use_time(stream, time(0));
1299 * Logically, we ought to be unflagging SP_INBOX, too. However,
1300 * the filtering code uses SP_INBOX when deciding if it should
1301 * filter some things, and we keep filtering after the mailbox
1302 * is closed. So leave SP_INBOX alone. This (the closing of INBOX)
1303 * usually only happens in goodnight_gracey when we're
1304 * shutting everything down.
1306 sp_unflag(stream, SP_LOCKED | SP_PERMLOCKED | SP_USERFLDR);
1308 else{
1309 dprint((7, "pine_mail_close: ref cnt is now %d\n",
1310 refcnt));
1313 else{
1314 dprint((7, "pine_mail_close: %s\n",
1315 sp_flagged(stream, SP_USEPOOL) ? "dead stream" : "no pool"));
1317 refcnt = sp_ref_cnt(stream);
1318 dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
1321 * If the reference count is down to 0, unlock it.
1322 * In any case, don't actually do any real closing.
1324 if(refcnt > 0)
1325 sp_set_ref_cnt(stream, refcnt-1);
1327 refcnt = sp_ref_cnt(stream);
1328 if(refcnt <= 0){
1329 pine_mail_actually_close(stream);
1331 else{
1332 dprint((7, "pine_mail_close: ref cnt is now %d\n",
1333 refcnt));
1340 void
1341 pine_mail_actually_close(MAILSTREAM *stream)
1343 if(!stream)
1344 return;
1346 if(!sp_closing(stream)){
1347 dprint((7, "pine_mail_actually_close: %s (%s)\n",
1348 stream && stream->mailbox ? stream->mailbox : "(NULL)",
1349 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
1351 sp_set_closing(stream, 1);
1353 if(!sp_flagged(stream, SP_LOCKED)
1354 && !sp_flagged(stream, SP_USERFLDR)
1355 && !sp_dead_stream(stream)
1356 && sp_new_mail_count(stream))
1357 process_filter_patterns(stream, sp_msgmap(stream),
1358 sp_new_mail_count(stream));
1359 sp_delete(stream);
1362 * let sp_free_callback() free the sp_s stuff and the callbacks to
1363 * free_pine_elt free the per-elt pine stuff.
1365 mail_close(stream);
1371 * If we haven't used a stream for a while, we may want to logout.
1373 void
1374 maybe_kill_old_stream(MAILSTREAM *stream)
1376 #define KILL_IF_IDLE_TIME (25 * 60)
1377 if(stream
1378 && !sp_flagged(stream, SP_LOCKED)
1379 && !sp_flagged(stream, SP_USERFLDR)
1380 && time(0) - sp_last_use_time(stream) > KILL_IF_IDLE_TIME){
1382 dprint((7,
1383 "killing idle stream: %s (%s): idle timer = %ld secs\n",
1384 stream && stream->mailbox ? stream->mailbox : "(NULL)",
1385 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress),
1386 (long) (time(0)-sp_last_use_time(stream))));
1389 * Another thing we could do here instead is to unselect the
1390 * mailbox, leaving the stream open to the server.
1392 pine_mail_actually_close(stream);
1398 * Catch searches that don't need to go to the server.
1399 * (Not anymore, now c-client does this for us.)
1401 long
1402 pine_mail_search_full(MAILSTREAM *stream, char *charset,
1403 SEARCHPGM *pgm, long int flags)
1406 * The charset should either be UTF-8 or NULL for alpine.
1407 * If it is UTF-8 we may be able to change it to NULL if
1408 * everything in the search is actually ascii. We try to
1409 * do this because not all servers understand the CHARSET
1410 * parameter.
1412 if(charset && !strucmp(charset, "utf-8")
1413 && is_imap_stream(stream) && !hibit_in_searchpgm(pgm))
1414 charset = NULL;
1416 if(F_ON(F_NNTP_SEARCH_USES_OVERVIEW, ps_global))
1417 flags |= SO_OVERVIEW;
1419 return(stream ? mail_search_full(stream, charset, pgm, flags) : NIL);
1424 hibit_in_searchpgm(SEARCHPGM *pgm)
1426 SEARCHOR *or;
1427 SEARCHPGMLIST *not;
1429 if(!pgm)
1430 return 0;
1432 if((pgm->subject && hibit_in_strlist(pgm->subject))
1433 || (pgm->text && hibit_in_strlist(pgm->text))
1434 || (pgm->body && hibit_in_strlist(pgm->body))
1435 || (pgm->cc && hibit_in_strlist(pgm->cc))
1436 || (pgm->from && hibit_in_strlist(pgm->from))
1437 || (pgm->to && hibit_in_strlist(pgm->to))
1438 || (pgm->bcc && hibit_in_strlist(pgm->bcc))
1439 || (pgm->keyword && hibit_in_strlist(pgm->keyword))
1440 || (pgm->unkeyword && hibit_in_strlist(pgm->return_path))
1441 || (pgm->sender && hibit_in_strlist(pgm->sender))
1442 || (pgm->reply_to && hibit_in_strlist(pgm->reply_to))
1443 || (pgm->in_reply_to && hibit_in_strlist(pgm->in_reply_to))
1444 || (pgm->message_id && hibit_in_strlist(pgm->message_id))
1445 || (pgm->newsgroups && hibit_in_strlist(pgm->newsgroups))
1446 || (pgm->followup_to && hibit_in_strlist(pgm->followup_to))
1447 || (pgm->references && hibit_in_strlist(pgm->references))
1448 || (pgm->header && hibit_in_header(pgm->header)))
1449 return 1;
1451 for(or = pgm->or; or; or = or->next)
1452 if(hibit_in_searchpgm(or->first) || hibit_in_searchpgm(or->second))
1453 return 1;
1455 for(not = pgm->not; not; not = not->next)
1456 if(hibit_in_searchpgm(not->pgm))
1457 return 1;
1459 return 0;
1464 hibit_in_strlist(STRINGLIST *sl)
1466 for(; sl; sl = sl->next)
1467 if(hibit_in_sizedtext(&sl->text))
1468 return 1;
1470 return 0;
1475 hibit_in_header(SEARCHHEADER *header)
1477 SEARCHHEADER *hdr;
1479 for(hdr = header; hdr; hdr = hdr->next)
1480 if(hibit_in_sizedtext(&hdr->line) || hibit_in_sizedtext(&hdr->text))
1481 return 1;
1483 return 0;
1488 hibit_in_sizedtext(SIZEDTEXT *st)
1490 unsigned char *p, *end;
1492 p = st ? st->data : NULL;
1493 if(p)
1494 for(end = p + st->size; p < end; p++)
1495 if(*p & 0x80)
1496 return 1;
1498 return 0;
1502 void
1503 pine_mail_fetch_flags(MAILSTREAM *stream, char *sequence, long int flags)
1505 ps_global->dont_count_flagchanges = 1;
1506 mail_fetch_flags(stream, sequence, flags);
1507 ps_global->dont_count_flagchanges = 0;
1511 ENVELOPE *
1512 pine_mail_fetchenvelope(MAILSTREAM *stream, long unsigned int msgno)
1514 ENVELOPE *env = NULL;
1516 ps_global->dont_count_flagchanges = 1;
1517 if(stream && msgno > 0L && msgno <= stream->nmsgs)
1518 env = mail_fetchenvelope(stream, msgno);
1520 ps_global->dont_count_flagchanges = 0;
1521 return(env);
1525 ENVELOPE *
1526 pine_mail_fetch_structure(MAILSTREAM *stream, long unsigned int msgno,
1527 struct mail_bodystruct **body, long int flags)
1529 ENVELOPE *env = NULL;
1531 ps_global->dont_count_flagchanges = 1;
1532 if(stream && (flags & FT_UID || (msgno > 0L && msgno <= stream->nmsgs)))
1533 env = mail_fetch_structure(stream, msgno, body, flags);
1535 ps_global->dont_count_flagchanges = 0;
1536 return(env);
1540 ENVELOPE *
1541 pine_mail_fetchstructure(MAILSTREAM *stream, long unsigned int msgno, struct mail_bodystruct **body)
1543 ENVELOPE *env = NULL;
1545 ps_global->dont_count_flagchanges = 1;
1546 if(stream && msgno > 0L && msgno <= stream->nmsgs)
1547 env = mail_fetchstructure(stream, msgno, body);
1549 ps_global->dont_count_flagchanges = 0;
1550 return(env);
1555 * Wrapper around mail_fetch_body.
1556 * Currently only used to turn on partial fetching if trying
1557 * to work around the microsoft ssl bug.
1559 char *
1560 pine_mail_fetch_body(MAILSTREAM *stream, long unsigned int msgno, char *section,
1561 long unsigned int *len, long int flags)
1563 #ifdef _WINDOWS
1564 if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global))
1565 return(pine_mail_partial_fetch_wrapper(stream, msgno,
1566 section, len, flags, 0, NULL, 0));
1567 else
1568 #endif
1569 return(mail_fetch_body(stream, msgno, section, len, flags));
1573 * Wrapper around mail_fetch_text.
1574 * Currently the only purpose this wrapper serves is to turn
1575 * on partial fetching for quell-ssl-largeblocks.
1577 char *
1578 pine_mail_fetch_text(MAILSTREAM *stream, long unsigned int msgno, char *section,
1579 long unsigned int *len, long int flags)
1581 #ifdef _WINDOWS
1582 if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global))
1583 return(pine_mail_partial_fetch_wrapper(stream, msgno,
1584 section, len, flags,
1585 0, NULL, 1));
1586 else
1587 #endif
1588 return(mail_fetch_text(stream, msgno, section, len, flags));
1593 * Determine whether to do partial-fetching or not, and do it
1594 * args - same as c-client functions being wrapped around
1595 * get_n_bytes - try to partial fetch for the first n bytes.
1596 * makes no guarantees, might wind up fetching
1597 * the entire text anyway.
1598 * str_to_free - pointer to string to free if we only get
1599 * (non-cachable) partial text (required for
1600 * get_n_bytes).
1601 * is_text_fetch -
1602 * set, tells us to do mail_fetch_text and mail_partial_text
1603 * unset, tells us to do mail_fetch_body and mail_partial_body
1605 char *
1606 pine_mail_partial_fetch_wrapper(MAILSTREAM *stream, long unsigned int msgno,
1607 char *section, long unsigned int *len,
1608 long int flags, long unsigned int get_n_bytes,
1609 char **str_to_free, int is_text_fetch)
1611 BODY *body;
1612 unsigned long size, firstbyte, lastbyte;
1613 void *old_gets;
1614 FETCH_READC_S *pftc;
1615 char imap_cache_section[MAILTMPLEN];
1616 SIZEDTEXT new_text;
1617 MESSAGECACHE *mc;
1618 char *(*fetch_full)(MAILSTREAM *, unsigned long, char *,
1619 unsigned long *, long);
1620 long (*fetch_partial)(MAILSTREAM *, unsigned long, char *,
1621 unsigned long, unsigned long, long);
1623 fetch_full = is_text_fetch ? mail_fetch_text : mail_fetch_body;
1624 fetch_partial = is_text_fetch ? mail_partial_text : mail_partial_body;
1625 if(str_to_free)
1626 *str_to_free = NULL;
1627 #ifdef _WINDOWS
1628 if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global) || get_n_bytes){
1629 #else
1630 if(get_n_bytes){
1631 #endif /* _WINDOWS */
1632 if(section && *section)
1633 body = mail_body(stream, msgno, (unsigned char *) section);
1634 else
1635 pine_mail_fetch_structure(stream, msgno, &body, flags);
1636 if(!body)
1637 return NULL;
1638 if(body->type != TYPEMULTIPART)
1639 size = body->size.bytes;
1640 else if((!section || !*section) && msgno > 0L
1641 && stream && msgno <= stream->nmsgs
1642 && (mc = mail_elt(stream, msgno)))
1643 size = mc->rfc822_size; /* upper bound */
1644 else /* just a guess, can't get actual size */
1645 size = fcc_size_guess(body) + 2048;
1648 * imap_cache, originally intended for c-client internal use,
1649 * takes a section argument that is different from one we
1650 * would pass to mail_body. Typically in this function
1651 * section is NULL, which translates to "TEXT", but in other
1652 * cases we would want to append ".TEXT" to the section
1654 if(is_text_fetch)
1655 snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s%sTEXT", MAILTMPLEN - 10,
1656 section && *section ? section : "",
1657 section && *section ? "." : "");
1658 else
1659 snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s", MAILTMPLEN - 10,
1660 section && *section ? section : "");
1662 if(modern_imap_stream(stream)
1663 #ifdef _WINDOWS
1664 && ((size > AVOID_MICROSOFT_SSL_CHUNKING_BUG)
1665 || (get_n_bytes && size > get_n_bytes))
1666 #else
1667 && (get_n_bytes && size > get_n_bytes)
1668 #endif /* _WINDOWS */
1669 && !imap_cache(stream, msgno, imap_cache_section,
1670 NULL, NULL)){
1671 if(get_n_bytes == 0){
1672 dprint((8,
1673 "fetch_wrapper: doing partial fetching to work around microsoft bug\n"));
1675 else{
1676 dprint((8,
1677 "fetch_wrapper: partial fetching due to %lu get_n_bytes\n", get_n_bytes));
1679 pftc = (FETCH_READC_S *)fs_get(sizeof(FETCH_READC_S));
1680 memset(g_pft_desc = pftc, 0, sizeof(FETCH_READC_S));
1681 #ifdef _WINDOWS
1682 if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global)){
1683 if(get_n_bytes)
1684 pftc->chunksize = MIN(get_n_bytes,
1685 AVOID_MICROSOFT_SSL_CHUNKING_BUG);
1686 else
1687 pftc->chunksize = AVOID_MICROSOFT_SSL_CHUNKING_BUG;
1689 else
1690 #endif /* _WINDOWS */
1691 pftc->chunksize = get_n_bytes;
1693 pftc->chunk = (char *) fs_get((pftc->chunksize+1)
1694 * sizeof(char));
1695 pftc->cache = so_get(CharStar, NULL, EDIT_ACCESS);
1696 pftc->read = 0L;
1697 so_truncate(pftc->cache, size + 1);
1698 old_gets = mail_parameters(stream, GET_GETS, (void *)NULL);
1699 mail_parameters(stream, SET_GETS, (void *) partial_text_gets);
1700 /* start fetching */
1702 firstbyte = pftc->read ;
1703 lastbyte = firstbyte + pftc->chunksize;
1704 if(get_n_bytes > firstbyte && get_n_bytes < lastbyte){
1705 pftc->chunksize = get_n_bytes - firstbyte;
1706 lastbyte = get_n_bytes;
1708 (*fetch_partial)(stream, msgno, section, firstbyte,
1709 pftc->chunksize, flags);
1711 if(pftc->read != lastbyte)
1712 break;
1713 } while((pftc->read == lastbyte)
1714 && (!get_n_bytes || (pftc->read < get_n_bytes)));
1715 dprint((8,
1716 "fetch_wrapper: anticipated size=%lu read=%lu\n",
1717 size, pftc->read));
1718 mail_parameters(stream, SET_GETS, old_gets);
1719 new_text.size = pftc->read;
1720 new_text.data = (unsigned char *)so_text(pftc->cache);
1722 if(get_n_bytes && pftc->read == get_n_bytes){
1724 * don't write to cache if we're only dealing with
1725 * partial text.
1727 if(!str_to_free)
1728 alpine_panic("Programmer botch: partial fetch attempt w/o string pointer");
1729 else
1730 *str_to_free = (char *) new_text.data;
1732 else {
1733 /* ugh, rewrite string in case it got stomped on last call */
1734 if(is_text_fetch)
1735 snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s%sTEXT", MAILTMPLEN - 10,
1736 section && *section ? section : "",
1737 section && *section ? "." : "");
1738 else
1739 snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s", MAILTMPLEN - 10,
1740 section && *section ? section : "");
1742 imap_cache(stream, msgno, imap_cache_section, NULL, &new_text);
1745 pftc->cache->txt = (void *)NULL;
1746 so_give(&pftc->cache);
1747 fs_give((void **)&pftc->chunk);
1748 fs_give((void **)&pftc);
1749 g_pft_desc = NULL;
1750 if(len)
1751 *len = new_text.size;
1752 return ((char *)new_text.data);
1754 else
1755 return((*fetch_full)(stream, msgno, section, len, flags));
1757 else
1758 return((*fetch_full)(stream, msgno, section, len, flags));
1762 * c-client callback that handles getting the text
1764 char *
1765 partial_text_gets(readfn_t f, void *stream, long unsigned int size, GETS_DATA *md)
1767 unsigned long n;
1769 n = MIN(g_pft_desc->chunksize, size);
1770 g_pft_desc->read +=n;
1772 (*f) (stream, n, g_pft_desc->chunk);
1774 if(g_pft_desc->cache)
1775 so_nputs(g_pft_desc->cache, g_pft_desc->chunk, (long) n);
1778 return(NULL);
1783 * Pings the stream. Returns 0 if the stream is dead, non-zero otherwise.
1785 long
1786 pine_mail_ping(MAILSTREAM *stream)
1788 time_t now;
1789 long ret = 0L;
1791 if(!sp_dead_stream(stream)){
1792 ret = mail_ping(stream);
1793 if(ret && sp_dead_stream(stream))
1794 ret = 0L;
1797 if(ret){
1798 now = time(0);
1799 sp_set_last_ping(stream, now);
1800 sp_set_last_expunged_reaper(stream, now);
1803 return(ret);
1807 void
1808 pine_mail_check(MAILSTREAM *stream)
1810 reset_check_point(stream);
1811 mail_check(stream);
1816 * Unlike mail_list, this version returns a value. The returned value is
1817 * TRUE if the stream is opened ok, and FALSE if we failed opening the
1818 * stream. This allows us to differentiate between a LIST which returns
1819 * no matches and a failure opening the stream. We do this by pre-opening
1820 * the stream ourselves.
1823 pine_mail_list(MAILSTREAM *stream, char *ref, char *pat, unsigned int *options)
1825 int we_open = 0;
1826 char *halfopen_target;
1827 char source[MAILTMPLEN], *target = NULL;
1828 MAILSTREAM *ourstream = NULL;
1830 dprint((7, "pine_mail_list: ref=%s pat=%s%s\n",
1831 ref ? ref : "(NULL)",
1832 pat ? pat : "(NULL)",
1833 stream ? "" : " (stream was NULL)"));
1835 if((!ref && check_for_move_mbox(pat, source, sizeof(source), &target))
1837 check_for_move_mbox(ref, source, sizeof(source), &target)){
1838 ref = NIL;
1839 pat = target;
1840 if(options)
1841 *options |= PML_IS_MOVE_MBOX;
1843 dprint((7,
1844 "pine_mail_list: #move special case, listing \"%s\"%s\n",
1845 pat ? pat : "(NULL)",
1846 stream ? "" : " (stream was NULL)"));
1849 if(!stream && ((pat && *pat == '{') || (ref && *ref == '{'))){
1850 we_open++;
1851 if(pat && *pat == '{'){
1852 ref = NIL;
1853 halfopen_target = pat;
1855 else
1856 halfopen_target = ref;
1859 if(we_open){
1860 long flags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
1862 stream = sp_stream_get(halfopen_target, SP_MATCH);
1863 if(!stream)
1864 stream = sp_stream_get(halfopen_target, SP_SAME);
1866 if(!stream){
1867 DRIVER *d;
1870 * POP is a special case. We don't need to have a stream
1871 * to call mail_list for a POP name. The else part is the
1872 * POP part.
1874 if((d = mail_valid(NIL, halfopen_target, (char *) NIL))
1875 && (d->flags & DR_HALFOPEN)){
1876 stream = pine_mail_open(NIL, halfopen_target, flags, NULL);
1877 ourstream = stream;
1878 if(!stream)
1879 return(FALSE);
1881 else
1882 stream = NULL;
1885 mail_list_internal(stream, ref, pat);
1887 else
1888 mail_list_internal(stream, ref, pat);
1890 if(ourstream)
1891 pine_mail_close(ourstream);
1893 return(TRUE);
1898 * mail_list_internal -- A monument to software religion and those who
1899 * would force it upon us.
1901 void
1902 mail_list_internal(MAILSTREAM *s, char *r, char *p)
1904 if(F_ON(F_FIX_BROKEN_LIST, ps_global)
1905 && ((s && s->mailbox && *s->mailbox == '{')
1906 || (!s && ((r && *r == '{') || (p && *p == '{'))))){
1907 char tmp[2*MAILTMPLEN];
1908 /* MAILTMPLEN = sizeof(tmp)/2 */
1909 snprintf(tmp, sizeof(tmp), "%.*s%.*s", MAILTMPLEN-1, r ? r : "",
1910 MAILTMPLEN-1, p);
1911 mail_list(s, "", tmp);
1913 else
1914 mail_list(s, r, p);
1918 long
1919 pine_mail_status(MAILSTREAM *stream, char *mailbox, long int flags)
1921 return(pine_mail_status_full(stream, mailbox, flags, NULL, NULL));
1925 long
1926 pine_mail_status_full(MAILSTREAM *stream, char *mailbox, long int flags,
1927 imapuid_t *uidvalidity, imapuid_t *uidnext)
1929 char source[MAILTMPLEN], *target = NULL;
1930 long ret = NIL;
1931 MAILSTATUS cache, status;
1932 MAILSTREAM *ourstream = NULL;
1934 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
1935 memset(&status, 0, sizeof(status));
1936 memset(&cache, 0, sizeof(cache));
1938 /* tell mm_status() to write partial return here */
1939 pine_cached_status = &status;
1941 flags |= (SA_UIDVALIDITY | SA_UIDNEXT | SA_MESSAGES);
1943 /* do status of destination */
1945 stream = sp_stream_get(target, SP_SAME);
1947 /* should never be news, don't worry about mulnewrsc flag*/
1948 if((ret = pine_mail_status_full(stream, target, flags, uidvalidity,
1949 uidnext))
1950 && !status.recent){
1952 /* do status of source */
1953 pine_cached_status = &cache;
1954 stream = sp_stream_get(source, SP_SAME);
1956 if(!stream){
1957 DRIVER *d;
1959 if((d = mail_valid (NIL, source, (char *) NIL))
1960 && !strcmp(d->name, "imap")){
1961 long openflags =OP_HALFOPEN|OP_SILENT|SP_USEPOOL|SP_TEMPUSE;
1963 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1964 openflags |= OP_TRYALT;
1966 stream = pine_mail_open(NULL, source, openflags, NULL);
1967 ourstream = stream;
1969 else if(F_ON(F_ENABLE_MULNEWSRCS, ps_global)
1970 && d && (!strucmp(d->name, "news")
1971 || !strucmp(d->name, "nntp")))
1972 flags |= SA_MULNEWSRC;
1976 if(!ps_global->user_says_cancel && mail_status(stream, source, flags)){
1977 DRIVER *d;
1978 int is_news = 0;
1981 * If the target has recent messages, then we don't come
1982 * through here. We just use the answer from the target.
1984 * If not, then we leave the target results in "status" and
1985 * run a mail_status on the source that puts its results
1986 * in "cache". Combine the results from cache with the
1987 * results that were already in status.
1989 * We count all messages in the source mailbox as recent and
1990 * unseen, even if they are not recent or unseen in the source,
1991 * because they will be recent and unseen in the target
1992 * when we open it. (Not quite true. It is possible that some
1993 * messages from a POP server will end up seen instead
1994 * of unseen.
1995 * It is also possible that it is correct. If we add unseen, or
1996 * if we add messages, we could get it wrong. As far as I
1997 * can tell, Pine doesn't ever even use status.unseen, so it
1998 * is currently academic anyway.) Hubert 2003-03-05
1999 * (Does now 2004-06-02 in next_folder.)
2001 * However, we don't want to count all messages as recent if
2002 * the source mailbox is NNTP or NEWS, because we won't be
2003 * deleting those messages from the source.
2004 * We only count recent.
2006 * There are other cases that are trouble. One in particular
2007 * is an IMAP-to-NNTP proxy, where the messages can't be removed
2008 * from the mailbox but they can be deleted. If we count
2009 * messages in the source as being recent and it turns out they
2010 * were all deleted already, then we incorrectly say the folder
2011 * has recent messages when it doesn't. We can recover from that
2012 * case at some cost by actually opening the folder the first
2013 * time if there are not recents, and then checking to see if
2014 * everything is deleted. Subsequently, we store the uid values
2015 * (which are returned by status) so that we can know if the
2016 * mailbox changed or not. The problem being solved is that
2017 * the TAB command indicates new messages in a folder when there
2018 * really aren't any. An alternative would be to use the is_news
2019 * half of the if-else in all cases. A problem with that is
2020 * that there could be non-recent messages sitting in the
2021 * source mailbox that we never discover. Hubert 2003-03-28
2024 if((d = mail_valid (NIL, source, (char *) NIL))
2025 && (!strcmp(d->name, "nntp") || !strcmp(d->name, "news")))
2026 is_news++;
2028 if(is_news && cache.flags & SA_RECENT){
2029 status.messages += cache.recent;
2030 status.recent += cache.recent;
2031 status.unseen += cache.recent;
2032 status.uidnext += cache.recent;
2034 else{
2035 if(uidvalidity && *uidvalidity
2036 && uidnext && *uidnext
2037 && cache.flags & SA_UIDVALIDITY
2038 && cache.uidvalidity == *uidvalidity
2039 && cache.flags & SA_UIDNEXT
2040 && cache.uidnext == *uidnext){
2041 ; /* nothing changed in source mailbox */
2043 else if(cache.flags & SA_RECENT && cache.recent){
2044 status.messages += cache.recent;
2045 status.recent += cache.recent;
2046 status.unseen += cache.recent;
2047 status.uidnext += cache.recent;
2049 else if(!(cache.flags & SA_MESSAGES) || cache.messages){
2050 long openflags = OP_SILENT | SP_USEPOOL | SP_TEMPUSE;
2051 long delete_count, not_deleted = 0L;
2053 /* Actually open it up and check */
2054 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
2055 openflags |= OP_TRYALT;
2057 if(!ourstream)
2058 stream = NULL;
2060 if(ourstream
2061 && !same_stream_and_mailbox(source, ourstream)){
2062 pine_mail_close(ourstream);
2063 ourstream = stream = NULL;
2066 if(!stream){
2067 stream = pine_mail_open(stream, source, openflags,
2068 NULL);
2069 ourstream = stream;
2072 if(stream){
2073 delete_count = count_flagged(stream, F_DEL);
2074 not_deleted = stream->nmsgs - delete_count;
2077 status.messages += not_deleted;
2078 status.recent += not_deleted;
2079 status.unseen += not_deleted;
2080 status.uidnext += not_deleted;
2083 if(uidvalidity && cache.flags & SA_UIDVALIDITY)
2084 *uidvalidity = cache.uidvalidity;
2086 if(uidnext && cache.flags & SA_UIDNEXT)
2087 *uidnext = cache.uidnext;
2093 * Do the regular mm_status callback which we've been intercepting
2094 * into different locations above.
2096 pine_cached_status = NIL;
2097 if(ret)
2098 mm_status(NULL, mailbox, &status);
2100 else{
2101 if(!stream){
2102 DRIVER *d;
2104 if((d = mail_valid (NIL, mailbox, (char *) NIL))
2105 && !strcmp(d->name, "imap")){
2106 long openflags = OP_HALFOPEN|OP_SILENT|SP_USEPOOL|SP_TEMPUSE;
2108 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
2109 openflags |= OP_TRYALT;
2112 * We just use this to find the answer.
2113 * We're asking for trouble if we do a STATUS on a
2114 * selected mailbox. I don't believe this happens in pine.
2115 * It does now (2004-06-02) in next_folder if the
2116 * F_TAB_USES_UNSEEN option is set and the folder was
2117 * already opened.
2119 stream = sp_stream_get(mailbox, SP_MATCH);
2120 if(stream){
2121 memset(&status, 0, sizeof(status));
2122 if(flags & SA_MESSAGES){
2123 status.flags |= SA_MESSAGES;
2124 status.messages = stream->nmsgs;
2127 if(flags & SA_RECENT){
2128 status.flags |= SA_RECENT;
2129 status.recent = stream->recent;
2132 if(flags & SA_UNSEEN){
2133 long i;
2134 SEARCHPGM *srchpgm;
2135 MESSAGECACHE *mc;
2137 srchpgm = mail_newsearchpgm();
2138 srchpgm->unseen = 1;
2140 pine_mail_search_full(stream, NULL, srchpgm,
2141 SE_NOPREFETCH | SE_FREE);
2142 status.flags |= SA_UNSEEN;
2143 status.unseen = 0L;
2144 for(i = 1L; i <= stream->nmsgs; i++)
2145 if((mc = mail_elt(stream, i)) && mc->searched)
2146 status.unseen++;
2149 if(flags & SA_UIDVALIDITY){
2150 status.flags |= SA_UIDVALIDITY;
2151 status.uidvalidity = stream->uid_validity;
2154 if(flags & SA_UIDNEXT){
2155 status.flags |= SA_UIDNEXT;
2156 status.uidnext = stream->uid_last + 1L;
2159 mm_status(NULL, mailbox, &status);
2160 return T; /* that's what c-client returns when success */
2163 if(!stream)
2164 stream = sp_stream_get(mailbox, SP_SAME);
2166 if(!stream){
2167 stream = pine_mail_open(NULL, mailbox, openflags, NULL);
2168 ourstream = stream;
2171 else if(F_ON(F_ENABLE_MULNEWSRCS, ps_global)
2172 && d && (!strucmp(d->name, "news")
2173 || !strucmp(d->name, "nntp")))
2174 flags |= SA_MULNEWSRC;
2177 if(!ps_global->user_says_cancel)
2178 ret = mail_status(stream, mailbox, flags); /* non #move case */
2181 if(ourstream)
2182 pine_mail_close(ourstream);
2184 return ret;
2189 * Check for a mailbox name that is a legitimate #move mailbox.
2191 * Args mbox -- The mailbox name to check
2192 * sourcebuf -- Copy the source mailbox name into this buffer
2193 * sbuflen -- Length of sourcebuf
2194 * targetptr -- Set the pointer this points to to point to the
2195 * target mailbox name in the original string
2197 * Returns 1 - is a #move mailbox
2198 * 0 - not
2201 check_for_move_mbox(char *mbox, char *sourcebuf, size_t sbuflen, char **targetptr)
2203 char delim, *s;
2204 int i;
2206 if(mbox && (mbox[0] == '#')
2207 && ((mbox[1] == 'M') || (mbox[1] == 'm'))
2208 && ((mbox[2] == 'O') || (mbox[2] == 'o'))
2209 && ((mbox[3] == 'V') || (mbox[3] == 'v'))
2210 && ((mbox[4] == 'E') || (mbox[4] == 'e'))
2211 && (delim = mbox[5])
2212 && (s = strchr(mbox+6, delim))
2213 && (i = s++ - (mbox + 6))
2214 && (!sourcebuf || i < sbuflen)){
2216 if(sourcebuf){
2217 strncpy(sourcebuf, mbox+6, i); /* copy source mailbox name */
2218 sourcebuf[i] = '\0';
2221 if(targetptr)
2222 *targetptr = s;
2224 return 1;
2227 return 0;
2232 * Checks through stream cache for a stream pointer already open to
2233 * this mailbox, read/write. Very similar to sp_stream_get, but we want
2234 * to look at all streams, not just imap streams.
2235 * Right now it is very specialized. If we want to use it more generally,
2236 * generalize it or combine it with sp_stream_get somehow.
2238 MAILSTREAM *
2239 already_open_stream(char *mailbox, int flags)
2241 int i;
2242 MAILSTREAM *m;
2244 if(!mailbox)
2245 return(NULL);
2247 if(*mailbox == '{'){
2248 for(i = 0; i < ps_global->s_pool.nstream; i++){
2249 m = ps_global->s_pool.streams[i];
2250 if(m && !(flags & AOS_RW_ONLY && m->rdonly)
2251 && (*m->mailbox == '{') && !sp_dead_stream(m)
2252 && same_stream_and_mailbox(mailbox, m))
2253 return(m);
2256 else{
2257 char *cn, tmp[MAILTMPLEN];
2259 cn = mailboxfile(tmp, mailbox);
2260 for(i = 0; i < ps_global->s_pool.nstream; i++){
2261 m = ps_global->s_pool.streams[i];
2262 if(m && !(flags & AOS_RW_ONLY && m->rdonly)
2263 && (*m->mailbox != '{') && !sp_dead_stream(m)
2264 && ((cn && *cn && !strcmp(cn, m->mailbox))
2265 || !strcmp(mailbox, m->original_mailbox)
2266 || !strcmp(mailbox, m->mailbox)))
2267 return(m);
2271 return(NULL);
2275 void
2276 pine_imap_cmd_happened(MAILSTREAM *stream, char *cmd, long int flags)
2278 dprint((9, "imap_cmd(%s, %s, 0x%lx)\n",
2279 STREAMNAME(stream), cmd ? cmd : "?", flags));
2281 if(cmd && !strucmp(cmd, "CHECK"))
2282 reset_check_point(stream);
2284 ps_global->can_interrupt = 0; /* never interrupt anything */
2286 if(is_imap_stream(stream)){
2287 time_t now;
2289 now = time(0);
2290 sp_set_last_ping(stream, now);
2291 sp_set_last_activity(stream, now);
2292 if(!(flags & SC_EXPUNGEDEFERRED))
2293 sp_set_last_expunged_reaper(stream, now);
2294 /* but can interrupt these ones */
2295 if(cmd && (!strucmp(cmd, "NOOP") || !strucmp(cmd, "LOGOUT")))
2296 ps_global->can_interrupt = 1;
2302 * Tells us whether we ought to check for a dead stream or not.
2303 * We assume that we ought to check if it is not IMAP and if it is IMAP we
2304 * don't need to check if the last activity was within the last 5 minutes.
2307 recent_activity(MAILSTREAM *stream)
2309 if(is_imap_stream(stream) && !sp_dead_stream(stream)
2310 && (time(0) - sp_last_activity(stream) < 5L * 60L))
2311 return 1;
2312 else
2313 return 0;
2316 void
2317 sp_cleanup_dead_streams(void)
2319 int i;
2320 MAILSTREAM *m;
2322 (void) streams_died(); /* tell user in case they don't know yet */
2324 for(i = 0; i < ps_global->s_pool.nstream; i++){
2325 m = ps_global->s_pool.streams[i];
2326 if(m && sp_dead_stream(m))
2327 pine_mail_close(m);
2333 * Returns 0 if stream flags not set, non-zero if they are.
2336 sp_flagged(MAILSTREAM *stream, long unsigned int flags)
2338 return(sp_flags(stream) & flags);
2342 void
2343 sp_set_fldr(MAILSTREAM *stream, char *folder)
2345 PER_STREAM_S **pss;
2347 pss = sp_data(stream);
2348 if(pss && *pss){
2349 if((*pss)->fldr)
2350 fs_give((void **) &(*pss)->fldr);
2352 if(folder)
2353 (*pss)->fldr = cpystr(folder);
2358 void
2359 sp_set_saved_cur_msg_id(MAILSTREAM *stream, char *id)
2361 PER_STREAM_S **pss;
2363 pss = sp_data(stream);
2364 if(pss && *pss){
2365 if((*pss)->saved_cur_msg_id)
2366 fs_give((void **) &(*pss)->saved_cur_msg_id);
2368 if(id)
2369 (*pss)->saved_cur_msg_id = cpystr(id);
2375 * Sets flags absolutely, erasing old flags.
2377 void
2378 sp_flag(MAILSTREAM *stream, long unsigned int flags)
2380 if(!stream)
2381 return;
2383 dprint((9, "sp_flag(%s, 0x%x): %s%s%s%s%s%s%s%s\n",
2384 (stream && stream->mailbox) ? stream->mailbox : "?",
2385 flags,
2386 flags ? "set" : "clear",
2387 (flags & SP_LOCKED) ? " SP_LOCKED" : "",
2388 (flags & SP_PERMLOCKED) ? " SP_PERMLOCKED" : "",
2389 (flags & SP_INBOX) ? " SP_INBOX" : "",
2390 (flags & SP_USERFLDR) ? " SP_USERFLDR" : "",
2391 (flags & SP_USEPOOL) ? " SP_USEPOOL" : "",
2392 (flags & SP_TEMPUSE) ? " SP_TEMPUSE" : "",
2393 !flags ? " ALL" : ""));
2395 sp_set_flags(stream, flags);
2400 * Clear individual stream flags.
2402 void
2403 sp_unflag(MAILSTREAM *stream, long unsigned int flags)
2405 if(!stream || !flags)
2406 return;
2408 dprint((9, "sp_unflag(%s, 0x%x): unset%s%s%s%s%s%s\n",
2409 (stream && stream->mailbox) ? stream->mailbox : "?",
2410 flags,
2411 (flags & SP_LOCKED) ? " SP_LOCKED" : "",
2412 (flags & SP_PERMLOCKED) ? " SP_PERMLOCKED" : "",
2413 (flags & SP_INBOX) ? " SP_INBOX" : "",
2414 (flags & SP_USERFLDR) ? " SP_USERFLDR" : "",
2415 (flags & SP_USEPOOL) ? " SP_USEPOOL" : "",
2416 (flags & SP_TEMPUSE) ? " SP_TEMPUSE" : ""));
2418 sp_set_flags(stream, sp_flags(stream) & ~flags);
2420 flags = sp_flags(stream);
2421 dprint((9, "sp_unflag(%s, 0x%x): result:%s%s%s%s%s%s\n",
2422 (stream && stream->mailbox) ? stream->mailbox : "?",
2423 flags,
2424 (flags & SP_LOCKED) ? " SP_LOCKED" : "",
2425 (flags & SP_PERMLOCKED) ? " SP_PERMLOCKED" : "",
2426 (flags & SP_INBOX) ? " SP_INBOX" : "",
2427 (flags & SP_USERFLDR) ? " SP_USERFLDR" : "",
2428 (flags & SP_USEPOOL) ? " SP_USEPOOL" : "",
2429 (flags & SP_TEMPUSE) ? " SP_TEMPUSE" : ""));
2434 * Set dead stream indicator and close if not locked.
2436 void
2437 sp_mark_stream_dead(MAILSTREAM *stream)
2439 if(!stream)
2440 return;
2442 dprint((9, "sp_mark_stream_dead(%s)\n",
2443 (stream && stream->mailbox) ? stream->mailbox : "?"));
2446 * If the stream isn't locked, it is no longer useful. Get rid of it.
2448 if(!sp_flagged(stream, SP_LOCKED))
2449 pine_mail_actually_close(stream);
2450 else{
2452 * If it is locked, then we have to worry about references to it
2453 * that still exist. For example, it might be a permlocked stream
2454 * or it might be the current stream. We need to let it be discovered
2455 * by those referencers instead of just eliminating it, so that they
2456 * can clean up the mess they need to clean up.
2458 sp_set_dead_stream(stream, 1);
2464 * Returns the number of streams in the stream pool which are
2465 * SP_USEPOOL but not SP_PERMLOCKED.
2468 sp_nusepool_notperm(void)
2470 int i, cnt = 0;
2471 MAILSTREAM *m;
2473 for(i = 0; i < ps_global->s_pool.nstream; i++){
2474 m = ps_global->s_pool.streams[i];
2475 if(sp_flagged(m, SP_USEPOOL) && !sp_flagged(m, SP_PERMLOCKED))
2476 cnt++;
2479 return(cnt);
2484 * Returns the number of folders that the user has marked to be PERMLOCKED
2485 * folders (plus INBOX) that are remote IMAP folders.
2487 * This routine depends on the fact that VAR_INBOX_PATH, VAR_PERMLOCKED,
2488 * and the ps_global->context_list are correctly set.
2491 sp_nremote_permlocked(void)
2493 int cnt = 0;
2494 char **lock_these, *p = NULL, *dummy = NULL, *lt;
2495 DRIVER *d;
2497 /* first check if INBOX is remote */
2498 lt = ps_global->VAR_INBOX_PATH;
2499 if(lt && (d=mail_valid(NIL, lt, (char *) NIL))
2500 && !strcmp(d->name, "imap"))
2501 cnt++;
2503 /* then count the user-specified permlocked folders */
2504 for(lock_these = ps_global->VAR_PERMLOCKED; lock_these && *lock_these;
2505 lock_these++){
2508 * Skip inbox, already done above. Should do this better so that we
2509 * catch the case where the user puts the technical spec of the inbox
2510 * in the list, or where the user lists one folder twice.
2512 if(*lock_these && !strucmp(*lock_these, ps_global->inbox_name))
2513 continue;
2515 /* there isn't really a pair, it just dequotes for us */
2516 get_pair(*lock_these, &dummy, &p, 0, 0);
2519 * Check to see if this is an incoming nickname and replace it
2520 * with the full name.
2522 if(!(p && ps_global->context_list
2523 && ps_global->context_list->use & CNTXT_INCMNG
2524 && (lt=folder_is_nick(p, FOLDERS(ps_global->context_list),
2525 FN_WHOLE_NAME))))
2526 lt = p;
2528 if(dummy)
2529 fs_give((void **) &dummy);
2531 if(lt && (d=mail_valid(NIL, lt, (char *) NIL))
2532 && !strcmp(d->name, "imap"))
2533 cnt++;
2535 if(p)
2536 fs_give((void **) &p);
2539 return(cnt);
2544 * Look for an already open stream that can be used for a new purpose.
2545 * (Note that we only look through streams flagged SP_USEPOOL.)
2547 * Args: mailbox
2548 * flags
2550 * Flags is a set of values or'd together which tells us what the request
2551 * is looking for.
2553 * Returns: a live stream from the stream pool or NULL.
2555 MAILSTREAM *
2556 sp_stream_get(char *mailbox, long unsigned int flags)
2558 int i;
2559 MAILSTREAM *m;
2561 dprint((7, "sp_stream_get(%s):%s%s%s%s%s\n",
2562 mailbox ? mailbox : "?",
2563 (flags & SP_MATCH) ? " SP_MATCH" : "",
2564 (flags & SP_RO_OK) ? " SP_RO_OK" : "",
2565 (flags & SP_SAME) ? " SP_SAME" : "",
2566 (flags & SP_UNLOCKED) ? " SP_UNLOCKED" : "",
2567 (flags & SP_TEMPUSE) ? " SP_TEMPUSE" : ""));
2569 /* look for stream already open to this mailbox */
2570 if(flags & SP_MATCH){
2571 for(i = 0; i < ps_global->s_pool.nstream; i++){
2572 m = ps_global->s_pool.streams[i];
2573 if(m && sp_flagged(m, SP_USEPOOL)
2574 && (!m->rdonly || (flags & SP_RO_OK)) && !sp_dead_stream(m)
2575 && same_stream_and_mailbox(mailbox, m)){
2576 if((sp_flagged(m, SP_LOCKED) && recent_activity(m))
2577 || pine_mail_ping(m)){
2578 dprint((7,
2579 "sp_stream_get: found exact match, slot %d\n", i));
2580 if(!sp_flagged(m, SP_LOCKED)){
2581 dprint((7,
2582 "reset idle timer1: next TAG %08lx (%s)\n",
2583 m->gensym,
2584 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
2585 sp_set_last_use_time(m, time(0));
2588 return(m);
2591 sp_mark_stream_dead(m);
2597 * SP_SAME will not match if an SP_MATCH match would have worked.
2598 * If the caller is interested in SP_MATCH streams as well as SP_SAME
2599 * streams then the caller should make two separate calls to this
2600 * routine.
2602 if(flags & SP_SAME){
2604 * If the flags arg does not have either SP_TEMPUSE or SP_UNLOCKED
2605 * set, then we'll accept any stream, even if locked.
2606 * We want to prefer the LOCKED case so that we don't have to ping.
2608 if(!(flags & SP_UNLOCKED) && !(flags & SP_TEMPUSE)){
2609 for(i = 0; i < ps_global->s_pool.nstream; i++){
2610 m = ps_global->s_pool.streams[i];
2611 if(m && sp_flagged(m, SP_USEPOOL)
2612 && sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
2613 && same_stream(mailbox, m)
2614 && !same_stream_and_mailbox(mailbox, m)){
2615 if(recent_activity(m) || pine_mail_ping(m)){
2616 dprint((7,
2617 "sp_stream_get: found SAME match, slot %d\n", i));
2618 return(m);
2621 sp_mark_stream_dead(m);
2625 /* consider the unlocked streams */
2626 for(i = 0; i < ps_global->s_pool.nstream; i++){
2627 m = ps_global->s_pool.streams[i];
2628 if(m && sp_flagged(m, SP_USEPOOL)
2629 && !sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
2630 && same_stream(mailbox, m)
2631 && !same_stream_and_mailbox(mailbox, m)){
2632 /* always ping unlocked streams */
2633 if(pine_mail_ping(m)){
2634 dprint((7,
2635 "sp_stream_get: found SAME match, slot %d\n", i));
2636 dprint((7,
2637 "reset idle timer4: next TAG %08lx (%s)\n",
2638 m->gensym,
2639 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
2640 sp_set_last_use_time(m, time(0));
2642 return(m);
2645 sp_mark_stream_dead(m);
2651 * Prefer streams marked SP_TEMPUSE and not LOCKED.
2652 * If SP_TEMPUSE is set in the flags arg then this is the
2653 * only loop we try.
2655 for(i = 0; i < ps_global->s_pool.nstream; i++){
2656 m = ps_global->s_pool.streams[i];
2657 if(m && sp_flagged(m, SP_USEPOOL) && sp_flagged(m, SP_TEMPUSE)
2658 && !sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
2659 && same_stream(mailbox, m)
2660 && !same_stream_and_mailbox(mailbox, m)){
2661 if(pine_mail_ping(m)){
2662 dprint((7,
2663 "sp_stream_get: found SAME/TEMPUSE match, slot %d\n", i));
2664 dprint((7,
2665 "reset idle timer2: next TAG %08lx (%s)\n",
2666 m->gensym,
2667 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
2668 sp_set_last_use_time(m, time(0));
2669 return(m);
2672 sp_mark_stream_dead(m);
2677 * If SP_TEMPUSE is not set in the flags arg but SP_UNLOCKED is,
2678 * then we will consider
2679 * streams which are not marked SP_TEMPUSE (but are still not
2680 * locked). We go through these in reverse order so that we'll get
2681 * the last one added instead of the first one. It's not clear if
2682 * that is a good idea or if a more complex search would somehow
2683 * be better. Maybe we should use a round-robin sort of search
2684 * here so that we don't leave behind unused streams. Or maybe
2685 * we should keep track of when we used it and look for the LRU stream.
2687 if(!(flags & SP_TEMPUSE)){
2688 for(i = ps_global->s_pool.nstream - 1; i >= 0; i--){
2689 m = ps_global->s_pool.streams[i];
2690 if(m && sp_flagged(m, SP_USEPOOL)
2691 && !sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
2692 && same_stream(mailbox, m)
2693 && !same_stream_and_mailbox(mailbox, m)){
2694 if(pine_mail_ping(m)){
2695 dprint((7,
2696 "sp_stream_get: found SAME/UNLOCKED match, slot %d\n", i));
2697 dprint((7,
2698 "reset idle timer3: next TAG %08lx (%s)\n",
2699 m->gensym,
2700 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
2701 sp_set_last_use_time(m, time(0));
2702 return(m);
2705 sp_mark_stream_dead(m);
2712 * If we can't find a useful stream to use in pine_mail_open, we may
2713 * want to re-use one that is not actively being used even though it
2714 * is not on the same server. We'll have to close it and then re-use
2715 * the slot.
2717 if(!(flags & (SP_SAME | SP_MATCH))){
2719 * Prefer streams marked SP_TEMPUSE and not LOCKED.
2720 * If SP_TEMPUSE is set in the flags arg then this is the
2721 * only loop we try.
2723 for(i = 0; i < ps_global->s_pool.nstream; i++){
2724 m = ps_global->s_pool.streams[i];
2725 if(m && sp_flagged(m, SP_USEPOOL) && sp_flagged(m, SP_TEMPUSE)
2726 && !sp_flagged(m, SP_LOCKED)){
2727 dprint((7,
2728 "sp_stream_get: found Not-SAME/TEMPUSE match, slot %d\n", i));
2730 * We ping it in case there is new mail that we should
2731 * pass through our filters. Pine_mail_actually_close will
2732 * do that.
2734 (void) pine_mail_ping(m);
2735 return(m);
2740 * If SP_TEMPUSE is not set in the flags arg, then we will consider
2741 * streams which are not marked SP_TEMPUSE (but are still not
2742 * locked). Maybe we should use a round-robin sort of search
2743 * here so that we don't leave behind unused streams. Or maybe
2744 * we should keep track of when we used it and look for the LRU stream.
2746 if(!(flags & SP_TEMPUSE)){
2747 for(i = ps_global->s_pool.nstream - 1; i >= 0; i--){
2748 m = ps_global->s_pool.streams[i];
2749 if(m && sp_flagged(m, SP_USEPOOL) && !sp_flagged(m, SP_LOCKED)){
2750 dprint((7,
2751 "sp_stream_get: found Not-SAME/UNLOCKED match, slot %d\n", i));
2753 * We ping it in case there is new mail that we should
2754 * pass through our filters. Pine_mail_actually_close will
2755 * do that.
2757 (void) pine_mail_ping(m);
2758 return(m);
2764 dprint((7, "sp_stream_get: no match found\n"));
2766 return(NULL);
2770 void
2771 sp_end(void)
2773 int i;
2774 MAILSTREAM *m;
2776 dprint((7, "sp_end\n"));
2778 for(i = 0; i < ps_global->s_pool.nstream; i++){
2779 m = ps_global->s_pool.streams[i];
2780 if(m)
2781 pine_mail_actually_close(m);
2784 if(ps_global->s_pool.streams)
2785 fs_give((void **) &ps_global->s_pool.streams);
2787 ps_global->s_pool.nstream = 0;
2792 * Find a vacant slot to put this new stream in.
2793 * We are willing to close and kick out another stream as long as it isn't
2794 * LOCKED. However, we may find that there is no place to put this one
2795 * because all the slots are used and locked. For now, we'll return -1
2796 * in that case and leave the new stream out of the pool.
2799 sp_add(MAILSTREAM *stream, int usepool)
2801 int i, slot = -1;
2802 MAILSTREAM *m;
2804 dprint((7, "sp_add(%s, %d)\n",
2805 (stream && stream->mailbox) ? stream->mailbox : "?", usepool));
2807 if(!stream){
2808 dprint((7, "sp_add: NULL stream\n"));
2809 return -1;
2812 /* If this stream is already there, don't add it again */
2813 for(i = 0; i < ps_global->s_pool.nstream; i++){
2814 m = ps_global->s_pool.streams[i];
2815 if(m == stream){
2816 slot = i;
2817 dprint((7,
2818 "sp_add: stream was already in slot %d\n", slot));
2819 return 0;
2823 if(usepool && !sp_flagged(stream, SP_PERMLOCKED)
2824 && sp_nusepool_notperm() >= ps_global->s_pool.max_remstream){
2825 dprint((7,
2826 "sp_add: reached max implicit SP_USEPOOL of %d\n",
2827 ps_global->s_pool.max_remstream));
2828 return -1;
2831 /* Look for an unused slot */
2832 for(i = 0; i < ps_global->s_pool.nstream; i++){
2833 m = ps_global->s_pool.streams[i];
2834 if(!m){
2835 slot = i;
2836 dprint((7,
2837 "sp_add: using empty slot %d\n", slot));
2838 break;
2842 /* else, allocate more space */
2843 if(slot < 0){
2844 ps_global->s_pool.nstream++;
2845 slot = ps_global->s_pool.nstream - 1;
2846 if(ps_global->s_pool.streams){
2847 fs_resize((void **) &ps_global->s_pool.streams,
2848 ps_global->s_pool.nstream *
2849 sizeof(*ps_global->s_pool.streams));
2850 ps_global->s_pool.streams[slot] = NULL;
2852 else{
2853 ps_global->s_pool.streams =
2854 (MAILSTREAM **) fs_get(ps_global->s_pool.nstream *
2855 sizeof(*ps_global->s_pool.streams));
2856 memset(ps_global->s_pool.streams, 0,
2857 ps_global->s_pool.nstream *
2858 sizeof(*ps_global->s_pool.streams));
2861 dprint((7,
2862 "sp_add: allocate more space, using new slot %d\n", slot));
2865 if(slot >= 0 && slot < ps_global->s_pool.nstream){
2866 ps_global->s_pool.streams[slot] = stream;
2867 return 0;
2869 else{
2870 dprint((7, "sp_add: failed to find a slot!\n"));
2871 return -1;
2877 * Simply remove this stream from the stream pool.
2879 void
2880 sp_delete(MAILSTREAM *stream)
2882 int i;
2883 MAILSTREAM *m;
2885 if(!stream)
2886 return;
2888 dprint((7, "sp_delete(%s)\n",
2889 (stream && stream->mailbox) ? stream->mailbox : "?"));
2892 * There are some global stream pointers that we have to worry
2893 * about before deleting the stream.
2896 /* first, mail_stream is the global currently open folder */
2897 if(ps_global->mail_stream == stream)
2898 ps_global->mail_stream = NULL;
2900 /* remote address books may have open stream pointers */
2901 note_closed_adrbk_stream(stream);
2903 if(pith_opt_closing_stream)
2904 (*pith_opt_closing_stream)(stream);
2906 for(i = 0; i < ps_global->s_pool.nstream; i++){
2907 m = ps_global->s_pool.streams[i];
2908 if(m == stream){
2909 ps_global->s_pool.streams[i] = NULL;
2910 dprint((7,
2911 "sp_delete: stream removed from slot %d\n", i));
2912 return;
2919 * Returns 1 if any locked userfldr is dead, 0 if all alive.
2922 sp_a_locked_stream_is_dead(void)
2924 int i, ret = 0;
2925 MAILSTREAM *m;
2927 for(i = 0; !ret && i < ps_global->s_pool.nstream; i++){
2928 m = ps_global->s_pool.streams[i];
2929 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
2930 && sp_dead_stream(m))
2931 ret++;
2934 return(ret);
2939 * Returns 1 if any locked stream is changed, 0 otherwise
2942 sp_a_locked_stream_changed(void)
2944 int i, ret = 0;
2945 MAILSTREAM *m;
2947 for(i = 0; !ret && i < ps_global->s_pool.nstream; i++){
2948 m = ps_global->s_pool.streams[i];
2949 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
2950 && sp_mail_box_changed(m))
2951 ret++;
2954 return(ret);
2959 * Returns the inbox stream or NULL.
2961 MAILSTREAM *
2962 sp_inbox_stream(void)
2964 int i;
2965 MAILSTREAM *m, *ret = NULL;
2967 for(i = 0; !ret && i < ps_global->s_pool.nstream; i++){
2968 m = ps_global->s_pool.streams[i];
2969 if(m && sp_flagged(m, SP_INBOX))
2970 ret = m;
2973 return(ret);
2978 * Make sure that the sp_data per-stream data storage area exists.
2980 * Returns a handle to the sp_data data unless stream is NULL,
2981 * in which case NULL is returned
2983 PER_STREAM_S **
2984 sp_data(MAILSTREAM *stream)
2986 PER_STREAM_S **pss = NULL;
2988 if(stream){
2989 if(*(pss = (PER_STREAM_S **) &stream->sparep) == NULL){
2990 *pss = (PER_STREAM_S *) fs_get(sizeof(PER_STREAM_S));
2991 memset(*pss, 0, sizeof(PER_STREAM_S));
2992 reset_check_point(stream);
2996 return(pss);
3001 * Returns a pointer to the msgmap associated with the argument stream.
3003 * If the PER_STREAM_S data or the msgmap does not already exist, it will be
3004 * created.
3006 MSGNO_S *
3007 sp_msgmap(MAILSTREAM *stream)
3009 MSGNO_S **msgmap = NULL;
3010 PER_STREAM_S **pss = NULL;
3012 pss = sp_data(stream);
3014 if(pss && *pss
3015 && (*(msgmap = (MSGNO_S **) &(*pss)->msgmap) == NULL))
3016 mn_init(msgmap, stream->nmsgs);
3018 return(msgmap ? *msgmap : NULL);
3022 void
3023 sp_free_callback(void **sparep)
3025 PER_STREAM_S **pss;
3026 MAILSTREAM *stream = NULL, *m;
3027 int i;
3029 pss = (PER_STREAM_S **) sparep;
3031 if(pss && *pss){
3033 * It is possible that this has been called from c-client when
3034 * we weren't expecting it. We need to clean up the stream pool
3035 * entries if the stream that goes with this pointer is in the
3036 * stream pool somewhere.
3038 for(i = 0; !stream && i < ps_global->s_pool.nstream; i++){
3039 m = ps_global->s_pool.streams[i];
3040 if(sparep && *sparep && m && m->sparep == *sparep)
3041 stream = m;
3044 if(stream){
3045 if(ps_global->mail_stream == stream)
3046 ps_global->mail_stream = NULL;
3048 sp_delete(stream);
3051 sp_free(pss);
3057 * Free the data but don't mess with the stream pool.
3059 void
3060 sp_free(PER_STREAM_S **pss)
3062 if(pss && *pss){
3063 if((*pss)->msgmap){
3064 if(ps_global->msgmap == (*pss)->msgmap)
3065 ps_global->msgmap = NULL;
3067 mn_give(&(*pss)->msgmap);
3070 if((*pss)->fldr)
3071 fs_give((void **) &(*pss)->fldr);
3073 fs_give((void **) pss);
3079 /*----------------------------------------------------------------------
3080 See if stream can be used for a mailbox name
3082 Accepts: mailbox name
3083 candidate stream
3084 Returns: stream if it can be used, else NIL
3086 This is called to weed out unnecessary use of c-client streams. In other
3087 words, to help facilitate re-use of streams.
3089 This code is very similar to the same_remote_mailboxes code below, which
3090 is used in pine_mail_open. That code compares two mailbox names. One is
3091 usually from the config file and the other is either from the config file
3092 or is typed in. Here and in same_stream_and_mailbox below, we're comparing
3093 an open stream to a name instead of two names. We could conceivably use
3094 same_remote_mailboxes to compare stream->mailbox to name, but it isn't
3095 exactly the same and the differences may be important. Some stuff that
3096 happens here seems wrong, but it isn't easy to fix.
3097 Having !mb_n.port count as a match to any mb_s.port isn't right. It should
3098 only match if mb_s.port is equal to the default, but the default isn't
3099 something that is available to us. The same thing is done in c-client in
3100 the mail_usable_network_stream() routine, and it isn't right there, either.
3101 The semantics of a missing user are also suspect, because just like with
3102 port, a default is used.
3103 ----*/
3104 MAILSTREAM *
3105 same_stream(char *name, MAILSTREAM *stream)
3107 NETMBX mb_s, mb_n, mb_o;
3109 if(stream && stream->mailbox && *stream->mailbox && name && *name
3110 && !(sp_dead_stream(stream))
3111 && mail_valid_net_parse(stream->mailbox, &mb_s)
3112 && mail_valid_net_parse(stream->original_mailbox, &mb_o)
3113 && mail_valid_net_parse(name, &mb_n)
3114 && !strucmp(mb_n.service, mb_s.service)
3115 && (!strucmp(mb_n.host, mb_o.host) /* s is already canonical */
3116 || !strucmp(canonical_name(mb_n.host), mb_s.host))
3117 && (!mb_n.port || mb_n.port == mb_s.port)
3118 && mb_n.anoflag == stream->anonymous
3119 && ((mb_n.user && *mb_n.user &&
3120 mb_s.user && !strcmp(mb_n.user, mb_s.user))
3122 ((!mb_n.user || !*mb_n.user)
3123 && mb_s.user
3124 && ((ps_global->VAR_USER_ID
3125 && !strcmp(ps_global->VAR_USER_ID, mb_s.user))
3127 (!ps_global->VAR_USER_ID
3128 && ps_global->ui.login[0]
3129 && !strcmp(ps_global->ui.login, mb_s.user))))
3131 (!((mb_n.user && *mb_n.user) || (mb_s.user && *mb_s.user))
3132 && stream->anonymous))
3133 && (struncmp(mb_n.service, "imap", 4) ? 1 : strcmp(imap_host(stream), ".NO-IMAP-CONNECTION."))){
3134 dprint((7, "same_stream: name->%s == stream->%s: yes\n",
3135 name ? name : "?",
3136 (stream && stream->mailbox) ? stream->mailbox : "NULL"));
3137 return(stream);
3140 dprint((7, "same_stream: name->%s == stream->%s: no dice\n",
3141 name ? name : "?",
3142 (stream && stream->mailbox) ? stream->mailbox : "NULL"));
3143 return(NULL);
3148 /*----------------------------------------------------------------------
3149 See if this stream has the named mailbox selected.
3151 Accepts: mailbox name
3152 candidate stream
3153 Returns: stream if it can be used, else NIL
3154 ----*/
3155 MAILSTREAM *
3156 same_stream_and_mailbox(char *name, MAILSTREAM *stream)
3158 NETMBX mb_s, mb_n;
3160 if(same_stream(name, stream)
3161 && mail_valid_net_parse(stream->mailbox, &mb_s)
3162 && mail_valid_net_parse(name, &mb_n)
3163 && (mb_n.mailbox && mb_s.mailbox
3164 && (!strcmp(mb_n.mailbox,mb_s.mailbox) /* case depend except INBOX */
3165 || (!strucmp(mb_n.mailbox,"INBOX")
3166 && !strucmp(mb_s.mailbox,"INBOX"))))){
3167 dprint((7,
3168 "same_stream_and_mailbox: name->%s == stream->%s: yes\n",
3169 name ? name : "?",
3170 (stream && stream->mailbox) ? stream->mailbox : "NULL"));
3171 return(stream);
3174 dprint((7,
3175 "same_stream_and_mailbox: name->%s == stream->%s: no dice\n",
3176 name ? name : "?",
3177 (stream && stream->mailbox) ? stream->mailbox : "NULL"));
3178 return(NULL);
3182 * Args -- name1 and name2 are remote mailbox names.
3184 * Returns -- True if names refer to same mailbox accessed in same way
3185 * False if not
3187 * This has some very similar code to same_stream_and_mailbox but we're not
3188 * quite ready to discard the differences.
3189 * The treatment of the port and the user is not quite the same.
3192 same_remote_mailboxes(char *name1, char *name2)
3194 NETMBX mb1, mb2;
3195 char *cn1;
3198 * Probably we should allow !port equal to default port, but we don't
3199 * know how to get the default port. To match what c-client does we
3200 * allow !port to be equal to anything.
3202 return(name1 && IS_REMOTE(name1)
3203 && name2 && IS_REMOTE(name2)
3204 && mail_valid_net_parse(name1, &mb1)
3205 && mail_valid_net_parse(name2, &mb2)
3206 && !strucmp(mb1.service, mb2.service)
3207 && (!strucmp(mb1.host, mb2.host) /* just to save DNS lookups */
3208 || !strucmp(cn1=canonical_name(mb1.host), mb2.host)
3209 || !strucmp(cn1, canonical_name(mb2.host)))
3210 && (!mb1.port || !mb2.port || mb1.port == mb2.port)
3211 && mb1.anoflag == mb2.anoflag
3212 && mb1.mailbox && mb2.mailbox
3213 && (!strcmp(mb1.mailbox, mb2.mailbox)
3214 || (!strucmp(mb1.mailbox,"INBOX")
3215 && !strucmp(mb2.mailbox,"INBOX")))
3216 && ((mb1.user && *mb1.user && mb2.user && *mb2.user
3217 && !strcmp(mb1.user, mb2.user))
3219 (!(mb1.user && *mb1.user) && !(mb2.user && *mb2.user))
3221 (!(mb1.user && *mb1.user)
3222 && ((ps_global->VAR_USER_ID
3223 && !strcmp(ps_global->VAR_USER_ID, mb2.user))
3225 (!ps_global->VAR_USER_ID
3226 && ps_global->ui.login[0]
3227 && !strcmp(ps_global->ui.login, mb2.user))))
3229 (!(mb2.user && *mb2.user)
3230 && ((ps_global->VAR_USER_ID
3231 && !strcmp(ps_global->VAR_USER_ID, mb1.user))
3233 (!ps_global->VAR_USER_ID
3234 && ps_global->ui.login[0]
3235 && !strcmp(ps_global->ui.login, mb1.user))))));
3240 is_imap_stream(MAILSTREAM *stream)
3242 return(stream && stream->dtb && stream->dtb->name
3243 && !strcmp(stream->dtb->name, "imap"));
3248 modern_imap_stream(MAILSTREAM *stream)
3250 return(is_imap_stream(stream) && LEVELIMAP4rev1(stream));
3254 /*----------------------------------------------------------------------
3255 Check and see if all the stream are alive
3257 Returns: 0 if there was no change
3258 >0 if streams have died since last call
3260 Also outputs a message that the streams have died
3261 ----*/
3263 streams_died(void)
3265 int rv = 0;
3266 int i;
3267 MAILSTREAM *m;
3268 unsigned char *folder;
3270 for(i = 0; i < ps_global->s_pool.nstream; i++){
3271 m = ps_global->s_pool.streams[i];
3272 if(m && sp_dead_stream(m)){
3273 if(sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)){
3274 if(!sp_noticed_dead_stream(m)){
3275 rv++;
3276 sp_set_noticed_dead_stream(m, 1);
3277 folder = folder_name_decoded((unsigned char *)STREAMNAME(m));
3278 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3279 _("MAIL FOLDER \"%s\" CLOSED DUE TO ACCESS ERROR"),
3280 short_str(pretty_fn((char *) folder) ? pretty_fn((char *) folder) : "?",
3281 tmp_20k_buf+1000, SIZEOF_20KBUF-1000, 35, FrontDots));
3282 dprint((6, "streams_died: locked: \"%s\"\n",
3283 folder));
3284 if(rv == 1){
3285 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Folder \"%s\" is Closed"),
3286 short_str(pretty_fn((char *)folder) ? pretty_fn((char *)folder) : "?",
3287 tmp_20k_buf+1000, SIZEOF_20KBUF-1000, 35, FrontDots));
3288 if(pith_opt_icon_text)
3289 (*pith_opt_icon_text)(tmp_20k_buf, IT_MCLOSED);
3291 if(folder) fs_give((void **)&folder);
3294 else{
3295 if(!sp_noticed_dead_stream(m)){
3296 sp_set_noticed_dead_stream(m, 1);
3297 folder = (unsigned char *) STREAMNAME(m);
3299 * If a cached stream died and then we tried to use it
3300 * it could cause problems. We could warn about it here
3301 * but it may be confusing because it might be
3302 * unrelated to what the user is doing and not cause
3303 * any problem at all.
3305 #if 0
3306 if(sp_flagged(m, SP_USEPOOL))
3307 q_status_message(SM_ORDER, 3, 3,
3308 "Warning: Possible problem accessing remote data, connection died.");
3309 #endif
3311 dprint((6, "streams_died: not locked: \"%s\"\n",
3312 folder));
3315 pine_mail_actually_close(m);
3320 return(rv);
3324 /* Some stream is locked checks to see if there is any stream for which we
3325 * are in a callback from c-client
3329 some_stream_is_locked(void)
3331 int rv = 0, i;
3332 MAILSTREAM *m;
3334 for(i = 0; rv == 0 && i < ps_global->s_pool.nstream; i++){
3335 m = ps_global->s_pool.streams[i];
3336 if(m && m->lock)
3337 rv++;
3340 return(rv);
3344 * Very simple version of appenduid_cb until we need something
3345 * more complex.
3348 static imapuid_t last_append_uid;
3350 void
3351 appenduid_cb(char *mailbox,unsigned long uidvalidity, SEARCHSET *set)
3353 last_append_uid = set ? set->first : 0L;
3357 imapuid_t
3358 get_last_append_uid(void)
3360 return last_append_uid;
3365 * mail_cmd_stream - return a stream suitable for mail_lsub,
3366 * mail_subscribe, and mail_unsubscribe
3369 MAILSTREAM *
3370 mail_cmd_stream(CONTEXT_S *context, int *closeit)
3372 char tmp[MAILTMPLEN];
3374 *closeit = 1;
3375 (void) context_apply(tmp, context, "x", sizeof(tmp));
3377 return(pine_mail_open(NULL, tmp,
3378 OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE,
3379 NULL));
3384 * This is so we can replace the old rfc822_ routines like rfc822_header_line
3385 * with the new version that checks bounds, like rfc822_output_header_line.
3386 * This routine is called when would be a bounds overflow, which we simply log
3387 * and go on with the truncated data.
3389 long
3390 dummy_soutr(void *stream, char *string)
3392 dprint((2, "dummy_soutr unexpected call, caught overflow\n"));
3393 return LONGT;