1
#if !defined(lint) && !defined(DOS)
2
static char rcsid[] = "$Id: stream.c 394 2007-01-25 20:29:45Z hubert@u.washington.edu $";
6
* ========================================================================
7
* Copyright 2006 University of Washington
9
* Licensed under the Apache License, Version 2.0 (the "License");
10
* you may not use this file except in compliance with the License.
11
* You may obtain a copy of the License at
13
* http://www.apache.org/licenses/LICENSE-2.0
15
* ========================================================================
18
/*======================================================================
20
Implements the Pine mail stream management routines
21
and c-client wrapper functions
25
#include "../pith/headers.h"
26
#include "../pith/stream.h"
27
#include "../pith/state.h"
28
#include "../pith/conf.h"
29
#include "../pith/flag.h"
30
#include "../pith/msgno.h"
31
#include "../pith/adrbklib.h"
32
#include "../pith/status.h"
33
#include "../pith/newmail.h"
34
#include "../pith/detach.h"
35
#include "../pith/folder.h"
36
#include "../pith/mailcmd.h"
37
#include "../pith/util.h"
38
#include "../pith/news.h"
39
#include "../pith/sequence.h"
40
#include "../pith/options.h"
45
void reset_stream_view_state(MAILSTREAM *);
46
void carefully_reset_sp_flags(MAILSTREAM *, unsigned long);
47
char *partial_text_gets(readfn_t, void *, unsigned long, GETS_DATA *);
48
void mail_list_internal(MAILSTREAM *, char *, char *);
49
int recent_activity(MAILSTREAM *);
50
int sp_nusepool_notperm(void);
51
int sp_add(MAILSTREAM *, int);
52
void sp_delete(MAILSTREAM *);
53
void sp_free(PER_STREAM_S **);
56
static FETCH_READC_S *g_pft_desc;
59
MAILSTATUS *pine_cached_status; /* implement status for #move folder */
64
* Pine wrapper around mail_open. If we have the PREFER_ALT_AUTH flag turned
65
* on, we need to set the TRYALT flag before trying the open.
66
* This routine manages the stream pool, too. It tries to re-use existing
67
* streams instead of opening new ones, or maybe it will leave one open and
68
* use a new one if that seems to make more sense. Pine_mail_close leaves
69
* streams open so that they may be re-used. Each pine_mail_open should have
70
* a matching pine_mail_close (or possible pine_mail_actually_close) somewhere
74
* stream -- A possible stream for recycling. This isn't usually the
75
* way recycling happens. Usually it is automatic.
76
* mailbox -- The mailbox to be opened.
77
* openflags -- Flags passed here to modify the behavior.
78
* retflags -- Flags returned from here. SP_MATCH will be lit if that is
79
* what happened. If SP_MATCH is lit then SP_LOCKED may also
80
* be lit if the matched stream was already locked when
84
pine_mail_open(MAILSTREAM *stream, char *mailbox, long int openflags, long int *retflags)
86
MAILSTREAM *retstream = NULL;
88
int permlocked = 0, is_inbox = 0, usepool = 0, tempuse = 0, uf = 0;
91
static unsigned long streamcounter = 0;
94
"pine_mail_open: opening \"%s\"%s openflags=0x%x %s%s%s%s%s%s%s%s%s (%s)\n",
95
mailbox ? mailbox : "(NULL)",
96
stream ? "" : " (stream was NULL)",
98
openflags & OP_HALFOPEN ? " OP_HALFOPEN" : "",
99
openflags & OP_READONLY ? " OP_READONLY" : "",
100
openflags & OP_SILENT ? " OP_SILENT" : "",
101
openflags & OP_DEBUG ? " OP_DEBUG" : "",
102
openflags & SP_PERMLOCKED ? " SP_PERMLOCKED" : "",
103
openflags & SP_INBOX ? " SP_INBOX" : "",
104
openflags & SP_USERFLDR ? " SP_USERFLDR" : "",
105
openflags & SP_USEPOOL ? " SP_USEPOOL" : "",
106
openflags & SP_TEMPUSE ? " SP_TEMPUSE" : "",
107
debug_time(1, ps_global->debug_timestamp)));
112
if(ps_global->user_says_cancel){
113
dprint((7, "pine_mail_open: cancelled by user\n"));
117
is_inbox = openflags & SP_INBOX;
118
uf = openflags & SP_USERFLDR;
120
/* inbox is still special, assume that we want to permlock it */
121
permlocked = (is_inbox || openflags & SP_PERMLOCKED) ? 1 : 0;
123
/* check to see if user wants this folder permlocked */
124
for(lock_these = ps_global->VAR_PERMLOCKED;
125
uf && !permlocked && lock_these && *lock_these; lock_these++){
126
char *p = NULL, *dummy = NULL, *lt, *lname, *mname;
127
char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN];
129
/* there isn't really a pair, it just dequotes for us */
130
get_pair(*lock_these, &dummy, &p, 0, 0);
133
* Check to see if this is an incoming nickname and replace it
134
* with the full name.
136
if(!(p && ps_global->context_list
137
&& ps_global->context_list->use & CNTXT_INCMNG
138
&& (lt=folder_is_nick(p, FOLDERS(ps_global->context_list), 0))))
142
fs_give((void **) &dummy);
145
&& (same_remote_mailboxes(mailbox, lt)
148
&& (lname=mailboxfile(tmp1, lt))
149
&& (mname=mailboxfile(tmp2, mailbox))
150
&& !strcmp(lname, mname))))
154
fs_give((void **) &p);
158
* Only cache if remote, not nntp, not pop, and caller asked us to.
159
* It might make sense to do some caching for nntp and pop, as well, but
160
* we aren't doing it right now. For example, an nntp stream open to
161
* one group could be reused for another group. An open pop stream could
162
* be used for mail_copy_full.
164
* An implication of doing only imap here is that sp_stream_get will only
165
* be concerned with imap streams.
167
if((d = mail_valid (NIL, mailbox, (char *) NIL))
168
&& !strcmp(d->name, "imap")){
169
usepool = openflags & SP_USEPOOL;
170
tempuse = openflags & SP_TEMPUSE;
173
if(IS_REMOTE(mailbox)){
174
dprint((9, "pine_mail_open: not cacheable: %s\n", !d ? "no driver?" : d->name ? d->name : "?" ));
177
if(permlocked || (openflags & OP_READONLY)){
179
* This is a strange case. We want to allow stay-open local
180
* folders, but they don't fit into the rest of the framework
181
* well. So we'll look for it being already open in this case
182
* and special-case it (the already_open_stream() case
186
"pine_mail_open: not cacheable: not remote, but check for local stream\n"));
190
"pine_mail_open: not cacheable: not remote\n"));
195
/* If driver doesn't support halfopen, just open it. */
196
if(d && (openflags & OP_HALFOPEN) && !(d->flags & DR_HALFOPEN)){
197
openflags &= ~OP_HALFOPEN;
199
"pine_mail_open: turning off OP_HALFOPEN flag\n"));
203
* Some of the flags are pine's, the rest are meant for mail_open.
204
* We've noted the pine flags, now remove them before we call mail_open.
206
openflags &= ~(SP_USEPOOL | SP_TEMPUSE | SP_INBOX
207
| SP_PERMLOCKED | SP_USERFLDR);
210
if(ps_global->debug_imap > 3 || ps_global->debugmem)
211
openflags |= OP_DEBUG;
214
if(F_ON(F_PREFER_ALT_AUTH, ps_global)){
215
if((d = mail_valid (NIL, mailbox, (char *) NIL))
216
&& !strcmp(d->name, "imap"))
217
openflags |= OP_TRYALT;
220
if(F_ON(F_ENABLE_MULNEWSRCS, ps_global)){
221
char source[MAILTMPLEN], *target = NULL;
222
if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
224
if((d = mail_valid(NIL, source, (char *) NIL))
225
&& (!strcmp(d->name, "news")
226
|| !strcmp(d->name, "nntp")))
227
openflags |= OP_MULNEWSRC;
229
else if((d = mail_valid(NIL, mailbox, (char *) NIL))
230
&& !strcmp(d->name, "nntp"))
231
openflags |= OP_MULNEWSRC;
235
* One of the problems is that the new-style stream caching (the
236
* sp_stream_get stuff) may conflict with some of the old-style caching
237
* (the passed in argument stream) that is still in the code. We should
238
* probably eliminate the old-style caching, but some of it is still useful,
239
* especially if it deals with something other than IMAP. We want to prevent
240
* mistakes caused by conflicts between the two styles. In particular, we
241
* don't want to have a new-style cached stream re-opened because of the
242
* old-style caching code. This can happen if a stream is passed in that
243
* is not useable, and then a new stream is opened because the passed in
244
* stream causes us to bypass the new caching code. Play it safe. If it
245
* is an IMAP stream, just close it. This should leave it in the new-style
246
* cache anyway, causing no loss. Maybe not if the cache wasn't large
247
* enough to have it in there in the first place, in which case we get
248
* a possibly unnecessary close and open. If it isn't IMAP we still have
249
* to worry about it because it will cause us to bypass the caching code.
250
* So if the stream isn't IMAP but the mailbox we're opening is, close it.
251
* The immediate alternative would be to try to emulate the code in
252
* mail_open that checks whether it is re-usable or not, but that is
253
* dangerous if that code changes on us.
256
if(is_imap_stream(stream)
257
|| ((d = mail_valid (NIL, mailbox, (char *) NIL))
258
&& !strcmp(d->name, "imap"))){
259
if(is_imap_stream(stream)){
261
"pine_mail_open: closing passed in IMAP stream %s\n",
262
stream->mailbox ? stream->mailbox : "?"));
266
"pine_mail_open: closing passed in non-IMAP stream %s\n",
267
stream->mailbox ? stream->mailbox : "?"));
270
pine_mail_close(stream);
275
if((usepool && !stream && permlocked)
276
|| (!usepool && (permlocked || (openflags & OP_READONLY))
277
&& (retstream = already_open_stream(mailbox, AOS_NONE)))){
281
stream = sp_stream_get(mailbox,
282
SP_MATCH | ((openflags & OP_READONLY) ? SP_RO_OK : 0));
285
| (usepool ? SP_USEPOOL : 0)
286
| (permlocked ? SP_PERMLOCKED : 0)
287
| (is_inbox ? SP_INBOX : 0)
288
| (uf ? SP_USERFLDR : 0)
289
| (tempuse ? SP_TEMPUSE : 0);
292
* If the stream wasn't already locked, then we reset it so it
293
* looks like we are reopening it. We have to worry about recent
294
* messages since they will still be recent, if that affects us.
296
if(!(sp_flags(stream) & SP_LOCKED))
297
reset_stream_view_state(stream);
300
*retflags |= SP_MATCH;
301
if(sp_flags(stream) & SP_LOCKED)
302
*retflags |= SP_LOCKED;
305
if(sp_flags(stream) & SP_LOCKED
306
&& sp_flags(stream) & SP_USERFLDR
307
&& !(flags & SP_USERFLDR)){
308
sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
310
"pine_mail_open: permlocked: ref cnt up to %d\n",
311
sp_ref_cnt(stream)));
313
else if(sp_ref_cnt(stream) <= 0){
314
sp_set_ref_cnt(stream, 1);
316
"pine_mail_open: permexact: ref cnt set to %d\n",
317
sp_ref_cnt(stream)));
320
carefully_reset_sp_flags(stream, flags);
322
stream->silent = (openflags & OP_SILENT) ? T : NIL;
324
dprint((9, "pine_mail_open: stream was already open\n"));
325
if(stream && stream->dtb && stream->dtb->name
326
&& !strcmp(stream->dtb->name, "imap")){
327
dprint((7, "pine_mail_open: next TAG %08lx\n",
335
if(usepool && !stream){
337
* First, we look for an exact match, a stream which is already
338
* open to the mailbox we are trying to re-open, and we use that.
339
* Skip permlocked only because we did it above already.
342
stream = sp_stream_get(mailbox,
343
SP_MATCH | ((openflags & OP_READONLY) ? SP_RO_OK : 0));
347
| (usepool ? SP_USEPOOL : 0)
348
| (permlocked ? SP_PERMLOCKED : 0)
349
| (is_inbox ? SP_INBOX : 0)
350
| (uf ? SP_USERFLDR : 0)
351
| (tempuse ? SP_TEMPUSE : 0);
354
* If the stream wasn't already locked, then we reset it so it
355
* looks like we are reopening it. We have to worry about recent
356
* messages since they will still be recent, if that affects us.
358
if(!(sp_flags(stream) & SP_LOCKED))
359
reset_stream_view_state(stream);
362
*retflags |= SP_MATCH;
363
if(sp_flags(stream) & SP_LOCKED)
364
*retflags |= SP_LOCKED;
367
if(sp_flags(stream) & SP_LOCKED
368
&& sp_flags(stream) & SP_USERFLDR
369
&& !(flags & SP_USERFLDR)){
370
sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
372
"pine_mail_open: matched: ref cnt up to %d\n",
373
sp_ref_cnt(stream)));
375
else if(sp_ref_cnt(stream) <= 0){
376
sp_set_ref_cnt(stream, 1);
378
"pine_mail_open: exact: ref cnt set to %d\n",
379
sp_ref_cnt(stream)));
382
carefully_reset_sp_flags(stream, flags);
385
* We may be re-using a stream that was previously open
386
* with OP_SILENT and now we don't want OP_SILENT, or vice
387
* versa, I suppose. Fix it.
389
* WARNING: we're messing with c-client internals (by necessity).
391
stream->silent = (openflags & OP_SILENT) ? T : NIL;
393
dprint((9, "pine_mail_open: stream already open\n"));
394
if(stream && stream->dtb && stream->dtb->name
395
&& !strcmp(stream->dtb->name, "imap")){
396
dprint((7, "pine_mail_open: next TAG %08lx\n",
404
* No exact match, look for a stream which is open to the same
405
* server and marked for TEMPUSE.
407
stream = sp_stream_get(mailbox, SP_SAME | SP_TEMPUSE);
410
"pine_mail_open: attempting to re-use TEMP stream\n"));
413
* No SAME/TEMPUSE stream so we look to see if there is an
414
* open slot available (we're not yet at max_remstream). If there
415
* is an open slot, we'll just open a new stream and put it there.
416
* If not, we'll go inside this conditional.
419
&& sp_nusepool_notperm() >= ps_global->s_pool.max_remstream){
421
"pine_mail_open: no empty slots\n"));
423
* No empty remote slots available. See if there is a
424
* TEMPUSE stream that is open but to the wrong server.
426
stream = sp_stream_get(mailbox, SP_TEMPUSE);
429
* We will close this stream and use the empty slot
433
"pine_mail_open: close a TEMPUSE stream and re-use that slot\n"));
434
pine_mail_actually_close(stream);
440
* Still no luck. Look for a stream open to the same
441
* server that is just not locked. This would be a
442
* stream that might be reusable in the future, but we
443
* need it now instead.
445
stream = sp_stream_get(mailbox, SP_SAME | SP_UNLOCKED);
448
"pine_mail_open: attempting to re-use stream\n"));
452
* We'll take any UNLOCKED stream and re-use it.
454
stream = sp_stream_get(mailbox, 0);
457
* We will close this stream and use the empty slot
461
"pine_mail_open: close an UNLOCKED stream and re-use the slot\n"));
462
pine_mail_actually_close(stream);
466
if(ps_global->s_pool.max_remstream){
467
dprint((9, "pine_mail_open: all USEPOOL slots full of LOCKED streams, nothing to use\n"));
470
dprint((9, "pine_mail_open: no caching, max_remstream == 0\n"));
478
"pine_mail_open5: Can't mark folder Stay-Open: at max-remote limit\n"));
479
q_status_message1(SM_ORDER, 3, 5,
480
"Can't mark folder Stay-Open: reached max-remote limit (%s)",
481
comatose((long) ps_global->s_pool.max_remstream));
489
"pine_mail_open: there is an empty slot to use\n"));
493
* We'll make an assumption here. If we were asked to halfopen a
494
* stream then we'll assume that the caller doesn't really care if
495
* the stream is halfopen or if it happens to be open to some mailbox
496
* already. They are just saying halfopen because they don't need to
497
* SELECT a mailbox. That's the assumption, anyway.
499
if(openflags & OP_HALFOPEN && stream){
501
"pine_mail_open: asked for HALFOPEN so returning stream as is\n"));
502
sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
504
"pine_mail_open: halfopen: ref cnt up to %d\n",
505
sp_ref_cnt(stream)));
506
if(stream && stream->dtb && stream->dtb->name
507
&& !strcmp(stream->dtb->name, "imap")){
508
dprint((7, "pine_mail_open: next TAG %08lx\n",
512
stream->silent = (openflags & OP_SILENT) ? T : NIL;
518
* We're going to SELECT another folder with this open stream.
522
* We will have just pinged the stream to make sure it is
523
* still alive. That ping may have discovered some new mail.
524
* Before unselecting the folder, we should process the filters
527
if(!sp_flagged(stream, SP_LOCKED)
528
&& !sp_flagged(stream, SP_USERFLDR)
529
&& sp_new_mail_count(stream))
530
process_filter_patterns(stream, sp_msgmap(stream),
531
sp_new_mail_count(stream));
533
if(stream && stream->dtb && stream->dtb->name
534
&& !strcmp(stream->dtb->name, "imap")){
536
"pine_mail_open: cancel idle timer: TAG %08lx (%s)\n",
537
stream->gensym, debug_time(1, ps_global->debug_timestamp)));
541
* We need to reset the counters and everything.
542
* The easiest way to do that is just to delete all of the
543
* sp_s data and let the open fill it in correctly for
546
sp_free((PER_STREAM_S **) &stream->sparep);
551
* When we pass a stream to mail_open, it will either re-use it or
554
retstream = mail_open(stream, mailbox, openflags);
558
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));
561
* So it is easier to figure out which command goes with which
562
* stream when debugging, change the tag associated with each stream.
563
* Of course, this will come after the SELECT, so the startup IMAP
564
* commands will have confusing tags.
566
if(retstream != stream && retstream->dtb && retstream->dtb->name
567
&& !strcmp(retstream->dtb->name, "imap")){
568
retstream->gensym += (streamcounter * 0x1000000);
569
streamcounter = (streamcounter + 1) % (8 * 16);
572
if(retstream && retstream->dtb && retstream->dtb->name
573
&& !strcmp(retstream->dtb->name, "imap")){
574
dprint((7, "pine_mail_open: next TAG %08lx\n",
579
* Catch the case where our test up above (where usepool was set)
580
* did not notice that this was news, but that we can tell once
581
* we've opened the stream. (One such case would be an imap proxy
582
* to an nntp server.) Remove it from being cached here. There was
583
* a possible penalty for not noticing sooner. If all the usepool
584
* slots were full, we will have closed one of the UNLOCKED streams
585
* above, preventing us from future re-use of that stream.
586
* We could figure out how to do the test better with just the
587
* name. We could open the stream and then close the other one
588
* after the fact. Or we could just not worry about it since it is
591
if(IS_NEWS(retstream)){
596
/* make sure the message map has been instantiated */
597
(void) sp_msgmap(retstream);
600
| (usepool ? SP_USEPOOL : 0)
601
| (permlocked ? SP_PERMLOCKED : 0)
602
| (is_inbox ? SP_INBOX : 0)
603
| (uf ? SP_USERFLDR : 0)
604
| (tempuse ? SP_TEMPUSE : 0);
606
sp_flag(retstream, flags);
607
sp_set_recent_since_visited(retstream, retstream->recent);
609
/* initialize the reference count */
610
sp_set_ref_cnt(retstream, 1);
611
dprint((7, "pine_mail_open: reset: ref cnt set to %d\n",
612
sp_ref_cnt(retstream)));
614
if(sp_add(retstream, usepool) != 0 && usepool){
618
| (usepool ? SP_USEPOOL : 0)
619
| (permlocked ? SP_PERMLOCKED : 0)
620
| (is_inbox ? SP_INBOX : 0)
621
| (uf ? SP_USERFLDR : 0)
622
| (tempuse ? SP_TEMPUSE : 0);
624
sp_flag(retstream, flags);
625
(void) sp_add(retstream, usepool);
630
* When opening a newsgroup, c-client marks the messages up to the
631
* last Deleted as Unseen. If the feature news-approximates-new-status
632
* is on, we'd rather they be treated as Seen. That way, selecting New
633
* messages will give us the ones past the last Deleted. So we're going
634
* to change them to Seen. Since Seen is a session flag for news, making
635
* this change won't have any permanent effect. C-client also marks the
636
* messages after the last deleted Recent, which is the bit of
637
* information we'll use to find the messages we want to change.
639
if(F_ON(F_FAKE_NEW_IN_NEWS, ps_global) &&
640
retstream->nmsgs > 0 && IS_NEWS(retstream)){
642
long i, mflags = ST_SET;
646
* Search for !recent messages to set the searched bit for
647
* those messages we want to change. Then we'll flip the bits.
649
(void)count_flagged(retstream, F_UNRECENT);
651
for(i = 1L; i <= retstream->nmsgs; i++)
652
if((mc = mail_elt(retstream,i)) && mc->searched)
657
if(!is_imap_stream(retstream))
659
if((seq = build_sequence(retstream, NULL, NULL)) != NULL){
660
mail_flag(retstream, seq, "\\SEEN", mflags);
661
fs_give((void **)&seq);
671
reset_stream_view_state(MAILSTREAM *stream)
678
mm = sp_msgmap(stream);
683
sp_set_viewing_a_thread(stream, 0);
684
sp_set_need_to_rethread(stream, 0);
686
mn_reset_cur(mm, stream->nmsgs > 0L ? stream->nmsgs : 0L); /* default */
688
mm->visible_threads = -1L;
691
mm->top_after_thrd = 0L;
693
mn_set_mansort(mm, 0);
696
* Get rid of zooming and selections, but leave filtering flags. All the
697
* flag counts and everything should still be correct because set_lflag
698
* preserves them correctly.
700
if(any_lflagged(mm, MN_SLCT | MN_HIDE)){
703
for(i = 1L; i <= mn_get_total(mm); i++)
704
set_lflag(stream, mm, i, MN_SLCT | MN_HIDE, 0);
708
* We could try to set up a default sort order, but the caller is going
709
* to re-sort anyway if they are interested in sorting. So we won't do
716
* We have to be careful when we change the flags of an already
717
* open stream, because there may be more than one section of
718
* code actively using the stream.
719
* We allow turning on (but not off) SP_LOCKED
723
* We allow turning off (but not on) SP_TEMPUSE
726
carefully_reset_sp_flags(MAILSTREAM *stream, long unsigned int flags)
728
if(sp_flags(stream) != flags){
729
/* allow turning on but not off */
730
if(sp_flags(stream) & SP_LOCKED && !(flags & SP_LOCKED))
733
if(sp_flags(stream) & SP_PERMLOCKED && !(flags & SP_PERMLOCKED))
734
flags |= SP_PERMLOCKED;
736
if(sp_flags(stream) & SP_USERFLDR && !(flags & SP_USERFLDR))
737
flags |= SP_USERFLDR;
739
if(sp_flags(stream) & SP_INBOX && !(flags & SP_INBOX))
743
/* allow turning off but not on */
744
if(!(sp_flags(stream) & SP_TEMPUSE) && flags & SP_TEMPUSE)
745
flags &= ~SP_TEMPUSE;
748
/* leave the way they already are */
749
if((sp_flags(stream) & SP_FILTERED) != (flags & SP_FILTERED))
750
flags = (flags & ~SP_FILTERED) | (sp_flags(stream) & SP_FILTERED);
753
if(sp_flags(stream) != flags)
754
sp_flag(stream, flags);
760
* Pine wrapper around mail_create. If we have the PREFER_ALT_AUTH flag turned
761
* on we don't want to pass a NULL stream to c-client because it will open
762
* a non-ssl connection when we want it to be ssl.
765
pine_mail_create(MAILSTREAM *stream, char *mailbox)
767
MAILSTREAM *ourstream = NULL;
769
long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
770
char source[MAILTMPLEN], *target = NULL;
773
dprint((7, "pine_mail_create: creating \"%s\"%s\n",
774
mailbox ? mailbox : "(NULL)",
775
stream ? "" : " (stream was NULL)"));
777
if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
780
"pine_mail_create: #move special case, creating \"%s\"\n",
781
mailbox ? mailbox : "(NULL)"));
785
* We don't really need this anymore, since we are now using IMAPTRYALT.
786
* We'll leave it since it works.
788
if((F_ON(F_PREFER_ALT_AUTH, ps_global)
789
|| (ps_global->debug_imap > 3 || ps_global->debugmem))){
791
if((d = mail_valid (NIL, mailbox, (char *) NIL))
792
&& !strcmp(d->name, "imap")){
794
if(F_ON(F_PREFER_ALT_AUTH, ps_global))
795
openflags |= OP_TRYALT;
800
stream = sp_stream_get(mailbox, SP_MATCH);
802
stream = sp_stream_get(mailbox, SP_SAME);
806
* It is only useful to open a stream in the imap case.
808
if((d = mail_valid (NIL, mailbox, (char *) NIL))
809
&& !strcmp(d->name, "imap")){
811
stream = pine_mail_open(NULL, mailbox, openflags, NULL);
816
return_val = mail_create(stream, mailbox);
819
pine_mail_close(ourstream);
826
* Pine wrapper around mail_delete.
829
pine_mail_delete(MAILSTREAM *stream, char *mailbox)
831
MAILSTREAM *ourstream = NULL;
833
long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
834
char source[MAILTMPLEN], *target = NULL;
837
dprint((7, "pine_mail_delete: deleting \"%s\"%s\n",
838
mailbox ? mailbox : "(NULL)",
839
stream ? "" : " (stream was NULL)"));
841
if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
844
"pine_mail_delete: #move special case, deleting \"%s\"\n",
845
mailbox ? mailbox : "(NULL)"));
849
* We don't really need this anymore, since we are now using IMAPTRYALT.
851
if((F_ON(F_PREFER_ALT_AUTH, ps_global)
852
|| (ps_global->debug_imap > 3 || ps_global->debugmem))){
854
if((d = mail_valid (NIL, mailbox, (char *) NIL))
855
&& !strcmp(d->name, "imap")){
857
if(F_ON(F_PREFER_ALT_AUTH, ps_global))
858
openflags |= OP_TRYALT;
862
/* oops, we seem to be deleting a selected stream */
863
if(!stream && (stream = sp_stream_get(mailbox, SP_MATCH))){
864
pine_mail_actually_close(stream);
869
stream = sp_stream_get(mailbox, SP_SAME);
873
* It is only useful to open a stream in the imap case.
875
if((d = mail_valid (NIL, mailbox, (char *) NIL))
876
&& !strcmp(d->name, "imap")){
878
stream = pine_mail_open(NULL, mailbox, openflags, NULL);
883
return_val = mail_delete(stream, mailbox);
886
pine_mail_close(ourstream);
893
* Pine wrapper around mail_append.
896
pine_mail_append_full(MAILSTREAM *stream, char *mailbox, char *flags, char *date, STRING *message)
898
MAILSTREAM *ourstream = NULL;
900
long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
901
char source[MAILTMPLEN], *target = NULL;
904
dprint((7, "pine_mail_append_full: appending to \"%s\"%s\n",
905
mailbox ? mailbox : "(NULL)",
906
stream ? "" : " (stream was NULL)"));
908
if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
911
"pine_mail_append_full: #move special case, appending to \"%s\"\n",
912
mailbox ? mailbox : "(NULL)"));
916
* We don't really need this anymore, since we are now using IMAPTRYALT.
918
if((F_ON(F_PREFER_ALT_AUTH, ps_global)
919
|| (ps_global->debug_imap > 3 || ps_global->debugmem))){
921
if((d = mail_valid (NIL, mailbox, (char *) NIL))
922
&& !strcmp(d->name, "imap")){
924
if(F_ON(F_PREFER_ALT_AUTH, ps_global))
925
openflags |= OP_TRYALT;
930
stream = sp_stream_get(mailbox, SP_MATCH);
932
stream = sp_stream_get(mailbox, SP_SAME);
936
* It is only useful to open a stream in the imap case.
938
if((d = mail_valid (NIL, mailbox, (char *) NIL))
939
&& !strcmp(d->name, "imap")){
941
stream = pine_mail_open(NULL, mailbox, openflags, NULL);
946
return_val = mail_append_full(stream, mailbox, flags, date, message);
949
pine_mail_close(ourstream);
956
* Pine wrapper around mail_append.
959
pine_mail_append_multiple(MAILSTREAM *stream, char *mailbox, append_t af,
960
APPENDPACKAGE *data, MAILSTREAM *not_this_stream)
962
MAILSTREAM *ourstream = NULL;
964
long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
965
char source[MAILTMPLEN], *target = NULL;
967
int we_blocked_reuse = 0;
969
dprint((7, "pine_mail_append_multiple: appending to \"%s\"%s\n",
970
mailbox ? mailbox : "(NULL)",
971
stream ? "" : " (stream was NULL)"));
973
if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
976
"pine_mail_append_multiple: #move special case, appending to \"%s\"\n",
977
mailbox ? mailbox : "(NULL)"));
980
if((F_ON(F_PREFER_ALT_AUTH, ps_global)
981
|| (ps_global->debug_imap > 3 || ps_global->debugmem))){
983
if((d = mail_valid (NIL, mailbox, (char *) NIL))
984
&& !strcmp(d->name, "imap")){
986
if(F_ON(F_PREFER_ALT_AUTH, ps_global))
987
openflags |= OP_TRYALT;
992
* We have to be careful re-using streams for multiappend, because of
993
* the way it works. We call into c-client below but part of the call
994
* is data containing a callback function to us to supply the data to
995
* be appended. That function may need to get the data from the server.
996
* If that uses the same stream as we're trying to append on, we're
997
* in trouble. We can't call back into c-client from c-client on the same
998
* stream. (Just think about it, we're in the middle of an APPEND command.
999
* We can't issue a FETCH before the APPEND completes in order to complete
1000
* the APPEND.) We can re-use a stream if it is a different stream from
1001
* the one we are reading from, so that's what the not_this_stream
1002
* stuff is for. If we mark it !SP_USEPOOL, it won't get reused.
1004
if(sp_flagged(not_this_stream, SP_USEPOOL)){
1006
sp_unflag(not_this_stream, SP_USEPOOL);
1010
stream = sp_stream_get(mailbox, SP_MATCH);
1012
stream = sp_stream_get(mailbox, SP_SAME);
1016
* It is only useful to open a stream in the imap case.
1018
if((d = mail_valid (NIL, mailbox, (char *) NIL))
1019
&& !strcmp(d->name, "imap")){
1021
stream = pine_mail_open(NULL, mailbox, openflags, NULL);
1026
if(we_blocked_reuse)
1027
sp_set_flags(not_this_stream, sp_flags(not_this_stream) | SP_USEPOOL);
1029
return_val = mail_append_multiple(stream, mailbox, af, (void *) data);
1032
pine_mail_close(ourstream);
1039
* Pine wrapper around mail_copy.
1042
pine_mail_copy_full(MAILSTREAM *stream, char *sequence, char *mailbox, long int options)
1044
MAILSTREAM *ourstream = NULL;
1046
long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
1047
char source[MAILTMPLEN], *target = NULL;
1050
dprint((7, "pine_mail_copy_full: copying to \"%s\"%s\n",
1051
mailbox ? mailbox : "(NULL)",
1052
stream ? "" : " (stream was NULL)"));
1054
if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
1057
"pine_mail_copy_full: #move special case, copying to \"%s\"\n",
1058
mailbox ? mailbox : "(NULL)"));
1062
* We don't really need this anymore, since we are now using IMAPTRYALT.
1064
if((F_ON(F_PREFER_ALT_AUTH, ps_global)
1065
|| (ps_global->debug_imap > 3 || ps_global->debugmem))){
1067
if((d = mail_valid (NIL, mailbox, (char *) NIL))
1068
&& !strcmp(d->name, "imap")){
1070
if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1071
openflags |= OP_TRYALT;
1076
stream = sp_stream_get(mailbox, SP_MATCH);
1078
stream = sp_stream_get(mailbox, SP_SAME);
1082
* It is only useful to open a stream in the imap case.
1083
* Actually, mail_copy_full is the case where it might also be
1084
* useful to provide a stream in the nntp and pop3 cases. If we
1085
* cache such streams, then we will probably want to open one
1086
* here so that it gets cached.
1088
if((d = mail_valid (NIL, mailbox, (char *) NIL))
1089
&& !strcmp(d->name, "imap")){
1091
stream = pine_mail_open(NULL, mailbox, openflags, NULL);
1096
return_val = mail_copy_full(stream, sequence, mailbox, options);
1099
pine_mail_close(ourstream);
1106
* Pine wrapper around mail_rename.
1109
pine_mail_rename(MAILSTREAM *stream, char *old, char *new)
1111
MAILSTREAM *ourstream = NULL;
1113
long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
1116
dprint((7, "pine_mail_rename(%s,%s)\n", old ? old : "",
1120
* We don't really need this anymore, since we are now using IMAPTRYALT.
1122
if((F_ON(F_PREFER_ALT_AUTH, ps_global)
1123
|| (ps_global->debug_imap > 3 || ps_global->debugmem))){
1125
if((d = mail_valid (NIL, old, (char *) NIL))
1126
&& !strcmp(d->name, "imap")){
1128
if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1129
openflags |= OP_TRYALT;
1133
/* oops, we seem to be renaming a selected stream */
1134
if(!stream && (stream = sp_stream_get(old, SP_MATCH))){
1135
pine_mail_actually_close(stream);
1140
stream = sp_stream_get(old, SP_SAME);
1144
* It is only useful to open a stream in the imap case.
1146
if((d = mail_valid (NIL, old, (char *) NIL))
1147
&& !strcmp(d->name, "imap")){
1149
stream = pine_mail_open(NULL, old, openflags, NULL);
1154
return_val = mail_rename(stream, old, new);
1157
pine_mail_close(ourstream);
1163
/*----------------------------------------------------------------------
1164
Our mail_close wrapper to clean up anything on the mailstream we may have
1165
added to it. mostly in the unused bits of the elt's.
1168
pine_mail_close(MAILSTREAM *stream)
1170
unsigned long uid_last, last_uid;
1176
dprint((7, "pine_mail_close: %s (%s)\n",
1177
stream && stream->mailbox ? stream->mailbox : "(NULL)",
1178
debug_time(1, ps_global->debug_timestamp)));
1180
if(sp_flagged(stream, SP_USEPOOL) && !sp_dead_stream(stream)){
1182
refcnt = sp_ref_cnt(stream);
1183
dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
1186
* Instead of checkpointing here, which takes time that the user
1187
* definitely notices, we checkpoint in new_mail at the next
1188
* opportune time, hopefully when the user is idle.
1191
if(sp_flagged(stream, SP_LOCKED) && sp_flagged(stream, SP_USERFLDR)
1192
&& !stream->halfopen && refcnt <= 1){
1193
if(changes_to_checkpoint(stream))
1194
pine_mail_check(stream);
1197
"pine_mail_close: dont think we need to checkpoint\n"));
1203
* Uid_last is valid when we first open a stream, but not always
1204
* valid after that. So if we know the last uid should be higher
1205
* than uid_last (!#%) use that instead.
1207
uid_last = stream->uid_last;
1208
if(stream->nmsgs > 0L
1209
&& (last_uid=mail_uid(stream,stream->nmsgs)) > uid_last)
1210
uid_last = last_uid;
1212
sp_set_saved_uid_validity(stream, stream->uid_validity);
1213
sp_set_saved_uid_last(stream, uid_last);
1216
* If the reference count is down to 0, unlock it.
1217
* In any case, don't actually do any real closing.
1220
sp_set_ref_cnt(stream, refcnt-1);
1222
refcnt = sp_ref_cnt(stream);
1223
dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
1226
"pine_mail_close: unlocking: start idle timer: TAG %08lx (%s)\n",
1227
stream->gensym, debug_time(1, ps_global->debug_timestamp)));
1228
sp_set_last_use_time(stream, time(0));
1231
* Logically, we ought to be unflagging SP_INBOX, too. However,
1232
* the filtering code uses SP_INBOX when deciding if it should
1233
* filter some things, and we keep filtering after the mailbox
1234
* is closed. So leave SP_INBOX alone. This (the closing of INBOX)
1235
* usually only happens in goodnight_gracey when we're
1236
* shutting everything down.
1238
sp_unflag(stream, SP_LOCKED | SP_PERMLOCKED | SP_USERFLDR);
1241
dprint((7, "pine_mail_close: ref cnt is now %d\n",
1246
dprint((7, "pine_mail_close: %s\n",
1247
sp_flagged(stream, SP_USEPOOL) ? "dead stream" : "no pool"));
1249
refcnt = sp_ref_cnt(stream);
1250
dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
1253
* If the reference count is down to 0, unlock it.
1254
* In any case, don't actually do any real closing.
1257
sp_set_ref_cnt(stream, refcnt-1);
1259
refcnt = sp_ref_cnt(stream);
1261
pine_mail_actually_close(stream);
1264
dprint((7, "pine_mail_close: ref cnt is now %d\n",
1273
pine_mail_actually_close(MAILSTREAM *stream)
1281
if(!sp_closing(stream)){
1282
dprint((7, "pine_mail_actually_close: %s (%s)\n",
1283
stream && stream->mailbox ? stream->mailbox : "(NULL)",
1284
debug_time(1, ps_global->debug_timestamp)));
1286
sp_set_closing(stream, 1);
1288
if(!sp_flagged(stream, SP_LOCKED)
1289
&& !sp_flagged(stream, SP_USERFLDR)
1290
&& !sp_dead_stream(stream)
1291
&& sp_new_mail_count(stream))
1292
process_filter_patterns(stream, sp_msgmap(stream),
1293
sp_new_mail_count(stream));
1297
* let sp_free_callback() free the sp_s stuff and the callbacks to
1298
* free_pine_elt free the per-elt pine stuff.
1306
* If we haven't used a stream for a while, we may want to logout.
1309
maybe_kill_old_stream(MAILSTREAM *stream)
1311
#define KILL_IF_IDLE_TIME (25 * 60)
1313
&& !sp_flagged(stream, SP_LOCKED)
1314
&& !sp_flagged(stream, SP_USERFLDR)
1315
&& time(0) - sp_last_use_time(stream) > KILL_IF_IDLE_TIME){
1318
"killing idle stream: %s (%s): idle timer = %ld secs\n",
1319
stream && stream->mailbox ? stream->mailbox : "(NULL)",
1320
debug_time(1, ps_global->debug_timestamp),
1321
(long) (time(0)-sp_last_use_time(stream))));
1324
* Another thing we could do here instead is to unselect the
1325
* mailbox, leaving the stream open to the server.
1327
pine_mail_actually_close(stream);
1333
* Catch searches that don't need to go to the server.
1334
* (Not anymore, now c-client does this for us.)
1337
pine_mail_search_full(MAILSTREAM *stream, char *charset,
1338
struct search_program *pgm, long int flags)
1340
return(stream ? mail_search_full(stream, charset, pgm, flags) : NIL);
1345
pine_mail_fetch_flags(MAILSTREAM *stream, char *sequence, long int flags)
1347
ps_global->dont_count_flagchanges = 1;
1348
mail_fetch_flags(stream, sequence, flags);
1349
ps_global->dont_count_flagchanges = 0;
1354
pine_mail_fetchenvelope(MAILSTREAM *stream, long unsigned int msgno)
1356
ENVELOPE *env = NULL;
1358
ps_global->dont_count_flagchanges = 1;
1359
if(stream && msgno > 0L && msgno <= stream->nmsgs)
1360
env = mail_fetchenvelope(stream, msgno);
1362
ps_global->dont_count_flagchanges = 0;
1368
pine_mail_fetch_structure(MAILSTREAM *stream, long unsigned int msgno,
1369
struct mail_bodystruct **body, long int flags)
1371
ENVELOPE *env = NULL;
1373
ps_global->dont_count_flagchanges = 1;
1374
if(stream && msgno > 0L && msgno <= stream->nmsgs)
1375
env = mail_fetch_structure(stream, msgno, body, flags);
1377
ps_global->dont_count_flagchanges = 0;
1383
pine_mail_fetchstructure(MAILSTREAM *stream, long unsigned int msgno, struct mail_bodystruct **body)
1385
ENVELOPE *env = NULL;
1387
ps_global->dont_count_flagchanges = 1;
1388
if(stream && msgno > 0L && msgno <= stream->nmsgs)
1389
env = mail_fetchstructure(stream, msgno, body);
1391
ps_global->dont_count_flagchanges = 0;
1397
* Wrapper around mail_fetch_body.
1398
* Currently only used to turn on partial fetching if trying
1399
* to work around the microsoft ssl bug.
1402
pine_mail_fetch_body(MAILSTREAM *stream, long unsigned int msgno, char *section,
1403
long unsigned int *len, long int flags)
1406
if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global))
1407
return(pine_mail_partial_fetch_wrapper(stream, msgno,
1408
section, len, flags, 0, NULL, 0));
1411
return(mail_fetch_body(stream, msgno, section, len, flags));
1415
* Wrapper around mail_fetch_text.
1416
* Currently the only purpose this wrapper serves is to turn
1417
* on partial fetching for quell-ssl-largeblocks.
1420
pine_mail_fetch_text(MAILSTREAM *stream, long unsigned int msgno, char *section,
1421
long unsigned int *len, long int flags)
1424
if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global))
1425
return(pine_mail_partial_fetch_wrapper(stream, msgno,
1426
section, len, flags,
1430
return(mail_fetch_text(stream, msgno, section, len, flags));
1435
* Determine whether to do partial-fetching or not, and do it
1436
* args - same as c-client functions being wrapped around
1437
* get_n_bytes - try to partial fetch for the first n bytes.
1438
* makes no guarantees, might wind up fetching
1439
* the entire text anyway.
1440
* str_to_free - pointer to string to free if we only get
1441
* (non-cachable) partial text (required for
1444
* set, tells us to do mail_fetch_text and mail_partial_text
1445
* unset, tells us to do mail_fetch_body and mail_partial_body
1448
pine_mail_partial_fetch_wrapper(MAILSTREAM *stream, long unsigned int msgno,
1449
char *section, long unsigned int *len,
1450
long int flags, long unsigned int get_n_bytes,
1451
char **str_to_free, int is_text_fetch)
1454
unsigned long size, firstbyte, lastbyte;
1456
FETCH_READC_S *pftc;
1457
char imap_cache_section[MAILTMPLEN];
1460
char *(*fetch_full)(MAILSTREAM *, unsigned long, char *,
1461
unsigned long *, long);
1462
long (*fetch_partial)(MAILSTREAM *, unsigned long, char *,
1463
unsigned long, unsigned long, long);
1465
fetch_full = is_text_fetch ? mail_fetch_text : mail_fetch_body;
1466
fetch_partial = is_text_fetch ? mail_partial_text : mail_partial_body;
1468
*str_to_free = NULL;
1470
if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global) || get_n_bytes){
1473
#endif /* _WINDOWS */
1474
if(section && *section)
1475
body = mail_body(stream, msgno, (unsigned char *) section);
1477
pine_mail_fetch_structure(stream, msgno, &body, flags);
1480
if(body->type != TYPEMULTIPART)
1481
size = body->size.bytes;
1482
else if((!section || !*section) && msgno > 0L
1483
&& stream && msgno <= stream->nmsgs
1484
&& (mc = mail_elt(stream, msgno)))
1485
size = mc->rfc822_size; /* upper bound */
1486
else /* just a guess, can't get actual size */
1487
size = fcc_size_guess(body) + 2048;
1490
* imap_cache, originally intended for c-client internal use,
1491
* takes a section argument that is different from one we
1492
* would pass to mail_body. Typically in this function
1493
* section is NULL, which translates to "TEXT", but in other
1494
* cases we would want to append ".TEXT" to the section
1497
snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s%sTEXT", MAILTMPLEN - 10,
1498
section && *section ? section : "",
1499
section && *section ? "." : "");
1501
snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s", MAILTMPLEN - 10,
1502
section && *section ? section : "");
1504
if(modern_imap_stream(stream)
1506
&& ((size > AVOID_MICROSOFT_SSL_CHUNKING_BUG)
1507
|| (get_n_bytes && size > get_n_bytes))
1509
&& (get_n_bytes && size > get_n_bytes)
1510
#endif /* _WINDOWS */
1511
&& !imap_cache(stream, msgno, imap_cache_section,
1513
if(get_n_bytes == 0){
1515
"fetch_wrapper: doing partial fetching to work around microsoft bug\n"));
1519
"fetch_wrapper: partial fetching due to %lu get_n_bytes\n", get_n_bytes));
1521
pftc = (FETCH_READC_S *)fs_get(sizeof(FETCH_READC_S));
1522
memset(g_pft_desc = pftc, 0, sizeof(FETCH_READC_S));
1524
if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global)){
1526
pftc->chunksize = MIN(get_n_bytes,
1527
AVOID_MICROSOFT_SSL_CHUNKING_BUG);
1529
pftc->chunksize = AVOID_MICROSOFT_SSL_CHUNKING_BUG;
1532
#endif /* _WINDOWS */
1533
pftc->chunksize = get_n_bytes;
1535
pftc->chunk = (char *) fs_get((pftc->chunksize+1)
1537
pftc->cache = so_get(CharStar, NULL, EDIT_ACCESS);
1539
so_truncate(pftc->cache, size + 1);
1540
old_gets = mail_parameters(stream, GET_GETS, (void *)NULL);
1541
mail_parameters(stream, SET_GETS, (void *) partial_text_gets);
1542
/* start fetching */
1544
firstbyte = pftc->read ;
1545
lastbyte = firstbyte + pftc->chunksize;
1546
if(get_n_bytes > firstbyte && get_n_bytes < lastbyte){
1547
pftc->chunksize = get_n_bytes - firstbyte;
1548
lastbyte = get_n_bytes;
1550
(*fetch_partial)(stream, msgno, section, firstbyte,
1551
pftc->chunksize, flags);
1553
if(pftc->read != lastbyte)
1555
} while((pftc->read == lastbyte)
1556
&& (!get_n_bytes || (pftc->read < get_n_bytes)));
1558
"fetch_wrapper: anticipated size=%lu read=%lu\n",
1560
mail_parameters(stream, SET_GETS, old_gets);
1561
new_text.size = pftc->read;
1562
new_text.data = (unsigned char *)so_text(pftc->cache);
1564
if(get_n_bytes && pftc->read == get_n_bytes){
1566
* don't write to cache if we're only dealing with
1570
panic("Programmer botch: partial fetch attempt w/o string pointer");
1572
*str_to_free = (char *) new_text.data;
1575
/* ugh, rewrite string in case it got stomped on last call */
1577
snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s%sTEXT", MAILTMPLEN - 10,
1578
section && *section ? section : "",
1579
section && *section ? "." : "");
1581
snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s", MAILTMPLEN - 10,
1582
section && *section ? section : "");
1584
imap_cache(stream, msgno, imap_cache_section, NULL, &new_text);
1587
pftc->cache->txt = (void *)NULL;
1588
so_give(&pftc->cache);
1589
fs_give((void **)&pftc->chunk);
1590
fs_give((void **)&pftc);
1593
*len = new_text.size;
1594
return ((char *)new_text.data);
1597
return((*fetch_full)(stream, msgno, section, len, flags));
1600
return((*fetch_full)(stream, msgno, section, len, flags));
1604
* c-client callback that handles getting the text
1607
partial_text_gets(readfn_t f, void *stream, long unsigned int size, GETS_DATA *md)
1611
n = MIN(g_pft_desc->chunksize, size);
1612
g_pft_desc->read +=n;
1614
(*f) (stream, n, g_pft_desc->chunk);
1616
if(g_pft_desc->cache)
1617
so_nputs(g_pft_desc->cache, g_pft_desc->chunk, (long) n);
1625
* Pings the stream. Returns 0 if the stream is dead, non-zero otherwise.
1628
pine_mail_ping(MAILSTREAM *stream)
1633
if(!sp_dead_stream(stream)){
1634
ret = mail_ping(stream);
1635
if(ret && sp_dead_stream(stream))
1641
sp_set_last_ping(stream, now);
1642
sp_set_last_expunged_reaper(stream, now);
1650
pine_mail_check(MAILSTREAM *stream)
1652
reset_check_point(stream);
1658
* Unlike mail_list, this version returns a value. The returned value is
1659
* TRUE if the stream is opened ok, and FALSE if we failed opening the
1660
* stream. This allows us to differentiate between a LIST which returns
1661
* no matches and a failure opening the stream. We do this by pre-opening
1662
* the stream ourselves.
1665
pine_mail_list(MAILSTREAM *stream, char *ref, char *pat, unsigned int *options)
1668
char *halfopen_target;
1669
char source[MAILTMPLEN], *target = NULL;
1670
MAILSTREAM *ourstream = NULL;
1672
dprint((7, "pine_mail_list: ref=%s pat=%s%s\n",
1673
ref ? ref : "(NULL)",
1674
pat ? pat : "(NULL)",
1675
stream ? "" : " (stream was NULL)"));
1677
if(!ref && check_for_move_mbox(pat, source, sizeof(source), &target)
1679
check_for_move_mbox(ref, source, sizeof(source), &target)){
1683
*options |= PML_IS_MOVE_MBOX;
1686
"pine_mail_list: #move special case, listing \"%s\"%s\n",
1687
pat ? pat : "(NULL)",
1688
stream ? "" : " (stream was NULL)"));
1691
if(!stream && ((pat && *pat == '{') || (ref && *ref == '{'))){
1693
if(pat && *pat == '{'){
1695
halfopen_target = pat;
1698
halfopen_target = ref;
1702
long flags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
1704
stream = sp_stream_get(halfopen_target, SP_MATCH);
1706
stream = sp_stream_get(halfopen_target, SP_SAME);
1712
* POP is a special case. We don't need to have a stream
1713
* to call mail_list for a POP name. The else part is the
1716
if((d = mail_valid(NIL, halfopen_target, (char *) NIL))
1717
&& (d->flags & DR_HALFOPEN)){
1718
stream = pine_mail_open(NIL, halfopen_target, flags, NULL);
1727
mail_list_internal(stream, ref, pat);
1730
mail_list_internal(stream, ref, pat);
1733
pine_mail_close(ourstream);
1740
* mail_list_internal -- A monument to software religion and those who
1741
* would force it upon us.
1744
mail_list_internal(MAILSTREAM *s, char *r, char *p)
1746
if(F_ON(F_FIX_BROKEN_LIST, ps_global)
1747
&& ((s && s->mailbox && *s->mailbox == '{')
1748
|| (!s && ((r && *r == '{') || (p && *p == '{'))))){
1749
char tmp[2*MAILTMPLEN];
1751
snprintf(tmp, sizeof(tmp), "%.*s%.*s", sizeof(tmp)/2-1, r ? r : "",
1752
sizeof(tmp)/2-1, p);
1753
mail_list(s, "", tmp);
1761
pine_mail_status(MAILSTREAM *stream, char *mailbox, long int flags)
1763
return(pine_mail_status_full(stream, mailbox, flags, NULL, NULL));
1768
pine_mail_status_full(MAILSTREAM *stream, char *mailbox, long int flags,
1769
imapuid_t *uidvalidity, imapuid_t *uidnext)
1771
char source[MAILTMPLEN], *target = NULL;
1773
MAILSTATUS cache, status;
1774
MAILSTREAM *ourstream = NULL;
1776
if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
1779
memset(&status, 0, sizeof(status));
1780
memset(&cache, 0, sizeof(cache));
1782
/* tell mm_status() to write partial return here */
1783
pine_cached_status = &status;
1785
flags |= (SA_UIDVALIDITY | SA_UIDNEXT | SA_MESSAGES);
1787
/* do status of destination */
1789
stream = sp_stream_get(target, SP_SAME);
1791
/* should never be news, don't worry about mulnewrsc flag*/
1792
if((ret = pine_mail_status_full(stream, target, flags, uidvalidity,
1796
/* do status of source */
1797
pine_cached_status = &cache;
1798
stream = sp_stream_get(source, SP_SAME);
1803
if((d = mail_valid (NIL, source, (char *) NIL))
1804
&& !strcmp(d->name, "imap")){
1805
long openflags =OP_HALFOPEN|OP_SILENT|SP_USEPOOL|SP_TEMPUSE;
1807
if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1808
openflags |= OP_TRYALT;
1810
stream = pine_mail_open(NULL, source, openflags, NULL);
1813
else if(F_ON(F_ENABLE_MULNEWSRCS, ps_global)
1814
&& d && (!strucmp(d->name, "news")
1815
|| !strucmp(d->name, "nntp")))
1816
flags |= SA_MULNEWSRC;
1820
if(!ps_global->user_says_cancel && mail_status(stream, source, flags)){
1825
* If the target has recent messages, then we don't come
1826
* through here. We just use the answer from the target.
1828
* If not, then we leave the target results in "status" and
1829
* run a mail_status on the source that puts its results
1830
* in "cache". Combine the results from cache with the
1831
* results that were already in status.
1833
* We count all messages in the source mailbox as recent and
1834
* unseen, even if they are not recent or unseen in the source,
1835
* because they will be recent and unseen in the target
1836
* when we open it. (Not quite true. It is possible that some
1837
* messages from a POP server will end up seen instead
1839
* It is also possible that it is correct. If we add unseen, or
1840
* if we add messages, we could get it wrong. As far as I
1841
* can tell, Pine doesn't ever even use status.unseen, so it
1842
* is currently academic anyway.) Hubert 2003-03-05
1843
* (Does now 2004-06-02 in next_folder.)
1845
* However, we don't want to count all messages as recent if
1846
* the source mailbox is NNTP or NEWS, because we won't be
1847
* deleting those messages from the source.
1848
* We only count recent.
1850
* There are other cases that are trouble. One in particular
1851
* is an IMAP-to-NNTP proxy, where the messages can't be removed
1852
* from the mailbox but they can be deleted. If we count
1853
* messages in the source as being recent and it turns out they
1854
* were all deleted already, then we incorrectly say the folder
1855
* has recent messages when it doesn't. We can recover from that
1856
* case at some cost by actually opening the folder the first
1857
* time if there are not recents, and then checking to see if
1858
* everything is deleted. Subsequently, we store the uid values
1859
* (which are returned by status) so that we can know if the
1860
* mailbox changed or not. The problem being solved is that
1861
* the TAB command indicates new messages in a folder when there
1862
* really aren't any. An alternative would be to use the is_news
1863
* half of the if-else in all cases. A problem with that is
1864
* that there could be non-recent messages sitting in the
1865
* source mailbox that we never discover. Hubert 2003-03-28
1868
if((d = mail_valid (NIL, source, (char *) NIL))
1869
&& (!strcmp(d->name, "nntp") || !strcmp(d->name, "news")))
1872
if(is_news && cache.flags & SA_RECENT){
1873
status.messages += cache.recent;
1874
status.recent += cache.recent;
1875
status.unseen += cache.recent;
1876
status.uidnext += cache.recent;
1879
if(uidvalidity && *uidvalidity
1880
&& uidnext && *uidnext
1881
&& cache.flags & SA_UIDVALIDITY
1882
&& cache.uidvalidity == *uidvalidity
1883
&& cache.flags & SA_UIDNEXT
1884
&& cache.uidnext == *uidnext){
1885
; /* nothing changed in source mailbox */
1887
else if(cache.flags & SA_RECENT && cache.recent){
1888
status.messages += cache.recent;
1889
status.recent += cache.recent;
1890
status.unseen += cache.recent;
1891
status.uidnext += cache.recent;
1893
else if(!(cache.flags & SA_MESSAGES) || cache.messages){
1894
long openflags = OP_SILENT | SP_USEPOOL | SP_TEMPUSE;
1895
long delete_count, not_deleted = 0L;
1897
/* Actually open it up and check */
1898
if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1899
openflags |= OP_TRYALT;
1905
&& !same_stream_and_mailbox(source, ourstream)){
1906
pine_mail_close(ourstream);
1907
ourstream = stream = NULL;
1911
stream = pine_mail_open(stream, source, openflags,
1917
delete_count = count_flagged(stream, F_DEL);
1918
not_deleted = stream->nmsgs - delete_count;
1921
status.messages += not_deleted;
1922
status.recent += not_deleted;
1923
status.unseen += not_deleted;
1924
status.uidnext += not_deleted;
1927
if(uidvalidity && cache.flags & SA_UIDVALIDITY)
1928
*uidvalidity = cache.uidvalidity;
1930
if(uidnext && cache.flags & SA_UIDNEXT)
1931
*uidnext = cache.uidnext;
1937
* Do the regular mm_status callback which we've been intercepting
1938
* into different locations above.
1940
pine_cached_status = NIL;
1942
mm_status(NULL, mailbox, &status);
1948
if((d = mail_valid (NIL, mailbox, (char *) NIL))
1949
&& !strcmp(d->name, "imap")){
1950
long openflags = OP_HALFOPEN|OP_SILENT|SP_USEPOOL|SP_TEMPUSE;
1952
if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1953
openflags |= OP_TRYALT;
1956
* We just use this to find the answer.
1957
* We're asking for trouble if we do a STATUS on a
1958
* selected mailbox. I don't believe this happens in pine.
1959
* It does now (2004-06-02) in next_folder if the
1960
* F_TAB_USES_UNSEEN option is set and the folder was
1963
stream = sp_stream_get(mailbox, SP_MATCH);
1965
memset(&status, 0, sizeof(status));
1966
if(flags & SA_MESSAGES){
1967
status.flags |= SA_MESSAGES;
1968
status.messages = stream->nmsgs;
1971
if(flags & SA_RECENT){
1972
status.flags |= SA_RECENT;
1973
status.recent = stream->recent;
1976
if(flags & SA_UNSEEN){
1981
srchpgm = mail_newsearchpgm();
1982
srchpgm->unseen = 1;
1984
pine_mail_search_full(stream, NULL, srchpgm,
1985
SE_NOPREFETCH | SE_FREE);
1986
status.flags |= SA_UNSEEN;
1988
for(i = 1L; i <= stream->nmsgs; i++)
1989
if((mc = mail_elt(stream, i)) && mc->searched)
1993
if(flags & SA_UIDVALIDITY){
1994
status.flags |= SA_UIDVALIDITY;
1995
status.uidvalidity = stream->uid_validity;
1998
if(flags & SA_UIDNEXT){
1999
status.flags |= SA_UIDNEXT;
2000
status.uidnext = stream->uid_last + 1L;
2003
mm_status(NULL, mailbox, &status);
2004
return T; /* that's what c-client returns when success */
2008
stream = sp_stream_get(mailbox, SP_SAME);
2011
stream = pine_mail_open(NULL, mailbox, openflags, NULL);
2015
else if(F_ON(F_ENABLE_MULNEWSRCS, ps_global)
2016
&& d && (!strucmp(d->name, "news")
2017
|| !strucmp(d->name, "nntp")))
2018
flags |= SA_MULNEWSRC;
2021
if(!ps_global->user_says_cancel)
2022
ret = mail_status(stream, mailbox, flags); /* non #move case */
2026
pine_mail_close(ourstream);
2033
* Check for a mailbox name that is a legitimate #move mailbox.
2035
* Args mbox -- The mailbox name to check
2036
* sourcebuf -- Copy the source mailbox name into this buffer
2037
* sbuflen -- Length of sourcebuf
2038
* targetptr -- Set the pointer this points to to point to the
2039
* target mailbox name in the original string
2041
* Returns 1 - is a #move mailbox
2045
check_for_move_mbox(char *mbox, char *sourcebuf, size_t sbuflen, char **targetptr)
2050
if(mbox && (mbox[0] == '#')
2051
&& ((mbox[1] == 'M') || (mbox[1] == 'm'))
2052
&& ((mbox[2] == 'O') || (mbox[2] == 'o'))
2053
&& ((mbox[3] == 'V') || (mbox[3] == 'v'))
2054
&& ((mbox[4] == 'E') || (mbox[4] == 'e'))
2055
&& (delim = mbox[5])
2056
&& (s = strchr(mbox+6, delim))
2057
&& (i = s++ - (mbox + 6))
2058
&& (!sourcebuf || i < sbuflen)){
2061
strncpy(sourcebuf, mbox+6, i); /* copy source mailbox name */
2062
sourcebuf[i] = '\0';
2076
* Checks through stream cache for a stream pointer already open to
2077
* this mailbox, read/write. Very similar to sp_stream_get, but we want
2078
* to look at all streams, not just imap streams.
2079
* Right now it is very specialized. If we want to use it more generally,
2080
* generalize it or combine it with sp_stream_get somehow.
2083
already_open_stream(char *mailbox, int flags)
2091
if(*mailbox == '{'){
2092
for(i = 0; i < ps_global->s_pool.nstream; i++){
2093
m = ps_global->s_pool.streams[i];
2094
if(m && !(flags & AOS_RW_ONLY && m->rdonly)
2095
&& (*m->mailbox == '{') && !sp_dead_stream(m)
2096
&& same_stream_and_mailbox(mailbox, m))
2101
char *cn, tmp[MAILTMPLEN];
2103
cn = mailboxfile(tmp, mailbox);
2104
for(i = 0; i < ps_global->s_pool.nstream; i++){
2105
m = ps_global->s_pool.streams[i];
2106
if(m && !(flags & AOS_RW_ONLY && m->rdonly)
2107
&& (*m->mailbox != '{') && !sp_dead_stream(m)
2108
&& ((cn && *cn && !strcmp(cn, m->mailbox))
2109
|| !strcmp(mailbox, m->original_mailbox)
2110
|| !strcmp(mailbox, m->mailbox)))
2120
pine_imap_cmd_happened(MAILSTREAM *stream, char *cmd, long int flags)
2122
dprint((9, "imap_cmd(%s, %s, 0x%lx)\n",
2123
STREAMNAME(stream), cmd ? cmd : "?", flags));
2125
if(cmd && !strucmp(cmd, "CHECK"))
2126
reset_check_point(stream);
2128
if(is_imap_stream(stream)){
2132
sp_set_last_ping(stream, now);
2133
sp_set_last_activity(stream, now);
2134
if(!(flags & SC_EXPUNGEDEFERRED))
2135
sp_set_last_expunged_reaper(stream, now);
2141
* Tells us whether we ought to check for a dead stream or not.
2142
* We assume that we ought to check if it is not IMAP and if it is IMAP we
2143
* don't need to check if the last activity was within the last 5 minutes.
2146
recent_activity(MAILSTREAM *stream)
2148
if(is_imap_stream(stream) && !sp_dead_stream(stream)
2149
&& (time(0) - sp_last_activity(stream) < 5L * 60L))
2156
sp_cleanup_dead_streams(void)
2161
(void) streams_died(); /* tell user in case they don't know yet */
2163
for(i = 0; i < ps_global->s_pool.nstream; i++){
2164
m = ps_global->s_pool.streams[i];
2165
if(m && sp_dead_stream(m))
2172
* Returns 0 if stream flags not set, non-zero if they are.
2175
sp_flagged(MAILSTREAM *stream, long unsigned int flags)
2177
return(sp_flags(stream) & flags);
2182
sp_set_fldr(MAILSTREAM *stream, char *folder)
2186
pss = sp_data(stream);
2189
fs_give((void **) &(*pss)->fldr);
2192
(*pss)->fldr = cpystr(folder);
2198
sp_set_saved_cur_msg_id(MAILSTREAM *stream, char *id)
2202
pss = sp_data(stream);
2204
if((*pss)->saved_cur_msg_id)
2205
fs_give((void **) &(*pss)->saved_cur_msg_id);
2208
(*pss)->saved_cur_msg_id = cpystr(id);
2214
* Sets flags absolutely, erasing old flags.
2217
sp_flag(MAILSTREAM *stream, long unsigned int flags)
2222
dprint((9, "sp_flag(%s, 0x%x): %s%s%s%s%s%s%s%s\n",
2223
(stream && stream->mailbox) ? stream->mailbox : "?",
2225
flags ? "set" : "clear",
2226
(flags & SP_LOCKED) ? " SP_LOCKED" : "",
2227
(flags & SP_PERMLOCKED) ? " SP_PERMLOCKED" : "",
2228
(flags & SP_INBOX) ? " SP_INBOX" : "",
2229
(flags & SP_USERFLDR) ? " SP_USERFLDR" : "",
2230
(flags & SP_USEPOOL) ? " SP_USEPOOL" : "",
2231
(flags & SP_TEMPUSE) ? " SP_TEMPUSE" : "",
2232
!flags ? " ALL" : ""));
2234
sp_set_flags(stream, flags);
2239
* Clear individual stream flags.
2242
sp_unflag(MAILSTREAM *stream, long unsigned int flags)
2244
if(!stream || !flags)
2247
dprint((9, "sp_unflag(%s, 0x%x): unset%s%s%s%s%s%s\n",
2248
(stream && stream->mailbox) ? stream->mailbox : "?",
2250
(flags & SP_LOCKED) ? " SP_LOCKED" : "",
2251
(flags & SP_PERMLOCKED) ? " SP_PERMLOCKED" : "",
2252
(flags & SP_INBOX) ? " SP_INBOX" : "",
2253
(flags & SP_USERFLDR) ? " SP_USERFLDR" : "",
2254
(flags & SP_USEPOOL) ? " SP_USEPOOL" : "",
2255
(flags & SP_TEMPUSE) ? " SP_TEMPUSE" : ""));
2257
sp_set_flags(stream, sp_flags(stream) & ~flags);
2259
flags = sp_flags(stream);
2260
dprint((9, "sp_unflag(%s, 0x%x): result:%s%s%s%s%s%s\n",
2261
(stream && stream->mailbox) ? stream->mailbox : "?",
2263
(flags & SP_LOCKED) ? " SP_LOCKED" : "",
2264
(flags & SP_PERMLOCKED) ? " SP_PERMLOCKED" : "",
2265
(flags & SP_INBOX) ? " SP_INBOX" : "",
2266
(flags & SP_USERFLDR) ? " SP_USERFLDR" : "",
2267
(flags & SP_USEPOOL) ? " SP_USEPOOL" : "",
2268
(flags & SP_TEMPUSE) ? " SP_TEMPUSE" : ""));
2273
* Set dead stream indicator and close if not locked.
2276
sp_mark_stream_dead(MAILSTREAM *stream)
2281
dprint((9, "sp_mark_stream_dead(%s)\n",
2282
(stream && stream->mailbox) ? stream->mailbox : "?"));
2285
* If the stream isn't locked, it is no longer useful. Get rid of it.
2287
if(!sp_flagged(stream, SP_LOCKED))
2288
pine_mail_actually_close(stream);
2291
* If it is locked, then we have to worry about references to it
2292
* that still exist. For example, it might be a permlocked stream
2293
* or it might be the current stream. We need to let it be discovered
2294
* by those referencers instead of just eliminating it, so that they
2295
* can clean up the mess they need to clean up.
2297
sp_set_dead_stream(stream, 1);
2303
* Returns the number of streams in the stream pool which are
2304
* SP_USEPOOL but not SP_PERMLOCKED.
2307
sp_nusepool_notperm(void)
2312
for(i = 0; i < ps_global->s_pool.nstream; i++){
2313
m = ps_global->s_pool.streams[i];
2314
if(sp_flagged(m, SP_USEPOOL) && !sp_flagged(m, SP_PERMLOCKED))
2323
* Returns the number of folders that the user has marked to be PERMLOCKED
2324
* folders (plus INBOX) that are remote IMAP folders.
2326
* This routine depends on the fact that VAR_INBOX_PATH, VAR_PERMLOCKED,
2327
* and the ps_global->context_list are correctly set.
2330
sp_nremote_permlocked(void)
2333
char **lock_these, *p = NULL, *dummy = NULL, *lt;
2336
/* first check if INBOX is remote */
2337
lt = ps_global->VAR_INBOX_PATH;
2338
if(lt && (d=mail_valid(NIL, lt, (char *) NIL))
2339
&& !strcmp(d->name, "imap"))
2342
/* then count the user-specified permlocked folders */
2343
for(lock_these = ps_global->VAR_PERMLOCKED; lock_these && *lock_these;
2347
* Skip inbox, already done above. Should do this better so that we
2348
* catch the case where the user puts the technical spec of the inbox
2349
* in the list, or where the user lists one folder twice.
2351
if(*lock_these && !strucmp(*lock_these, ps_global->inbox_name))
2354
/* there isn't really a pair, it just dequotes for us */
2355
get_pair(*lock_these, &dummy, &p, 0, 0);
2358
* Check to see if this is an incoming nickname and replace it
2359
* with the full name.
2361
if(!(p && ps_global->context_list
2362
&& ps_global->context_list->use & CNTXT_INCMNG
2363
&& (lt=folder_is_nick(p, FOLDERS(ps_global->context_list),
2368
fs_give((void **) &dummy);
2370
if(lt && (d=mail_valid(NIL, lt, (char *) NIL))
2371
&& !strcmp(d->name, "imap"))
2375
fs_give((void **) &p);
2383
* Look for an already open stream that can be used for a new purpose.
2384
* (Note that we only look through streams flagged SP_USEPOOL.)
2389
* Flags is a set of values or'd together which tells us what the request
2392
* Returns: a live stream from the stream pool or NULL.
2395
sp_stream_get(char *mailbox, long unsigned int flags)
2400
dprint((7, "sp_stream_get(%s):%s%s%s%s%s\n",
2401
mailbox ? mailbox : "?",
2402
(flags & SP_MATCH) ? " SP_MATCH" : "",
2403
(flags & SP_RO_OK) ? " SP_RO_OK" : "",
2404
(flags & SP_SAME) ? " SP_SAME" : "",
2405
(flags & SP_UNLOCKED) ? " SP_UNLOCKED" : "",
2406
(flags & SP_TEMPUSE) ? " SP_TEMPUSE" : ""));
2408
/* look for stream already open to this mailbox */
2409
if(flags & SP_MATCH){
2410
for(i = 0; i < ps_global->s_pool.nstream; i++){
2411
m = ps_global->s_pool.streams[i];
2412
if(m && sp_flagged(m, SP_USEPOOL)
2413
&& (!m->rdonly || (flags & SP_RO_OK)) && !sp_dead_stream(m)
2414
&& same_stream_and_mailbox(mailbox, m)){
2415
if((sp_flagged(m, SP_LOCKED) && recent_activity(m))
2416
|| pine_mail_ping(m)){
2418
"sp_stream_get: found exact match, slot %d\n", i));
2419
if(!sp_flagged(m, SP_LOCKED)){
2421
"reset idle timer1: next TAG %08lx (%s)\n",
2423
debug_time(1, ps_global->debug_timestamp)));
2424
sp_set_last_use_time(m, time(0));
2430
sp_mark_stream_dead(m);
2436
* SP_SAME will not match if an SP_MATCH match would have worked.
2437
* If the caller is interested in SP_MATCH streams as well as SP_SAME
2438
* streams then the caller should make two separate calls to this
2441
if(flags & SP_SAME){
2443
* If the flags arg does not have either SP_TEMPUSE or SP_UNLOCKED
2444
* set, then we'll accept any stream, even if locked.
2445
* We want to prefer the LOCKED case so that we don't have to ping.
2447
if(!(flags & SP_UNLOCKED) && !(flags & SP_TEMPUSE)){
2448
for(i = 0; i < ps_global->s_pool.nstream; i++){
2449
m = ps_global->s_pool.streams[i];
2450
if(m && sp_flagged(m, SP_USEPOOL)
2451
&& sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
2452
&& same_stream(mailbox, m)
2453
&& !same_stream_and_mailbox(mailbox, m)){
2454
if(recent_activity(m) || pine_mail_ping(m)){
2456
"sp_stream_get: found SAME match, slot %d\n", i));
2460
sp_mark_stream_dead(m);
2464
/* consider the unlocked streams */
2465
for(i = 0; i < ps_global->s_pool.nstream; i++){
2466
m = ps_global->s_pool.streams[i];
2467
if(m && sp_flagged(m, SP_USEPOOL)
2468
&& !sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
2469
&& same_stream(mailbox, m)
2470
&& !same_stream_and_mailbox(mailbox, m)){
2471
/* always ping unlocked streams */
2472
if(pine_mail_ping(m)){
2474
"sp_stream_get: found SAME match, slot %d\n", i));
2476
"reset idle timer4: next TAG %08lx (%s)\n",
2478
debug_time(1, ps_global->debug_timestamp)));
2479
sp_set_last_use_time(m, time(0));
2484
sp_mark_stream_dead(m);
2490
* Prefer streams marked SP_TEMPUSE and not LOCKED.
2491
* If SP_TEMPUSE is set in the flags arg then this is the
2494
for(i = 0; i < ps_global->s_pool.nstream; i++){
2495
m = ps_global->s_pool.streams[i];
2496
if(m && sp_flagged(m, SP_USEPOOL) && sp_flagged(m, SP_TEMPUSE)
2497
&& !sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
2498
&& same_stream(mailbox, m)
2499
&& !same_stream_and_mailbox(mailbox, m)){
2500
if(pine_mail_ping(m)){
2502
"sp_stream_get: found SAME/TEMPUSE match, slot %d\n", i));
2504
"reset idle timer2: next TAG %08lx (%s)\n",
2506
debug_time(1, ps_global->debug_timestamp)));
2507
sp_set_last_use_time(m, time(0));
2511
sp_mark_stream_dead(m);
2516
* If SP_TEMPUSE is not set in the flags arg but SP_UNLOCKED is,
2517
* then we will consider
2518
* streams which are not marked SP_TEMPUSE (but are still not
2519
* locked). We go through these in reverse order so that we'll get
2520
* the last one added instead of the first one. It's not clear if
2521
* that is a good idea or if a more complex search would somehow
2522
* be better. Maybe we should use a round-robin sort of search
2523
* here so that we don't leave behind unused streams. Or maybe
2524
* we should keep track of when we used it and look for the LRU stream.
2526
if(!(flags & SP_TEMPUSE)){
2527
for(i = ps_global->s_pool.nstream - 1; i >= 0; i--){
2528
m = ps_global->s_pool.streams[i];
2529
if(m && sp_flagged(m, SP_USEPOOL)
2530
&& !sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
2531
&& same_stream(mailbox, m)
2532
&& !same_stream_and_mailbox(mailbox, m)){
2533
if(pine_mail_ping(m)){
2535
"sp_stream_get: found SAME/UNLOCKED match, slot %d\n", i));
2537
"reset idle timer3: next TAG %08lx (%s)\n",
2539
debug_time(1, ps_global->debug_timestamp)));
2540
sp_set_last_use_time(m, time(0));
2544
sp_mark_stream_dead(m);
2551
* If we can't find a useful stream to use in pine_mail_open, we may
2552
* want to re-use one that is not actively being used even though it
2553
* is not on the same server. We'll have to close it and then re-use
2556
if(!(flags & (SP_SAME | SP_MATCH))){
2558
* Prefer streams marked SP_TEMPUSE and not LOCKED.
2559
* If SP_TEMPUSE is set in the flags arg then this is the
2562
for(i = 0; i < ps_global->s_pool.nstream; i++){
2563
m = ps_global->s_pool.streams[i];
2564
if(m && sp_flagged(m, SP_USEPOOL) && sp_flagged(m, SP_TEMPUSE)
2565
&& !sp_flagged(m, SP_LOCKED)){
2567
"sp_stream_get: found Not-SAME/TEMPUSE match, slot %d\n", i));
2569
* We ping it in case there is new mail that we should
2570
* pass through our filters. Pine_mail_actually_close will
2573
(void) pine_mail_ping(m);
2579
* If SP_TEMPUSE is not set in the flags arg, then we will consider
2580
* streams which are not marked SP_TEMPUSE (but are still not
2581
* locked). Maybe we should use a round-robin sort of search
2582
* here so that we don't leave behind unused streams. Or maybe
2583
* we should keep track of when we used it and look for the LRU stream.
2585
if(!(flags & SP_TEMPUSE)){
2586
for(i = ps_global->s_pool.nstream - 1; i >= 0; i--){
2587
m = ps_global->s_pool.streams[i];
2588
if(m && sp_flagged(m, SP_USEPOOL) && !sp_flagged(m, SP_LOCKED)){
2590
"sp_stream_get: found Not-SAME/UNLOCKED match, slot %d\n", i));
2592
* We ping it in case there is new mail that we should
2593
* pass through our filters. Pine_mail_actually_close will
2596
(void) pine_mail_ping(m);
2603
dprint((7, "sp_stream_get: no match found\n"));
2615
dprint((7, "sp_end\n"));
2617
for(i = 0; i < ps_global->s_pool.nstream; i++){
2618
m = ps_global->s_pool.streams[i];
2620
pine_mail_actually_close(m);
2623
if(ps_global->s_pool.streams)
2624
fs_give((void **) &ps_global->s_pool.streams);
2626
ps_global->s_pool.nstream = 0;
2631
* Find a vacant slot to put this new stream in.
2632
* We are willing to close and kick out another stream as long as it isn't
2633
* LOCKED. However, we may find that there is no place to put this one
2634
* because all the slots are used and locked. For now, we'll return -1
2635
* in that case and leave the new stream out of the pool.
2638
sp_add(MAILSTREAM *stream, int usepool)
2643
dprint((7, "sp_add(%s, %d)\n",
2644
(stream && stream->mailbox) ? stream->mailbox : "?", usepool));
2647
dprint((7, "sp_add: NULL stream\n"));
2651
/* If this stream is already there, don't add it again */
2652
for(i = 0; i < ps_global->s_pool.nstream; i++){
2653
m = ps_global->s_pool.streams[i];
2657
"sp_add: stream was already in slot %d\n", slot));
2662
if(usepool && !sp_flagged(stream, SP_PERMLOCKED)
2663
&& sp_nusepool_notperm() >= ps_global->s_pool.max_remstream){
2665
"sp_add: reached max implicit SP_USEPOOL of %d\n",
2666
ps_global->s_pool.max_remstream));
2670
/* Look for an unused slot */
2671
for(i = 0; i < ps_global->s_pool.nstream; i++){
2672
m = ps_global->s_pool.streams[i];
2676
"sp_add: using empty slot %d\n", slot));
2681
/* else, allocate more space */
2683
ps_global->s_pool.nstream++;
2684
slot = ps_global->s_pool.nstream - 1;
2685
if(ps_global->s_pool.streams){
2686
fs_resize((void **) &ps_global->s_pool.streams,
2687
ps_global->s_pool.nstream *
2688
sizeof(*ps_global->s_pool.streams));
2689
ps_global->s_pool.streams[slot] = NULL;
2692
ps_global->s_pool.streams =
2693
(MAILSTREAM **) fs_get(ps_global->s_pool.nstream *
2694
sizeof(*ps_global->s_pool.streams));
2695
memset(ps_global->s_pool.streams, 0,
2696
ps_global->s_pool.nstream *
2697
sizeof(*ps_global->s_pool.streams));
2701
"sp_add: allocate more space, using new slot %d\n", slot));
2704
if(slot >= 0 && slot < ps_global->s_pool.nstream){
2705
ps_global->s_pool.streams[slot] = stream;
2709
dprint((7, "sp_add: failed to find a slot!\n"));
2716
* Simply remove this stream from the stream pool.
2719
sp_delete(MAILSTREAM *stream)
2727
dprint((7, "sp_delete(%s)\n",
2728
(stream && stream->mailbox) ? stream->mailbox : "?"));
2731
* There are some global stream pointers that we have to worry
2732
* about before deleting the stream.
2735
/* first, mail_stream is the global currently open folder */
2736
if(ps_global->mail_stream == stream)
2737
ps_global->mail_stream = NULL;
2739
/* remote address books may have open stream pointers */
2740
note_closed_adrbk_stream(stream);
2742
for(i = 0; i < ps_global->s_pool.nstream; i++){
2743
m = ps_global->s_pool.streams[i];
2745
ps_global->s_pool.streams[i] = NULL;
2747
"sp_delete: stream removed from slot %d\n", i));
2755
* Returns 1 if any locked userfldr is dead, 0 if all alive.
2758
sp_a_locked_stream_is_dead(void)
2763
for(i = 0; !ret && i < ps_global->s_pool.nstream; i++){
2764
m = ps_global->s_pool.streams[i];
2765
if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
2766
&& sp_dead_stream(m))
2775
* Returns 1 if any locked stream is changed, 0 otherwise
2778
sp_a_locked_stream_changed(void)
2783
for(i = 0; !ret && i < ps_global->s_pool.nstream; i++){
2784
m = ps_global->s_pool.streams[i];
2785
if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
2786
&& sp_mail_box_changed(m))
2795
* Returns the inbox stream or NULL.
2798
sp_inbox_stream(void)
2801
MAILSTREAM *m, *ret = NULL;
2803
for(i = 0; !ret && i < ps_global->s_pool.nstream; i++){
2804
m = ps_global->s_pool.streams[i];
2805
if(m && sp_flagged(m, SP_INBOX))
2814
* Make sure that the sp_data per-stream data storage area exists.
2816
* Returns a handle to the sp_data data unless stream is NULL,
2817
* in which case NULL is returned
2820
sp_data(MAILSTREAM *stream)
2822
PER_STREAM_S **pss = NULL;
2825
if(*(pss = (PER_STREAM_S **) &stream->sparep) == NULL){
2826
*pss = (PER_STREAM_S *) fs_get(sizeof(PER_STREAM_S));
2827
memset(*pss, 0, sizeof(PER_STREAM_S));
2828
reset_check_point(stream);
2837
* Returns a pointer to the msgmap associated with the argument stream.
2839
* If the PER_STREAM_S data or the msgmap does not already exist, it will be
2843
sp_msgmap(MAILSTREAM *stream)
2845
MSGNO_S **msgmap = NULL;
2846
PER_STREAM_S **pss = NULL;
2848
pss = sp_data(stream);
2851
&& (*(msgmap = (MSGNO_S **) &(*pss)->msgmap) == NULL))
2852
mn_init(msgmap, stream->nmsgs);
2854
return(msgmap ? *msgmap : NULL);
2859
sp_free_callback(void **sparep)
2862
MAILSTREAM *stream = NULL, *m;
2865
pss = (PER_STREAM_S **) sparep;
2869
* It is possible that this has been called from c-client when
2870
* we weren't expecting it. We need to clean up the stream pool
2871
* entries if the stream that goes with this pointer is in the
2872
* stream pool somewhere.
2874
for(i = 0; !stream && i < ps_global->s_pool.nstream; i++){
2875
m = ps_global->s_pool.streams[i];
2876
if(sparep && *sparep && m && m->sparep == *sparep)
2881
if(ps_global->mail_stream == stream)
2882
ps_global->mail_stream = NULL;
2893
* Free the data but don't mess with the stream pool.
2896
sp_free(PER_STREAM_S **pss)
2900
if(ps_global->msgmap == (*pss)->msgmap)
2901
ps_global->msgmap = NULL;
2903
mn_give(&(*pss)->msgmap);
2907
fs_give((void **) &(*pss)->fldr);
2909
fs_give((void **) pss);
2915
/*----------------------------------------------------------------------
2916
See if stream can be used for a mailbox name
2918
Accepts: mailbox name
2920
Returns: stream if it can be used, else NIL
2922
This is called to weed out unnecessary use of c-client streams. In other
2923
words, to help facilitate re-use of streams.
2925
This code is very similar to the same_remote_mailboxes code below, which
2926
is used in pine_mail_open. That code compares two mailbox names. One is
2927
usually from the config file and the other is either from the config file
2928
or is typed in. Here and in same_stream_and_mailbox below, we're comparing
2929
an open stream to a name instead of two names. We could conceivably use
2930
same_remote_mailboxes to compare stream->mailbox to name, but it isn't
2931
exactly the same and the differences may be important. Some stuff that
2932
happens here seems wrong, but it isn't easy to fix.
2933
Having !mb_n.port count as a match to any mb_s.port isn't right. It should
2934
only match if mb_s.port is equal to the default, but the default isn't
2935
something that is available to us. The same thing is done in c-client in
2936
the mail_usable_network_stream() routine, and it isn't right there, either.
2937
The semantics of a missing user are also suspect, because just like with
2938
port, a default is used.
2941
same_stream(char *name, MAILSTREAM *stream)
2943
NETMBX mb_s, mb_n, mb_o;
2945
if(stream && stream->mailbox && *stream->mailbox && name && *name
2946
&& !(sp_dead_stream(stream))
2947
&& mail_valid_net_parse(stream->mailbox, &mb_s)
2948
&& mail_valid_net_parse(stream->original_mailbox, &mb_o)
2949
&& mail_valid_net_parse(name, &mb_n)
2950
&& !strucmp(mb_n.service, mb_s.service)
2951
&& (!strucmp(mb_n.host, mb_o.host) /* s is already canonical */
2952
|| !strucmp(canonical_name(mb_n.host), mb_s.host))
2953
&& (!mb_n.port || mb_n.port == mb_s.port)
2954
&& mb_n.anoflag == stream->anonymous
2955
&& ((mb_n.user && *mb_n.user &&
2956
mb_s.user && !strcmp(mb_n.user, mb_s.user))
2958
((!mb_n.user || !*mb_n.user)
2960
&& ((ps_global->VAR_USER_ID
2961
&& !strcmp(ps_global->VAR_USER_ID, mb_s.user))
2963
(!ps_global->VAR_USER_ID
2964
&& ps_global->ui.login[0]
2965
&& !strcmp(ps_global->ui.login, mb_s.user))))
2967
(!((mb_n.user && *mb_n.user) || (mb_s.user && *mb_s.user))
2968
&& stream->anonymous))
2969
&& (struncmp(mb_n.service, "imap", 4) ? 1 : strcmp(imap_host(stream), ".NO-IMAP-CONNECTION."))){
2970
dprint((7, "same_stream: name->%s == stream->%s: yes\n",
2972
(stream && stream->mailbox) ? stream->mailbox : "NULL"));
2976
dprint((7, "same_stream: name->%s == stream->%s: no dice\n",
2978
(stream && stream->mailbox) ? stream->mailbox : "NULL"));
2984
/*----------------------------------------------------------------------
2985
See if this stream has the named mailbox selected.
2987
Accepts: mailbox name
2989
Returns: stream if it can be used, else NIL
2992
same_stream_and_mailbox(char *name, MAILSTREAM *stream)
2996
if(same_stream(name, stream)
2997
&& mail_valid_net_parse(stream->mailbox, &mb_s)
2998
&& mail_valid_net_parse(name, &mb_n)
2999
&& (mb_n.mailbox && mb_s.mailbox
3000
&& (!strcmp(mb_n.mailbox,mb_s.mailbox) /* case depend except INBOX */
3001
|| (!strucmp(mb_n.mailbox,"INBOX")
3002
&& !strucmp(mb_s.mailbox,"INBOX"))))){
3004
"same_stream_and_mailbox: name->%s == stream->%s: yes\n",
3006
(stream && stream->mailbox) ? stream->mailbox : "NULL"));
3011
"same_stream_and_mailbox: name->%s == stream->%s: no dice\n",
3013
(stream && stream->mailbox) ? stream->mailbox : "NULL"));
3018
* Args -- name1 and name2 are remote mailbox names.
3020
* Returns -- True if names refer to same mailbox accessed in same way
3023
* This has some very similar code to same_stream_and_mailbox but we're not
3024
* quite ready to discard the differences.
3025
* The treatment of the port and the user is not quite the same.
3028
same_remote_mailboxes(char *name1, char *name2)
3034
* Probably we should allow !port equal to default port, but we don't
3035
* know how to get the default port. To match what c-client does we
3036
* allow !port to be equal to anything.
3038
return(name1 && IS_REMOTE(name1)
3039
&& name2 && IS_REMOTE(name2)
3040
&& mail_valid_net_parse(name1, &mb1)
3041
&& mail_valid_net_parse(name2, &mb2)
3042
&& !strucmp(mb1.service, mb2.service)
3043
&& (!strucmp(mb1.host, mb2.host) /* just to save DNS lookups */
3044
|| !strucmp(cn1=canonical_name(mb1.host), mb2.host)
3045
|| !strucmp(cn1, canonical_name(mb2.host)))
3046
&& (!mb1.port || !mb2.port || mb1.port == mb2.port)
3047
&& mb1.anoflag == mb2.anoflag
3048
&& mb1.mailbox && mb2.mailbox
3049
&& (!strcmp(mb1.mailbox, mb2.mailbox)
3050
|| (!strucmp(mb1.mailbox,"INBOX")
3051
&& !strucmp(mb2.mailbox,"INBOX")))
3052
&& ((mb1.user && *mb1.user && mb2.user && *mb2.user
3053
&& !strcmp(mb1.user, mb2.user))
3055
(!(mb1.user && *mb1.user) && !(mb2.user && *mb2.user))
3057
(!(mb1.user && *mb1.user)
3058
&& ((ps_global->VAR_USER_ID
3059
&& !strcmp(ps_global->VAR_USER_ID, mb2.user))
3061
(!ps_global->VAR_USER_ID
3062
&& ps_global->ui.login[0]
3063
&& !strcmp(ps_global->ui.login, mb2.user))))
3065
(!(mb2.user && *mb2.user)
3066
&& ((ps_global->VAR_USER_ID
3067
&& !strcmp(ps_global->VAR_USER_ID, mb1.user))
3069
(!ps_global->VAR_USER_ID
3070
&& ps_global->ui.login[0]
3071
&& !strcmp(ps_global->ui.login, mb1.user))))));
3076
is_imap_stream(MAILSTREAM *stream)
3078
return(stream && stream->dtb && stream->dtb->name
3079
&& !strcmp(stream->dtb->name, "imap"));
3084
modern_imap_stream(MAILSTREAM *stream)
3086
return(is_imap_stream(stream) && LEVELIMAP4rev1(stream));
3090
/*----------------------------------------------------------------------
3091
Check and see if all the stream are alive
3093
Returns: 0 if there was no change
3094
>0 if streams have died since last call
3096
Also outputs a message that the streams have died
3106
for(i = 0; i < ps_global->s_pool.nstream; i++){
3107
m = ps_global->s_pool.streams[i];
3108
if(m && sp_dead_stream(m)){
3109
if(sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)){
3110
if(!sp_noticed_dead_stream(m)){
3112
sp_set_noticed_dead_stream(m, 1);
3113
folder = STREAMNAME(m);
3114
q_status_message1(SM_ORDER | SM_DING, 3, 3,
3115
_("MAIL FOLDER \"%s\" CLOSED DUE TO ACCESS ERROR"),
3116
short_str(pretty_fn(folder) ? pretty_fn(folder) : "?",
3117
tmp_20k_buf+1000, SIZEOF_20KBUF-1000, 35, FrontDots));
3118
dprint((6, "streams_died: locked: \"%s\"\n",
3121
snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Folder \"%s\" is Closed"),
3122
short_str(pretty_fn(folder) ? pretty_fn(folder) : "?",
3123
tmp_20k_buf+1000, SIZEOF_20KBUF-1000, 35, FrontDots));
3124
if(pith_opt_icon_text)
3125
(*pith_opt_icon_text)(tmp_20k_buf, IT_MCLOSED);
3130
if(!sp_noticed_dead_stream(m)){
3131
sp_set_noticed_dead_stream(m, 1);
3132
folder = STREAMNAME(m);
3134
* If a cached stream died and then we tried to use it
3135
* it could cause problems. We could warn about it here
3136
* but it may be confusing because it might be
3137
* unrelated to what the user is doing and not cause
3138
* any problem at all.
3141
if(sp_flagged(m, SP_USEPOOL))
3142
q_status_message(SM_ORDER, 3, 3,
3143
"Warning: Possible problem accessing remote data, connection died.");
3146
dprint((6, "streams_died: not locked: \"%s\"\n",
3150
pine_mail_actually_close(m);
3160
* mail_cmd_stream - return a stream suitable for mail_lsub,
3161
* mail_subscribe, and mail_unsubscribe
3165
mail_cmd_stream(CONTEXT_S *context, int *closeit)
3167
char tmp[MAILTMPLEN];
3170
(void) context_apply(tmp, context, "x", sizeof(tmp));
3172
return(pine_mail_open(NULL, tmp,
3173
OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE,
3179
* This is so we can replace the old rfc822_ routines like rfc822_header_line
3180
* with the new version that checks bounds, like rfc822_output_header_line.
3181
* This routine is called when would be a bounds overflow, which we simply log
3182
* and go on with the truncated data.
3185
dummy_soutr(void *stream, char *string)
3187
dprint((2, "dummy_soutr unexpected call, caught overflow\n"));