~ubuntu-branches/debian/squeeze/alpine/squeeze

« back to all changes in this revision

Viewing changes to pith/stream.c

  • Committer: Bazaar Package Importer
  • Author(s): Asheesh Laroia
  • Date: 2007-02-17 13:17:42 UTC
  • Revision ID: james.westby@ubuntu.com-20070217131742-99x5c6cpg1pbkdhw
Tags: upstream-0.82+dfsg
ImportĀ upstreamĀ versionĀ 0.82+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#if !defined(lint) && !defined(DOS)
 
2
static char rcsid[] = "$Id: stream.c 394 2007-01-25 20:29:45Z hubert@u.washington.edu $";
 
3
#endif
 
4
 
 
5
/*
 
6
 * ========================================================================
 
7
 * Copyright 2006 University of Washington
 
8
 *
 
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
 
12
 *
 
13
 *     http://www.apache.org/licenses/LICENSE-2.0
 
14
 *
 
15
 * ========================================================================
 
16
 */
 
17
 
 
18
/*======================================================================
 
19
     stream.c
 
20
     Implements the Pine mail stream management routines
 
21
     and c-client wrapper functions
 
22
  ====*/
 
23
 
 
24
 
 
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"
 
41
 
 
42
/*
 
43
 * Internal prototypes
 
44
 */
 
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 **);
 
54
 
 
55
 
 
56
static FETCH_READC_S *g_pft_desc;
 
57
 
 
58
 
 
59
MAILSTATUS *pine_cached_status;  /* implement status for #move folder */
 
60
 
 
61
 
 
62
 
 
63
/*
 
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
 
71
 * that goes with it.
 
72
 *
 
73
 * Args:
 
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
 
81
 *                we got here.
 
82
 */
 
83
MAILSTREAM *
 
84
pine_mail_open(MAILSTREAM *stream, char *mailbox, long int openflags, long int *retflags)
 
85
{
 
86
    MAILSTREAM *retstream = NULL;
 
87
    DRIVER     *d;
 
88
    int         permlocked = 0, is_inbox = 0, usepool = 0, tempuse = 0, uf = 0;
 
89
    unsigned long flags;
 
90
    char      **lock_these;
 
91
    static unsigned long streamcounter = 0;
 
92
 
 
93
    dprint((7,
 
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)",
 
97
           openflags,
 
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)));
 
108
    
 
109
    if(retflags)
 
110
      *retflags = 0L;
 
111
 
 
112
    if(ps_global->user_says_cancel){
 
113
        dprint((7, "pine_mail_open: cancelled by user\n"));
 
114
        return(retstream);
 
115
    }
 
116
 
 
117
    is_inbox    = openflags & SP_INBOX;
 
118
    uf          = openflags & SP_USERFLDR;
 
119
 
 
120
    /* inbox is still special, assume that we want to permlock it */
 
121
    permlocked  = (is_inbox || openflags & SP_PERMLOCKED) ? 1 : 0;
 
122
 
 
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];
 
128
 
 
129
        /* there isn't really a pair, it just dequotes for us */
 
130
        get_pair(*lock_these, &dummy, &p, 0, 0);
 
131
 
 
132
        /*
 
133
         * Check to see if this is an incoming nickname and replace it
 
134
         * with the full name.
 
135
         */
 
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))))
 
139
          lt = p;
 
140
 
 
141
        if(dummy)
 
142
          fs_give((void **) &dummy);
 
143
        
 
144
        if(lt && mailbox
 
145
           && (same_remote_mailboxes(mailbox, lt)
 
146
               ||
 
147
               (!IS_REMOTE(mailbox)
 
148
                && (lname=mailboxfile(tmp1, lt))
 
149
                && (mname=mailboxfile(tmp2, mailbox))
 
150
                && !strcmp(lname, mname))))
 
151
          permlocked++;
 
152
 
 
153
        if(p)
 
154
          fs_give((void **) &p);
 
155
    }
 
156
 
 
157
    /*
 
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.
 
163
     *
 
164
     * An implication of doing only imap here is that sp_stream_get will only
 
165
     * be concerned with imap streams.
 
166
     */
 
167
    if((d = mail_valid (NIL, mailbox, (char *) NIL))
 
168
       && !strcmp(d->name, "imap")){
 
169
        usepool = openflags & SP_USEPOOL;
 
170
        tempuse = openflags & SP_TEMPUSE;
 
171
    }
 
172
    else{
 
173
        if(IS_REMOTE(mailbox)){
 
174
            dprint((9, "pine_mail_open: not cacheable: %s\n", !d ? "no driver?" : d->name ? d->name : "?" ));
 
175
        }
 
176
        else{
 
177
            if(permlocked || (openflags & OP_READONLY)){
 
178
                /*
 
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
 
183
                 * below).
 
184
                 */
 
185
                dprint((9,
 
186
   "pine_mail_open: not cacheable: not remote, but check for local stream\n"));
 
187
            }
 
188
            else{
 
189
                dprint((9,
 
190
                       "pine_mail_open: not cacheable: not remote\n"));
 
191
            }
 
192
        }
 
193
    }
 
194
 
 
195
    /* If driver doesn't support halfopen, just open it. */
 
196
    if(d && (openflags & OP_HALFOPEN) && !(d->flags & DR_HALFOPEN)){
 
197
        openflags &= ~OP_HALFOPEN;
 
198
        dprint((9,
 
199
               "pine_mail_open: turning off OP_HALFOPEN flag\n"));
 
200
    }
 
201
 
 
202
    /*
 
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.
 
205
     */
 
206
    openflags &= ~(SP_USEPOOL | SP_TEMPUSE | SP_INBOX
 
207
                   | SP_PERMLOCKED | SP_USERFLDR);
 
208
 
 
209
#ifdef  DEBUG
 
210
    if(ps_global->debug_imap > 3 || ps_global->debugmem)
 
211
      openflags |= OP_DEBUG;
 
212
#endif
 
213
    
 
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;
 
218
    }
 
219
 
 
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)){
 
223
            DRIVER *d;
 
224
            if((d = mail_valid(NIL, source, (char *) NIL))
 
225
               && (!strcmp(d->name, "news")
 
226
                   || !strcmp(d->name, "nntp")))
 
227
              openflags |= OP_MULNEWSRC;
 
228
        }
 
229
        else if((d = mail_valid(NIL, mailbox, (char *) NIL))
 
230
           && !strcmp(d->name, "nntp"))
 
231
          openflags |= OP_MULNEWSRC;
 
232
    }
 
233
 
 
234
    /*    
 
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.
 
254
     */
 
255
    if(stream){
 
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)){
 
260
                dprint((7,
 
261
                       "pine_mail_open: closing passed in IMAP stream %s\n",
 
262
                       stream->mailbox ? stream->mailbox : "?"));
 
263
            }
 
264
            else{
 
265
                dprint((7,
 
266
                       "pine_mail_open: closing passed in non-IMAP stream %s\n",
 
267
                       stream->mailbox ? stream->mailbox : "?"));
 
268
            }
 
269
 
 
270
            pine_mail_close(stream);
 
271
            stream = NULL;
 
272
        }
 
273
    }
 
274
 
 
275
    if((usepool && !stream && permlocked)
 
276
       || (!usepool && (permlocked || (openflags & OP_READONLY))
 
277
           && (retstream = already_open_stream(mailbox, AOS_NONE)))){
 
278
        if(retstream)
 
279
          stream = retstream;
 
280
        else
 
281
          stream = sp_stream_get(mailbox,
 
282
                        SP_MATCH | ((openflags & OP_READONLY) ? SP_RO_OK : 0));
 
283
        if(stream){
 
284
            flags = SP_LOCKED
 
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);
 
290
 
 
291
            /*
 
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.
 
295
             */
 
296
            if(!(sp_flags(stream) & SP_LOCKED))
 
297
              reset_stream_view_state(stream);
 
298
 
 
299
            if(retflags){
 
300
                *retflags |= SP_MATCH;
 
301
                if(sp_flags(stream) & SP_LOCKED)
 
302
                  *retflags |= SP_LOCKED;
 
303
            }
 
304
 
 
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);
 
309
                dprint((7,
 
310
                       "pine_mail_open: permlocked: ref cnt up to %d\n",
 
311
                       sp_ref_cnt(stream)));
 
312
            }
 
313
            else if(sp_ref_cnt(stream) <= 0){
 
314
                sp_set_ref_cnt(stream, 1);
 
315
                dprint((7,
 
316
                       "pine_mail_open: permexact: ref cnt set to %d\n",
 
317
                       sp_ref_cnt(stream)));
 
318
            }
 
319
 
 
320
            carefully_reset_sp_flags(stream, flags);
 
321
 
 
322
            stream->silent = (openflags & OP_SILENT) ? T : NIL;
 
323
                
 
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",
 
328
                       stream->gensym));
 
329
            }
 
330
 
 
331
            return(stream);
 
332
        }
 
333
    }
 
334
 
 
335
    if(usepool && !stream){
 
336
        /*
 
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.
 
340
         */
 
341
        if(!permlocked)
 
342
          stream = sp_stream_get(mailbox,
 
343
                        SP_MATCH | ((openflags & OP_READONLY) ? SP_RO_OK : 0));
 
344
 
 
345
        if(stream){
 
346
            flags = SP_LOCKED
 
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);
 
352
 
 
353
            /*
 
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.
 
357
             */
 
358
            if(!(sp_flags(stream) & SP_LOCKED))
 
359
              reset_stream_view_state(stream);
 
360
 
 
361
            if(retflags){
 
362
                *retflags |= SP_MATCH;
 
363
                if(sp_flags(stream) & SP_LOCKED)
 
364
                  *retflags |= SP_LOCKED;
 
365
            }
 
366
 
 
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);
 
371
                dprint((7,
 
372
                       "pine_mail_open: matched: ref cnt up to %d\n",
 
373
                       sp_ref_cnt(stream)));
 
374
            }
 
375
            else if(sp_ref_cnt(stream) <= 0){
 
376
                sp_set_ref_cnt(stream, 1);
 
377
                dprint((7,
 
378
                       "pine_mail_open: exact: ref cnt set to %d\n",
 
379
                       sp_ref_cnt(stream)));
 
380
            }
 
381
 
 
382
            carefully_reset_sp_flags(stream, flags);
 
383
 
 
384
            /*
 
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.
 
388
             *
 
389
             * WARNING: we're messing with c-client internals (by necessity).
 
390
             */
 
391
            stream->silent = (openflags & OP_SILENT) ? T : NIL;
 
392
 
 
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",
 
397
                       stream->gensym));
 
398
            }
 
399
 
 
400
            return(stream);
 
401
        }
 
402
 
 
403
        /*
 
404
         * No exact match, look for a stream which is open to the same
 
405
         * server and marked for TEMPUSE.
 
406
         */
 
407
        stream = sp_stream_get(mailbox, SP_SAME | SP_TEMPUSE);
 
408
        if(stream){
 
409
            dprint((9,
 
410
                    "pine_mail_open: attempting to re-use TEMP stream\n"));
 
411
        }
 
412
        /*
 
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.
 
417
         */
 
418
        else if(!permlocked
 
419
                && sp_nusepool_notperm() >= ps_global->s_pool.max_remstream){
 
420
            dprint((9,
 
421
                    "pine_mail_open: no empty slots\n"));
 
422
            /*
 
423
             * No empty remote slots available. See if there is a
 
424
             * TEMPUSE stream that is open but to the wrong server.
 
425
             */
 
426
            stream = sp_stream_get(mailbox, SP_TEMPUSE);
 
427
            if(stream){
 
428
                /*
 
429
                 * We will close this stream and use the empty slot
 
430
                 * that that creates.
 
431
                 */
 
432
                dprint((9,
 
433
            "pine_mail_open: close a TEMPUSE stream and re-use that slot\n"));
 
434
                pine_mail_actually_close(stream);
 
435
                stream = NULL;
 
436
            }
 
437
            else{
 
438
 
 
439
                /*
 
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.
 
444
                 */
 
445
                stream = sp_stream_get(mailbox, SP_SAME | SP_UNLOCKED);
 
446
                if(stream){
 
447
                    dprint((9,
 
448
                            "pine_mail_open: attempting to re-use stream\n"));
 
449
                }
 
450
                else{
 
451
                    /*
 
452
                     * We'll take any UNLOCKED stream and re-use it.
 
453
                     */
 
454
                    stream = sp_stream_get(mailbox, 0);
 
455
                    if(stream){
 
456
                        /*
 
457
                         * We will close this stream and use the empty slot
 
458
                         * that that creates.
 
459
                         */
 
460
                        dprint((9,
 
461
            "pine_mail_open: close an UNLOCKED stream and re-use the slot\n"));
 
462
                        pine_mail_actually_close(stream);
 
463
                        stream = NULL;
 
464
                    }
 
465
                    else{
 
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"));
 
468
                        }
 
469
                        else{
 
470
                          dprint((9, "pine_mail_open: no caching, max_remstream == 0\n"));
 
471
                        }
 
472
 
 
473
                        usepool = 0;
 
474
                        tempuse = 0;
 
475
                        if(permlocked){
 
476
                            permlocked = 0;
 
477
                            dprint((2,
 
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));
 
482
                        }
 
483
                    }
 
484
                }
 
485
            }
 
486
        }
 
487
        else{
 
488
            dprint((9,
 
489
                    "pine_mail_open: there is an empty slot to use\n"));
 
490
        }
 
491
 
 
492
        /*
 
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.
 
498
         */
 
499
        if(openflags & OP_HALFOPEN && stream){
 
500
            dprint((9,
 
501
             "pine_mail_open: asked for HALFOPEN so returning stream as is\n"));
 
502
            sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
 
503
            dprint((7,
 
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",
 
509
                       stream->gensym));
 
510
            }
 
511
 
 
512
            stream->silent = (openflags & OP_SILENT) ? T : NIL;
 
513
 
 
514
            return(stream);
 
515
        }
 
516
 
 
517
        /*
 
518
         * We're going to SELECT another folder with this open stream.
 
519
         */
 
520
        if(stream){
 
521
            /*
 
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
 
525
             * for that new mail.
 
526
             */
 
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));
 
532
 
 
533
            if(stream && stream->dtb && stream->dtb->name
 
534
               && !strcmp(stream->dtb->name, "imap")){
 
535
                dprint((7,
 
536
                   "pine_mail_open: cancel idle timer: TAG %08lx (%s)\n",
 
537
                   stream->gensym, debug_time(1, ps_global->debug_timestamp)));
 
538
            }
 
539
 
 
540
            /*
 
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
 
544
             * the new folder.
 
545
             */
 
546
            sp_free((PER_STREAM_S **) &stream->sparep);
 
547
        }
 
548
    }
 
549
 
 
550
    /*
 
551
     * When we pass a stream to mail_open, it will either re-use it or
 
552
     * close it.
 
553
     */
 
554
    retstream = mail_open(stream, mailbox, openflags);
 
555
 
 
556
    if(retstream){
 
557
 
 
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));
 
559
 
 
560
        /*
 
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.
 
565
         */
 
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);
 
570
        }
 
571
 
 
572
        if(retstream && retstream->dtb && retstream->dtb->name
 
573
           && !strcmp(retstream->dtb->name, "imap")){
 
574
            dprint((7, "pine_mail_open: next TAG %08lx\n",
 
575
                   retstream->gensym));
 
576
        }
 
577
 
 
578
        /*
 
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
 
589
         * rare.
 
590
         */
 
591
        if(IS_NEWS(retstream)){
 
592
            usepool = 0;
 
593
            tempuse = 0;
 
594
        }
 
595
 
 
596
        /* make sure the message map has been instantiated */
 
597
        (void) sp_msgmap(retstream);
 
598
 
 
599
        flags = SP_LOCKED
 
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);
 
605
 
 
606
        sp_flag(retstream, flags);
 
607
        sp_set_recent_since_visited(retstream, retstream->recent);
 
608
 
 
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)));
 
613
 
 
614
        if(sp_add(retstream, usepool) != 0 && usepool){
 
615
            usepool = 0;
 
616
            tempuse = 0;
 
617
            flags = SP_LOCKED
 
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);
 
623
 
 
624
            sp_flag(retstream, flags);
 
625
            (void) sp_add(retstream, usepool);
 
626
        }
 
627
 
 
628
 
 
629
        /*
 
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.
 
638
         */
 
639
        if(F_ON(F_FAKE_NEW_IN_NEWS, ps_global) &&
 
640
           retstream->nmsgs > 0 && IS_NEWS(retstream)){
 
641
            char         *seq;
 
642
            long          i, mflags = ST_SET;
 
643
            MESSAGECACHE *mc;
 
644
 
 
645
            /*
 
646
             * Search for !recent messages to set the searched bit for
 
647
             * those messages we want to change. Then we'll flip the bits.
 
648
             */
 
649
            (void)count_flagged(retstream, F_UNRECENT);
 
650
 
 
651
            for(i = 1L; i <= retstream->nmsgs; i++)
 
652
              if((mc = mail_elt(retstream,i)) && mc->searched)
 
653
                mc->sequence = 1;
 
654
              else
 
655
                mc->sequence = 0;
 
656
 
 
657
            if(!is_imap_stream(retstream))
 
658
                mflags |= ST_SILENT;
 
659
            if((seq = build_sequence(retstream, NULL, NULL)) != NULL){
 
660
                mail_flag(retstream, seq, "\\SEEN", mflags);
 
661
                fs_give((void **)&seq);
 
662
            }
 
663
        }
 
664
    }
 
665
 
 
666
    return(retstream);
 
667
}
 
668
 
 
669
 
 
670
void
 
671
reset_stream_view_state(MAILSTREAM *stream)
 
672
{
 
673
    MSGNO_S *mm;
 
674
 
 
675
    if(!stream)
 
676
      return;
 
677
 
 
678
    mm = sp_msgmap(stream);
 
679
 
 
680
    if(!mm)
 
681
      return;
 
682
 
 
683
    sp_set_viewing_a_thread(stream, 0);
 
684
    sp_set_need_to_rethread(stream, 0);
 
685
 
 
686
    mn_reset_cur(mm, stream->nmsgs > 0L ? stream->nmsgs : 0L);  /* default */
 
687
 
 
688
    mm->visible_threads = -1L;
 
689
    mm->top             = 0L;
 
690
    mm->max_thrdno      = 0L;
 
691
    mm->top_after_thrd  = 0L;
 
692
 
 
693
    mn_set_mansort(mm, 0);
 
694
 
 
695
    /*
 
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.
 
699
     */
 
700
    if(any_lflagged(mm, MN_SLCT | MN_HIDE)){
 
701
        long i;
 
702
 
 
703
        for(i = 1L; i <= mn_get_total(mm); i++)
 
704
          set_lflag(stream, mm, i, MN_SLCT | MN_HIDE, 0);
 
705
    }
 
706
 
 
707
    /*
 
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
 
710
     * it here.
 
711
     */
 
712
}
 
713
 
 
714
 
 
715
/*
 
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
 
720
 *                                   SP_PERMLOCKED
 
721
 *                                   SP_USERFLDR
 
722
 *                                   SP_INBOX
 
723
 * We allow turning off (but not on) SP_TEMPUSE
 
724
 */
 
725
void
 
726
carefully_reset_sp_flags(MAILSTREAM *stream, long unsigned int flags)
 
727
{
 
728
    if(sp_flags(stream) != flags){
 
729
        /* allow turning on but not off */
 
730
        if(sp_flags(stream) & SP_LOCKED && !(flags & SP_LOCKED))
 
731
          flags |= SP_LOCKED;
 
732
 
 
733
        if(sp_flags(stream) & SP_PERMLOCKED && !(flags & SP_PERMLOCKED))
 
734
          flags |= SP_PERMLOCKED;
 
735
 
 
736
        if(sp_flags(stream) & SP_USERFLDR && !(flags & SP_USERFLDR))
 
737
          flags |= SP_USERFLDR;
 
738
 
 
739
        if(sp_flags(stream) & SP_INBOX && !(flags & SP_INBOX))
 
740
          flags |= SP_INBOX;
 
741
 
 
742
 
 
743
        /* allow turning off but not on */
 
744
        if(!(sp_flags(stream) & SP_TEMPUSE) && flags & SP_TEMPUSE)
 
745
          flags &= ~SP_TEMPUSE;
 
746
        
 
747
 
 
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);
 
751
 
 
752
 
 
753
        if(sp_flags(stream) != flags)
 
754
          sp_flag(stream, flags);
 
755
    }
 
756
}
 
757
 
 
758
 
 
759
/*
 
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.
 
763
 */
 
764
long
 
765
pine_mail_create(MAILSTREAM *stream, char *mailbox)
 
766
{
 
767
    MAILSTREAM *ourstream = NULL;
 
768
    long        return_val;
 
769
    long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
 
770
    char        source[MAILTMPLEN], *target = NULL;
 
771
    DRIVER     *d;
 
772
 
 
773
    dprint((7, "pine_mail_create: creating \"%s\"%s\n", 
 
774
               mailbox ? mailbox : "(NULL)",
 
775
               stream ? "" : " (stream was NULL)"));
 
776
 
 
777
    if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
 
778
        mailbox = target;
 
779
        dprint((7,
 
780
                   "pine_mail_create: #move special case, creating \"%s\"\n", 
 
781
                   mailbox ? mailbox : "(NULL)"));
 
782
    }
 
783
 
 
784
    /*
 
785
     * We don't really need this anymore, since we are now using IMAPTRYALT.
 
786
     * We'll leave it since it works.
 
787
     */
 
788
    if((F_ON(F_PREFER_ALT_AUTH, ps_global)
 
789
       || (ps_global->debug_imap > 3 || ps_global->debugmem))){
 
790
 
 
791
        if((d = mail_valid (NIL, mailbox, (char *) NIL))
 
792
           && !strcmp(d->name, "imap")){
 
793
 
 
794
            if(F_ON(F_PREFER_ALT_AUTH, ps_global))
 
795
              openflags |= OP_TRYALT;
 
796
        }
 
797
    }
 
798
 
 
799
    if(!stream)
 
800
      stream = sp_stream_get(mailbox, SP_MATCH);
 
801
    if(!stream)
 
802
      stream = sp_stream_get(mailbox, SP_SAME);
 
803
 
 
804
    if(!stream){
 
805
        /*
 
806
         * It is only useful to open a stream in the imap case.
 
807
         */
 
808
        if((d = mail_valid (NIL, mailbox, (char *) NIL))
 
809
           && !strcmp(d->name, "imap")){
 
810
 
 
811
            stream = pine_mail_open(NULL, mailbox, openflags, NULL);
 
812
            ourstream = stream;
 
813
        }
 
814
    }
 
815
 
 
816
    return_val = mail_create(stream, mailbox);
 
817
 
 
818
    if(ourstream)
 
819
      pine_mail_close(ourstream);
 
820
 
 
821
    return(return_val);
 
822
}
 
823
 
 
824
 
 
825
/*
 
826
 * Pine wrapper around mail_delete.
 
827
 */
 
828
long
 
829
pine_mail_delete(MAILSTREAM *stream, char *mailbox)
 
830
{
 
831
    MAILSTREAM *ourstream = NULL;
 
832
    long        return_val;
 
833
    long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
 
834
    char        source[MAILTMPLEN], *target = NULL;
 
835
    DRIVER     *d;
 
836
 
 
837
    dprint((7, "pine_mail_delete: deleting \"%s\"%s\n", 
 
838
               mailbox ? mailbox : "(NULL)",
 
839
               stream ? "" : " (stream was NULL)"));
 
840
 
 
841
    if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
 
842
        mailbox = target;
 
843
        dprint((7,
 
844
                   "pine_mail_delete: #move special case, deleting \"%s\"\n", 
 
845
                   mailbox ? mailbox : "(NULL)"));
 
846
    }
 
847
 
 
848
    /*
 
849
     * We don't really need this anymore, since we are now using IMAPTRYALT.
 
850
     */
 
851
    if((F_ON(F_PREFER_ALT_AUTH, ps_global)
 
852
       || (ps_global->debug_imap > 3 || ps_global->debugmem))){
 
853
 
 
854
        if((d = mail_valid (NIL, mailbox, (char *) NIL))
 
855
           && !strcmp(d->name, "imap")){
 
856
 
 
857
            if(F_ON(F_PREFER_ALT_AUTH, ps_global))
 
858
              openflags |= OP_TRYALT;
 
859
        }
 
860
    }
 
861
 
 
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);
 
865
        stream = NULL;
 
866
    }
 
867
 
 
868
    if(!stream)
 
869
      stream = sp_stream_get(mailbox, SP_SAME);
 
870
 
 
871
    if(!stream){
 
872
        /*
 
873
         * It is only useful to open a stream in the imap case.
 
874
         */
 
875
        if((d = mail_valid (NIL, mailbox, (char *) NIL))
 
876
           && !strcmp(d->name, "imap")){
 
877
 
 
878
            stream = pine_mail_open(NULL, mailbox, openflags, NULL);
 
879
            ourstream = stream;
 
880
        }
 
881
    }
 
882
 
 
883
    return_val = mail_delete(stream, mailbox);
 
884
 
 
885
    if(ourstream)
 
886
      pine_mail_close(ourstream);
 
887
 
 
888
    return(return_val);
 
889
}
 
890
 
 
891
 
 
892
/*
 
893
 * Pine wrapper around mail_append.
 
894
 */
 
895
long
 
896
pine_mail_append_full(MAILSTREAM *stream, char *mailbox, char *flags, char *date, STRING *message)
 
897
{
 
898
    MAILSTREAM *ourstream = NULL;
 
899
    long        return_val;
 
900
    long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
 
901
    char        source[MAILTMPLEN], *target = NULL;
 
902
    DRIVER     *d;
 
903
 
 
904
    dprint((7, "pine_mail_append_full: appending to \"%s\"%s\n", 
 
905
               mailbox ? mailbox : "(NULL)",
 
906
               stream ? "" : " (stream was NULL)"));
 
907
 
 
908
    if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
 
909
        mailbox = target;
 
910
        dprint((7,
 
911
           "pine_mail_append_full: #move special case, appending to \"%s\"\n", 
 
912
                   mailbox ? mailbox : "(NULL)"));
 
913
    }
 
914
 
 
915
    /*
 
916
     * We don't really need this anymore, since we are now using IMAPTRYALT.
 
917
     */
 
918
    if((F_ON(F_PREFER_ALT_AUTH, ps_global)
 
919
       || (ps_global->debug_imap > 3 || ps_global->debugmem))){
 
920
 
 
921
        if((d = mail_valid (NIL, mailbox, (char *) NIL))
 
922
           && !strcmp(d->name, "imap")){
 
923
 
 
924
            if(F_ON(F_PREFER_ALT_AUTH, ps_global))
 
925
              openflags |= OP_TRYALT;
 
926
        }
 
927
    }
 
928
 
 
929
    if(!stream)
 
930
      stream = sp_stream_get(mailbox, SP_MATCH);
 
931
    if(!stream)
 
932
      stream = sp_stream_get(mailbox, SP_SAME);
 
933
 
 
934
    if(!stream){
 
935
        /*
 
936
         * It is only useful to open a stream in the imap case.
 
937
         */
 
938
        if((d = mail_valid (NIL, mailbox, (char *) NIL))
 
939
           && !strcmp(d->name, "imap")){
 
940
 
 
941
            stream = pine_mail_open(NULL, mailbox, openflags, NULL);
 
942
            ourstream = stream;
 
943
        }
 
944
    }
 
945
 
 
946
    return_val = mail_append_full(stream, mailbox, flags, date, message);
 
947
 
 
948
    if(ourstream)
 
949
      pine_mail_close(ourstream);
 
950
 
 
951
    return(return_val);
 
952
}
 
953
 
 
954
 
 
955
/*
 
956
 * Pine wrapper around mail_append.
 
957
 */
 
958
long
 
959
pine_mail_append_multiple(MAILSTREAM *stream, char *mailbox, append_t af,
 
960
                          APPENDPACKAGE *data, MAILSTREAM *not_this_stream)
 
961
{
 
962
    MAILSTREAM *ourstream = NULL;
 
963
    long        return_val;
 
964
    long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
 
965
    char        source[MAILTMPLEN], *target = NULL;
 
966
    DRIVER     *d;
 
967
    int         we_blocked_reuse = 0;
 
968
 
 
969
    dprint((7, "pine_mail_append_multiple: appending to \"%s\"%s\n", 
 
970
               mailbox ? mailbox : "(NULL)",
 
971
               stream ? "" : " (stream was NULL)"));
 
972
 
 
973
    if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
 
974
        mailbox = target;
 
975
        dprint((7,
 
976
         "pine_mail_append_multiple: #move special case, appending to \"%s\"\n",
 
977
                   mailbox ? mailbox : "(NULL)"));
 
978
    }
 
979
 
 
980
    if((F_ON(F_PREFER_ALT_AUTH, ps_global)
 
981
       || (ps_global->debug_imap > 3 || ps_global->debugmem))){
 
982
 
 
983
        if((d = mail_valid (NIL, mailbox, (char *) NIL))
 
984
           && !strcmp(d->name, "imap")){
 
985
 
 
986
            if(F_ON(F_PREFER_ALT_AUTH, ps_global))
 
987
              openflags |= OP_TRYALT;
 
988
        }
 
989
    }
 
990
 
 
991
    /*
 
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.
 
1003
     */
 
1004
    if(sp_flagged(not_this_stream, SP_USEPOOL)){
 
1005
        we_blocked_reuse++;
 
1006
        sp_unflag(not_this_stream, SP_USEPOOL);
 
1007
    }
 
1008
 
 
1009
    if(!stream)
 
1010
      stream = sp_stream_get(mailbox, SP_MATCH);
 
1011
    if(!stream)
 
1012
      stream = sp_stream_get(mailbox, SP_SAME);
 
1013
 
 
1014
    if(!stream){
 
1015
        /*
 
1016
         * It is only useful to open a stream in the imap case.
 
1017
         */
 
1018
        if((d = mail_valid (NIL, mailbox, (char *) NIL))
 
1019
           && !strcmp(d->name, "imap")){
 
1020
 
 
1021
            stream = pine_mail_open(NULL, mailbox, openflags, NULL);
 
1022
            ourstream = stream;
 
1023
        }
 
1024
    }
 
1025
 
 
1026
    if(we_blocked_reuse)
 
1027
      sp_set_flags(not_this_stream, sp_flags(not_this_stream) | SP_USEPOOL);
 
1028
 
 
1029
    return_val = mail_append_multiple(stream, mailbox, af, (void *) data);
 
1030
 
 
1031
    if(ourstream)
 
1032
      pine_mail_close(ourstream);
 
1033
 
 
1034
    return(return_val);
 
1035
}
 
1036
 
 
1037
 
 
1038
/*
 
1039
 * Pine wrapper around mail_copy.
 
1040
 */
 
1041
long
 
1042
pine_mail_copy_full(MAILSTREAM *stream, char *sequence, char *mailbox, long int options)
 
1043
{
 
1044
    MAILSTREAM *ourstream = NULL;
 
1045
    long        return_val;
 
1046
    long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
 
1047
    char        source[MAILTMPLEN], *target = NULL;
 
1048
    DRIVER     *d;
 
1049
 
 
1050
    dprint((7, "pine_mail_copy_full: copying to \"%s\"%s\n", 
 
1051
               mailbox ? mailbox : "(NULL)",
 
1052
               stream ? "" : " (stream was NULL)"));
 
1053
 
 
1054
    if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
 
1055
        mailbox = target;
 
1056
        dprint((7,
 
1057
           "pine_mail_copy_full: #move special case, copying to \"%s\"\n", 
 
1058
                   mailbox ? mailbox : "(NULL)"));
 
1059
    }
 
1060
 
 
1061
    /*
 
1062
     * We don't really need this anymore, since we are now using IMAPTRYALT.
 
1063
     */
 
1064
    if((F_ON(F_PREFER_ALT_AUTH, ps_global)
 
1065
       || (ps_global->debug_imap > 3 || ps_global->debugmem))){
 
1066
 
 
1067
        if((d = mail_valid (NIL, mailbox, (char *) NIL))
 
1068
           && !strcmp(d->name, "imap")){
 
1069
 
 
1070
            if(F_ON(F_PREFER_ALT_AUTH, ps_global))
 
1071
              openflags |= OP_TRYALT;
 
1072
        }
 
1073
    }
 
1074
 
 
1075
    if(!stream)
 
1076
      stream = sp_stream_get(mailbox, SP_MATCH);
 
1077
    if(!stream)
 
1078
      stream = sp_stream_get(mailbox, SP_SAME);
 
1079
 
 
1080
    if(!stream){
 
1081
        /*
 
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.
 
1087
         */
 
1088
        if((d = mail_valid (NIL, mailbox, (char *) NIL))
 
1089
           && !strcmp(d->name, "imap")){
 
1090
 
 
1091
            stream = pine_mail_open(NULL, mailbox, openflags, NULL);
 
1092
            ourstream = stream;
 
1093
        }
 
1094
    }
 
1095
 
 
1096
    return_val = mail_copy_full(stream, sequence, mailbox, options);
 
1097
 
 
1098
    if(ourstream)
 
1099
      pine_mail_close(ourstream);
 
1100
 
 
1101
    return(return_val);
 
1102
}
 
1103
 
 
1104
 
 
1105
/*
 
1106
 * Pine wrapper around mail_rename.
 
1107
 */
 
1108
long
 
1109
pine_mail_rename(MAILSTREAM *stream, char *old, char *new)
 
1110
{
 
1111
    MAILSTREAM *ourstream = NULL;
 
1112
    long        return_val;
 
1113
    long        openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
 
1114
    DRIVER     *d;
 
1115
 
 
1116
    dprint((7, "pine_mail_rename(%s,%s)\n", old ? old : "",
 
1117
                new ? new : ""));
 
1118
 
 
1119
    /*
 
1120
     * We don't really need this anymore, since we are now using IMAPTRYALT.
 
1121
     */
 
1122
    if((F_ON(F_PREFER_ALT_AUTH, ps_global)
 
1123
       || (ps_global->debug_imap > 3 || ps_global->debugmem))){
 
1124
 
 
1125
        if((d = mail_valid (NIL, old, (char *) NIL))
 
1126
           && !strcmp(d->name, "imap")){
 
1127
 
 
1128
            if(F_ON(F_PREFER_ALT_AUTH, ps_global))
 
1129
              openflags |= OP_TRYALT;
 
1130
        }
 
1131
    }
 
1132
 
 
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);
 
1136
        stream = NULL;
 
1137
    }
 
1138
 
 
1139
    if(!stream)
 
1140
      stream = sp_stream_get(old, SP_SAME);
 
1141
 
 
1142
    if(!stream){
 
1143
        /*
 
1144
         * It is only useful to open a stream in the imap case.
 
1145
         */
 
1146
        if((d = mail_valid (NIL, old, (char *) NIL))
 
1147
           && !strcmp(d->name, "imap")){
 
1148
 
 
1149
            stream = pine_mail_open(NULL, old, openflags, NULL);
 
1150
            ourstream = stream;
 
1151
        }
 
1152
    }
 
1153
 
 
1154
    return_val = mail_rename(stream, old, new);
 
1155
 
 
1156
    if(ourstream)
 
1157
      pine_mail_close(ourstream);
 
1158
 
 
1159
    return(return_val);
 
1160
}
 
1161
 
 
1162
 
 
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.
 
1166
  ----*/
 
1167
void
 
1168
pine_mail_close(MAILSTREAM *stream)
 
1169
{
 
1170
    unsigned long uid_last, last_uid;
 
1171
    int refcnt;
 
1172
 
 
1173
    if(!stream)
 
1174
      return;
 
1175
 
 
1176
    dprint((7, "pine_mail_close: %s (%s)\n", 
 
1177
               stream && stream->mailbox ? stream->mailbox : "(NULL)",
 
1178
               debug_time(1, ps_global->debug_timestamp)));
 
1179
 
 
1180
    if(sp_flagged(stream, SP_USEPOOL) && !sp_dead_stream(stream)){
 
1181
 
 
1182
        refcnt = sp_ref_cnt(stream);
 
1183
        dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
 
1184
 
 
1185
        /*
 
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.
 
1189
         */
 
1190
#if 0
 
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);
 
1195
            else{
 
1196
              dprint((7,
 
1197
                     "pine_mail_close: dont think we need to checkpoint\n"));
 
1198
            }
 
1199
        }
 
1200
#endif
 
1201
 
 
1202
        /*
 
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.
 
1206
         */
 
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;
 
1211
 
 
1212
        sp_set_saved_uid_validity(stream, stream->uid_validity);
 
1213
        sp_set_saved_uid_last(stream, uid_last);
 
1214
 
 
1215
        /*
 
1216
         * If the reference count is down to 0, unlock it.
 
1217
         * In any case, don't actually do any real closing.
 
1218
         */
 
1219
        if(refcnt > 0)
 
1220
          sp_set_ref_cnt(stream, refcnt-1);
 
1221
 
 
1222
        refcnt = sp_ref_cnt(stream);
 
1223
        dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
 
1224
        if(refcnt <= 0){
 
1225
            dprint((7,
 
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));
 
1229
 
 
1230
            /*
 
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.
 
1237
             */
 
1238
            sp_unflag(stream, SP_LOCKED | SP_PERMLOCKED | SP_USERFLDR);
 
1239
        }
 
1240
        else{
 
1241
            dprint((7, "pine_mail_close: ref cnt is now %d\n", 
 
1242
                       refcnt));
 
1243
        }
 
1244
    }
 
1245
    else{
 
1246
        dprint((7, "pine_mail_close: %s\n", 
 
1247
                   sp_flagged(stream, SP_USEPOOL) ? "dead stream" : "no pool"));
 
1248
 
 
1249
        refcnt = sp_ref_cnt(stream);
 
1250
        dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
 
1251
 
 
1252
        /*
 
1253
         * If the reference count is down to 0, unlock it.
 
1254
         * In any case, don't actually do any real closing.
 
1255
         */
 
1256
        if(refcnt > 0)
 
1257
          sp_set_ref_cnt(stream, refcnt-1);
 
1258
 
 
1259
        refcnt = sp_ref_cnt(stream);
 
1260
        if(refcnt <= 0){
 
1261
            pine_mail_actually_close(stream);
 
1262
        }
 
1263
        else{
 
1264
            dprint((7, "pine_mail_close: ref cnt is now %d\n", 
 
1265
                       refcnt));
 
1266
        }
 
1267
 
 
1268
    }
 
1269
}
 
1270
 
 
1271
 
 
1272
void
 
1273
pine_mail_actually_close(MAILSTREAM *stream)
 
1274
{
 
1275
    int  i;
 
1276
    long n;
 
1277
 
 
1278
    if(!stream)
 
1279
      return;
 
1280
 
 
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)));
 
1285
 
 
1286
        sp_set_closing(stream, 1);
 
1287
        
 
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));
 
1294
        sp_delete(stream);
 
1295
 
 
1296
        /*
 
1297
         * let sp_free_callback() free the sp_s stuff and the callbacks to
 
1298
         * free_pine_elt free the per-elt pine stuff.
 
1299
         */
 
1300
        mail_close(stream);
 
1301
    }
 
1302
}
 
1303
 
 
1304
 
 
1305
/*
 
1306
 * If we haven't used a stream for a while, we may want to logout.
 
1307
 */
 
1308
void
 
1309
maybe_kill_old_stream(MAILSTREAM *stream)
 
1310
{
 
1311
#define KILL_IF_IDLE_TIME (25 * 60)
 
1312
    if(stream
 
1313
       && !sp_flagged(stream, SP_LOCKED)
 
1314
       && !sp_flagged(stream, SP_USERFLDR)
 
1315
       && time(0) - sp_last_use_time(stream) > KILL_IF_IDLE_TIME){
 
1316
 
 
1317
        dprint((7,
 
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))));
 
1322
 
 
1323
        /*
 
1324
         * Another thing we could do here instead is to unselect the
 
1325
         * mailbox, leaving the stream open to the server.
 
1326
         */
 
1327
        pine_mail_actually_close(stream);
 
1328
    }
 
1329
}
 
1330
 
 
1331
 
 
1332
/*
 
1333
 * Catch searches that don't need to go to the server.
 
1334
 * (Not anymore, now c-client does this for us.)
 
1335
 */
 
1336
long
 
1337
pine_mail_search_full(MAILSTREAM *stream, char *charset,
 
1338
                      struct search_program *pgm, long int flags)
 
1339
{
 
1340
    return(stream ? mail_search_full(stream, charset, pgm, flags) : NIL);
 
1341
}
 
1342
 
 
1343
 
 
1344
void
 
1345
pine_mail_fetch_flags(MAILSTREAM *stream, char *sequence, long int flags)
 
1346
{
 
1347
    ps_global->dont_count_flagchanges = 1;
 
1348
    mail_fetch_flags(stream, sequence, flags);
 
1349
    ps_global->dont_count_flagchanges = 0;
 
1350
}
 
1351
 
 
1352
 
 
1353
ENVELOPE *
 
1354
pine_mail_fetchenvelope(MAILSTREAM *stream, long unsigned int msgno)
 
1355
{
 
1356
    ENVELOPE *env = NULL;
 
1357
 
 
1358
    ps_global->dont_count_flagchanges = 1;
 
1359
    if(stream && msgno > 0L && msgno <= stream->nmsgs)
 
1360
      env = mail_fetchenvelope(stream, msgno);
 
1361
 
 
1362
    ps_global->dont_count_flagchanges = 0;
 
1363
    return(env);
 
1364
}
 
1365
 
 
1366
 
 
1367
ENVELOPE *
 
1368
pine_mail_fetch_structure(MAILSTREAM *stream, long unsigned int msgno,
 
1369
                          struct mail_bodystruct **body, long int flags)
 
1370
{
 
1371
    ENVELOPE *env = NULL;
 
1372
 
 
1373
    ps_global->dont_count_flagchanges = 1;
 
1374
    if(stream && msgno > 0L && msgno <= stream->nmsgs)
 
1375
      env = mail_fetch_structure(stream, msgno, body, flags);
 
1376
 
 
1377
    ps_global->dont_count_flagchanges = 0;
 
1378
    return(env);
 
1379
}
 
1380
 
 
1381
 
 
1382
ENVELOPE *
 
1383
pine_mail_fetchstructure(MAILSTREAM *stream, long unsigned int msgno, struct mail_bodystruct **body)
 
1384
{
 
1385
    ENVELOPE *env = NULL;
 
1386
 
 
1387
    ps_global->dont_count_flagchanges = 1;
 
1388
    if(stream && msgno > 0L && msgno <= stream->nmsgs)
 
1389
      env = mail_fetchstructure(stream, msgno, body);
 
1390
 
 
1391
    ps_global->dont_count_flagchanges = 0;
 
1392
    return(env);
 
1393
}
 
1394
 
 
1395
 
 
1396
/*
 
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.
 
1400
 */
 
1401
char *
 
1402
pine_mail_fetch_body(MAILSTREAM *stream, long unsigned int msgno, char *section,
 
1403
                     long unsigned int *len, long int flags)
 
1404
{
 
1405
#ifdef _WINDOWS
 
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));
 
1409
    else
 
1410
#endif
 
1411
    return(mail_fetch_body(stream, msgno, section, len, flags));
 
1412
}
 
1413
 
 
1414
/*
 
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.
 
1418
 */
 
1419
char *
 
1420
pine_mail_fetch_text(MAILSTREAM *stream, long unsigned int msgno, char *section,
 
1421
                     long unsigned int *len, long int flags)
 
1422
{
 
1423
#ifdef _WINDOWS
 
1424
    if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global))
 
1425
      return(pine_mail_partial_fetch_wrapper(stream, msgno,
 
1426
                                             section, len, flags, 
 
1427
                                             0, NULL, 1));
 
1428
    else
 
1429
#endif
 
1430
      return(mail_fetch_text(stream, msgno, section, len, flags));
 
1431
}
 
1432
 
 
1433
 
 
1434
/*
 
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
 
1442
 *                  get_n_bytes).
 
1443
 *    is_text_fetch - 
 
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
 
1446
 */
 
1447
char *
 
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)
 
1452
{
 
1453
    BODY *body;
 
1454
    unsigned long size, firstbyte, lastbyte;
 
1455
    void *old_gets;
 
1456
    FETCH_READC_S *pftc;
 
1457
    char imap_cache_section[MAILTMPLEN];
 
1458
    SIZEDTEXT new_text;
 
1459
    MESSAGECACHE *mc;
 
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);
 
1464
 
 
1465
    fetch_full = is_text_fetch ? mail_fetch_text : mail_fetch_body;
 
1466
    fetch_partial = is_text_fetch ? mail_partial_text : mail_partial_body;
 
1467
    if(str_to_free)
 
1468
      *str_to_free = NULL;
 
1469
#ifdef _WINDOWS
 
1470
    if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global) || get_n_bytes){
 
1471
#else
 
1472
    if(get_n_bytes){
 
1473
#endif /* _WINDOWS */
 
1474
        if(section && *section)
 
1475
          body = mail_body(stream, msgno, (unsigned char *) section);
 
1476
        else
 
1477
          pine_mail_fetch_structure(stream, msgno, &body, flags);
 
1478
        if(!body)
 
1479
          return NULL;
 
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;
 
1488
 
 
1489
        /* 
 
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
 
1495
         */
 
1496
        if(is_text_fetch)
 
1497
          snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s%sTEXT", MAILTMPLEN - 10,
 
1498
                  section && *section ? section : "",
 
1499
                  section && *section ? "." : "");
 
1500
        else
 
1501
          snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s", MAILTMPLEN - 10,
 
1502
                  section && *section ? section : "");
 
1503
 
 
1504
        if(modern_imap_stream(stream)
 
1505
#ifdef _WINDOWS
 
1506
           && ((size > AVOID_MICROSOFT_SSL_CHUNKING_BUG)
 
1507
               || (get_n_bytes && size > get_n_bytes))
 
1508
#else
 
1509
           && (get_n_bytes && size > get_n_bytes)
 
1510
#endif /* _WINDOWS */
 
1511
           && !imap_cache(stream, msgno, imap_cache_section,
 
1512
                          NULL, NULL)){
 
1513
            if(get_n_bytes == 0){
 
1514
              dprint((8, 
 
1515
    "fetch_wrapper: doing partial fetching to work around microsoft bug\n"));
 
1516
            }
 
1517
            else{
 
1518
              dprint((8,
 
1519
    "fetch_wrapper: partial fetching due to %lu get_n_bytes\n", get_n_bytes));
 
1520
            }
 
1521
            pftc = (FETCH_READC_S *)fs_get(sizeof(FETCH_READC_S));
 
1522
            memset(g_pft_desc = pftc, 0, sizeof(FETCH_READC_S));
 
1523
#ifdef _WINDOWS
 
1524
            if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global)){
 
1525
                if(get_n_bytes)
 
1526
                  pftc->chunksize = MIN(get_n_bytes,
 
1527
                                        AVOID_MICROSOFT_SSL_CHUNKING_BUG);
 
1528
                else
 
1529
                  pftc->chunksize = AVOID_MICROSOFT_SSL_CHUNKING_BUG;
 
1530
            }
 
1531
            else
 
1532
#endif /* _WINDOWS */
 
1533
            pftc->chunksize = get_n_bytes;
 
1534
 
 
1535
            pftc->chunk = (char *) fs_get((pftc->chunksize+1)
 
1536
                                          * sizeof(char));
 
1537
            pftc->cache = so_get(CharStar, NULL, EDIT_ACCESS);
 
1538
            pftc->read = 0L;
 
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 */
 
1543
            do{
 
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;
 
1549
                }
 
1550
                (*fetch_partial)(stream, msgno, section, firstbyte,
 
1551
                                 pftc->chunksize, flags);
 
1552
 
 
1553
                if(pftc->read != lastbyte)
 
1554
                  break;
 
1555
            } while((pftc->read ==  lastbyte)
 
1556
                    && (!get_n_bytes || (pftc->read < get_n_bytes)));
 
1557
            dprint((8,
 
1558
                       "fetch_wrapper: anticipated size=%lu read=%lu\n",
 
1559
                       size, pftc->read));
 
1560
            mail_parameters(stream, SET_GETS, old_gets);
 
1561
            new_text.size = pftc->read;
 
1562
            new_text.data = (unsigned char *)so_text(pftc->cache);
 
1563
 
 
1564
            if(get_n_bytes && pftc->read == get_n_bytes){
 
1565
                /* 
 
1566
                 * don't write to cache if we're only dealing with
 
1567
                 * partial text.
 
1568
                 */
 
1569
                if(!str_to_free)
 
1570
                  panic("Programmer botch: partial fetch attempt w/o string pointer");
 
1571
                else
 
1572
                  *str_to_free = (char *) new_text.data;
 
1573
            }
 
1574
            else {
 
1575
                /* ugh, rewrite string in case it got stomped on last call */
 
1576
                if(is_text_fetch)
 
1577
                  snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s%sTEXT", MAILTMPLEN - 10,
 
1578
                          section && *section ? section : "",
 
1579
                          section && *section ? "." : "");
 
1580
                else
 
1581
                  snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s", MAILTMPLEN - 10,
 
1582
                          section && *section ? section : "");
 
1583
 
 
1584
                imap_cache(stream, msgno, imap_cache_section, NULL, &new_text);
 
1585
            }
 
1586
 
 
1587
            pftc->cache->txt = (void *)NULL;
 
1588
            so_give(&pftc->cache);
 
1589
            fs_give((void **)&pftc->chunk);
 
1590
            fs_give((void **)&pftc);
 
1591
            g_pft_desc = NULL;
 
1592
            if(len)
 
1593
              *len = new_text.size;
 
1594
            return ((char *)new_text.data);
 
1595
        }
 
1596
        else
 
1597
          return((*fetch_full)(stream, msgno, section, len, flags));
 
1598
    }
 
1599
    else
 
1600
      return((*fetch_full)(stream, msgno, section, len, flags));
 
1601
}
 
1602
 
 
1603
/*
 
1604
 * c-client callback that handles getting the text
 
1605
 */
 
1606
char *
 
1607
partial_text_gets(readfn_t f, void *stream, long unsigned int size, GETS_DATA *md)
 
1608
{
 
1609
    unsigned long n;
 
1610
 
 
1611
    n = MIN(g_pft_desc->chunksize, size);
 
1612
    g_pft_desc->read +=n;
 
1613
 
 
1614
    (*f) (stream, n, g_pft_desc->chunk);
 
1615
 
 
1616
    if(g_pft_desc->cache)
 
1617
      so_nputs(g_pft_desc->cache, g_pft_desc->chunk, (long) n);
 
1618
 
 
1619
 
 
1620
    return(NULL);
 
1621
}
 
1622
 
 
1623
 
 
1624
/*
 
1625
 * Pings the stream. Returns 0 if the stream is dead, non-zero otherwise.
 
1626
 */
 
1627
long
 
1628
pine_mail_ping(MAILSTREAM *stream)
 
1629
{
 
1630
    time_t now;
 
1631
    long   ret = 0L;
 
1632
 
 
1633
    if(!sp_dead_stream(stream)){
 
1634
        ret = mail_ping(stream);
 
1635
        if(ret && sp_dead_stream(stream))
 
1636
          ret = 0L;
 
1637
    }
 
1638
 
 
1639
    if(ret){
 
1640
        now = time(0);
 
1641
        sp_set_last_ping(stream, now);
 
1642
        sp_set_last_expunged_reaper(stream, now);
 
1643
    }
 
1644
 
 
1645
    return(ret);
 
1646
}
 
1647
 
 
1648
 
 
1649
void
 
1650
pine_mail_check(MAILSTREAM *stream)
 
1651
{
 
1652
    reset_check_point(stream);
 
1653
    mail_check(stream);
 
1654
}
 
1655
 
 
1656
 
 
1657
/*
 
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.
 
1663
 */
 
1664
int
 
1665
pine_mail_list(MAILSTREAM *stream, char *ref, char *pat, unsigned int *options)
 
1666
{
 
1667
    int   we_open = 0;
 
1668
    char *halfopen_target;
 
1669
    char  source[MAILTMPLEN], *target = NULL;
 
1670
    MAILSTREAM *ourstream = NULL;
 
1671
 
 
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)"));
 
1676
 
 
1677
    if(!ref && check_for_move_mbox(pat, source, sizeof(source), &target)
 
1678
       ||
 
1679
       check_for_move_mbox(ref, source, sizeof(source), &target)){
 
1680
        ref = NIL;
 
1681
        pat = target;
 
1682
        if(options)
 
1683
          *options |= PML_IS_MOVE_MBOX;
 
1684
 
 
1685
        dprint((7,
 
1686
                   "pine_mail_list: #move special case, listing \"%s\"%s\n", 
 
1687
                   pat ? pat : "(NULL)",
 
1688
                   stream ? "" : " (stream was NULL)"));
 
1689
    }
 
1690
 
 
1691
    if(!stream && ((pat && *pat == '{') || (ref && *ref == '{'))){
 
1692
        we_open++;
 
1693
        if(pat && *pat == '{'){
 
1694
            ref = NIL;
 
1695
            halfopen_target = pat;
 
1696
        }
 
1697
        else
 
1698
          halfopen_target = ref;
 
1699
    }
 
1700
 
 
1701
    if(we_open){
 
1702
        long flags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
 
1703
 
 
1704
        stream = sp_stream_get(halfopen_target, SP_MATCH);
 
1705
        if(!stream)
 
1706
          stream = sp_stream_get(halfopen_target, SP_SAME);
 
1707
 
 
1708
        if(!stream){
 
1709
            DRIVER *d;
 
1710
 
 
1711
            /*
 
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
 
1714
             * POP part.
 
1715
             */
 
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);
 
1719
                ourstream = stream;
 
1720
                if(!stream)
 
1721
                  return(FALSE);
 
1722
            }
 
1723
            else
 
1724
              stream = NULL;
 
1725
        }
 
1726
 
 
1727
        mail_list_internal(stream, ref, pat);
 
1728
    }
 
1729
    else
 
1730
      mail_list_internal(stream, ref, pat);
 
1731
    
 
1732
    if(ourstream)
 
1733
      pine_mail_close(ourstream);
 
1734
 
 
1735
    return(TRUE);
 
1736
}
 
1737
 
 
1738
 
 
1739
/*
 
1740
 * mail_list_internal -- A monument to software religion and those who
 
1741
 *                       would force it upon us.
 
1742
 */
 
1743
void
 
1744
mail_list_internal(MAILSTREAM *s, char *r, char *p)
 
1745
{
 
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];
 
1750
 
 
1751
        snprintf(tmp, sizeof(tmp), "%.*s%.*s", sizeof(tmp)/2-1, r ? r : "",
 
1752
                sizeof(tmp)/2-1, p);
 
1753
        mail_list(s, "", tmp);
 
1754
    }
 
1755
    else
 
1756
      mail_list(s, r, p);
 
1757
}
 
1758
 
 
1759
 
 
1760
long
 
1761
pine_mail_status(MAILSTREAM *stream, char *mailbox, long int flags)
 
1762
{
 
1763
    return(pine_mail_status_full(stream, mailbox, flags, NULL, NULL));
 
1764
}
 
1765
 
 
1766
 
 
1767
long
 
1768
pine_mail_status_full(MAILSTREAM *stream, char *mailbox, long int flags,
 
1769
                      imapuid_t *uidvalidity, imapuid_t *uidnext)
 
1770
{
 
1771
    char        source[MAILTMPLEN], *target = NULL;
 
1772
    long        ret = NIL;
 
1773
    MAILSTATUS  cache, status;
 
1774
    MAILSTREAM *ourstream = NULL;
 
1775
 
 
1776
    if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
 
1777
        DRIVER *d;
 
1778
 
 
1779
        memset(&status, 0, sizeof(status));
 
1780
        memset(&cache,  0, sizeof(cache));
 
1781
 
 
1782
        /* tell mm_status() to write partial return here */
 
1783
        pine_cached_status = &status;
 
1784
 
 
1785
        flags |= (SA_UIDVALIDITY | SA_UIDNEXT | SA_MESSAGES);
 
1786
 
 
1787
        /* do status of destination */
 
1788
 
 
1789
        stream = sp_stream_get(target, SP_SAME);
 
1790
 
 
1791
        /* should never be news, don't worry about mulnewrsc flag*/
 
1792
        if((ret = pine_mail_status_full(stream, target, flags, uidvalidity,
 
1793
                                        uidnext))
 
1794
           && !status.recent){
 
1795
 
 
1796
            /* do status of source */
 
1797
            pine_cached_status = &cache;
 
1798
            stream = sp_stream_get(source, SP_SAME);
 
1799
 
 
1800
            if(!stream){
 
1801
                DRIVER *d;
 
1802
 
 
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;
 
1806
 
 
1807
                    if(F_ON(F_PREFER_ALT_AUTH, ps_global))
 
1808
                      openflags |= OP_TRYALT;
 
1809
 
 
1810
                    stream = pine_mail_open(NULL, source, openflags, NULL);
 
1811
                    ourstream = stream;
 
1812
                }
 
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;
 
1817
 
 
1818
            }
 
1819
 
 
1820
            if(!ps_global->user_says_cancel && mail_status(stream, source, flags)){
 
1821
                DRIVER *d;
 
1822
                int     is_news = 0;
 
1823
 
 
1824
                /*
 
1825
                 * If the target has recent messages, then we don't come
 
1826
                 * through here. We just use the answer from the target.
 
1827
                 *
 
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.
 
1832
                 *
 
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
 
1838
                 * of unseen.
 
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.)
 
1844
                 *
 
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.
 
1849
                 *
 
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
 
1866
                 */
 
1867
 
 
1868
                if((d = mail_valid (NIL, source, (char *) NIL))
 
1869
                   && (!strcmp(d->name, "nntp") || !strcmp(d->name, "news")))
 
1870
                  is_news++;
 
1871
 
 
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;
 
1877
                }
 
1878
                else{
 
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 */
 
1886
                    }
 
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;
 
1892
                    }
 
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;
 
1896
 
 
1897
                        /* Actually open it up and check */
 
1898
                        if(F_ON(F_PREFER_ALT_AUTH, ps_global))
 
1899
                          openflags |= OP_TRYALT;
 
1900
 
 
1901
                        if(!ourstream)
 
1902
                          stream = NULL;
 
1903
 
 
1904
                        if(ourstream
 
1905
                           && !same_stream_and_mailbox(source, ourstream)){
 
1906
                            pine_mail_close(ourstream);
 
1907
                            ourstream = stream = NULL;
 
1908
                        }
 
1909
 
 
1910
                        if(!stream){
 
1911
                            stream = pine_mail_open(stream, source, openflags,
 
1912
                                                    NULL);
 
1913
                            ourstream = stream;
 
1914
                        }
 
1915
 
 
1916
                        if(stream){
 
1917
                            delete_count = count_flagged(stream, F_DEL);
 
1918
                            not_deleted = stream->nmsgs - delete_count;
 
1919
                        }
 
1920
 
 
1921
                        status.messages += not_deleted;
 
1922
                        status.recent   += not_deleted;
 
1923
                        status.unseen   += not_deleted;
 
1924
                        status.uidnext  += not_deleted;
 
1925
                    }
 
1926
 
 
1927
                    if(uidvalidity && cache.flags & SA_UIDVALIDITY)
 
1928
                      *uidvalidity = cache.uidvalidity;
 
1929
 
 
1930
                    if(uidnext && cache.flags & SA_UIDNEXT)
 
1931
                      *uidnext = cache.uidnext;
 
1932
                }
 
1933
            }
 
1934
        }
 
1935
 
 
1936
        /*
 
1937
         * Do the regular mm_status callback which we've been intercepting
 
1938
         * into different locations above.
 
1939
         */
 
1940
        pine_cached_status = NIL;
 
1941
        if(ret)
 
1942
          mm_status(NULL, mailbox, &status);
 
1943
    }
 
1944
    else{
 
1945
        if(!stream){
 
1946
            DRIVER *d;
 
1947
 
 
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;
 
1951
 
 
1952
                if(F_ON(F_PREFER_ALT_AUTH, ps_global))
 
1953
                  openflags |= OP_TRYALT;
 
1954
 
 
1955
                /*
 
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
 
1961
                 * already opened.
 
1962
                 */
 
1963
                stream = sp_stream_get(mailbox, SP_MATCH);
 
1964
                if(stream){ 
 
1965
                    memset(&status, 0, sizeof(status));
 
1966
                    if(flags & SA_MESSAGES){
 
1967
                        status.flags |= SA_MESSAGES;
 
1968
                        status.messages = stream->nmsgs;
 
1969
                    }
 
1970
 
 
1971
                    if(flags & SA_RECENT){
 
1972
                        status.flags |= SA_RECENT;
 
1973
                        status.recent = stream->recent;
 
1974
                    }
 
1975
 
 
1976
                    if(flags & SA_UNSEEN){
 
1977
                        long i;
 
1978
                        SEARCHPGM *srchpgm;
 
1979
                        MESSAGECACHE *mc;
 
1980
 
 
1981
                        srchpgm = mail_newsearchpgm();
 
1982
                        srchpgm->unseen = 1;
 
1983
                      
 
1984
                        pine_mail_search_full(stream, NULL, srchpgm,
 
1985
                                              SE_NOPREFETCH | SE_FREE);
 
1986
                        status.flags |= SA_UNSEEN;
 
1987
                        status.unseen = 0L;
 
1988
                        for(i = 1L; i <= stream->nmsgs; i++)
 
1989
                          if((mc = mail_elt(stream, i)) && mc->searched)
 
1990
                            status.unseen++;
 
1991
                    }
 
1992
 
 
1993
                    if(flags & SA_UIDVALIDITY){
 
1994
                        status.flags |= SA_UIDVALIDITY;
 
1995
                        status.uidvalidity = stream->uid_validity;
 
1996
                    }
 
1997
 
 
1998
                    if(flags & SA_UIDNEXT){
 
1999
                        status.flags |= SA_UIDNEXT;
 
2000
                        status.uidnext = stream->uid_last + 1L;
 
2001
                    }
 
2002
 
 
2003
                    mm_status(NULL, mailbox, &status);
 
2004
                    return T;  /* that's what c-client returns when success */
 
2005
                }
 
2006
 
 
2007
                if(!stream)
 
2008
                  stream = sp_stream_get(mailbox, SP_SAME);
 
2009
 
 
2010
                if(!stream){
 
2011
                    stream = pine_mail_open(NULL, mailbox, openflags, NULL);
 
2012
                    ourstream = stream;
 
2013
                }
 
2014
            }
 
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;
 
2019
        }
 
2020
 
 
2021
        if(!ps_global->user_says_cancel)
 
2022
          ret = mail_status(stream, mailbox, flags);    /* non #move case */
 
2023
    }
 
2024
 
 
2025
    if(ourstream)
 
2026
      pine_mail_close(ourstream);
 
2027
 
 
2028
    return ret;
 
2029
}
 
2030
 
 
2031
 
 
2032
/*
 
2033
 * Check for a mailbox name that is a legitimate #move mailbox.
 
2034
 *
 
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
 
2040
 *
 
2041
 * Returns  1 - is a #move mailbox
 
2042
 *          0 - not
 
2043
 */
 
2044
int
 
2045
check_for_move_mbox(char *mbox, char *sourcebuf, size_t sbuflen, char **targetptr)
 
2046
{
 
2047
    char delim, *s;
 
2048
    int  i;
 
2049
 
 
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)){
 
2059
 
 
2060
        if(sourcebuf){
 
2061
            strncpy(sourcebuf, mbox+6, i);      /* copy source mailbox name */
 
2062
            sourcebuf[i] = '\0';
 
2063
        }
 
2064
 
 
2065
        if(targetptr)
 
2066
          *targetptr = s;
 
2067
        
 
2068
        return 1;
 
2069
    }
 
2070
 
 
2071
    return 0;
 
2072
}
 
2073
 
 
2074
 
 
2075
/*
 
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.
 
2081
 */
 
2082
MAILSTREAM *
 
2083
already_open_stream(char *mailbox, int flags)
 
2084
{
 
2085
    int         i;
 
2086
    MAILSTREAM *m;
 
2087
 
 
2088
    if(!mailbox)
 
2089
      return(NULL);
 
2090
 
 
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))
 
2097
              return(m);
 
2098
        }
 
2099
    }
 
2100
    else{
 
2101
        char *cn, tmp[MAILTMPLEN];
 
2102
 
 
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)))
 
2111
              return(m);
 
2112
        }
 
2113
    }
 
2114
    
 
2115
    return(NULL);
 
2116
}
 
2117
 
 
2118
 
 
2119
void
 
2120
pine_imap_cmd_happened(MAILSTREAM *stream, char *cmd, long int flags)
 
2121
{
 
2122
    dprint((9, "imap_cmd(%s, %s, 0x%lx)\n",
 
2123
            STREAMNAME(stream), cmd ? cmd : "?", flags));
 
2124
 
 
2125
    if(cmd && !strucmp(cmd, "CHECK"))
 
2126
      reset_check_point(stream);
 
2127
 
 
2128
    if(is_imap_stream(stream)){
 
2129
        time_t now;
 
2130
 
 
2131
        now = time(0);
 
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);
 
2136
    }
 
2137
}
 
2138
 
 
2139
 
 
2140
/*
 
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.
 
2144
 */
 
2145
int
 
2146
recent_activity(MAILSTREAM *stream)
 
2147
{
 
2148
    if(is_imap_stream(stream) && !sp_dead_stream(stream)
 
2149
       && (time(0) - sp_last_activity(stream) < 5L * 60L))
 
2150
      return 1;
 
2151
    else
 
2152
      return 0;
 
2153
}
 
2154
 
 
2155
void
 
2156
sp_cleanup_dead_streams(void)
 
2157
{
 
2158
    int         i;
 
2159
    MAILSTREAM *m;
 
2160
 
 
2161
    (void) streams_died();      /* tell user in case they don't know yet */
 
2162
 
 
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))
 
2166
          pine_mail_close(m);
 
2167
    }
 
2168
}
 
2169
 
 
2170
 
 
2171
/*
 
2172
 * Returns 0 if stream flags not set, non-zero if they are.
 
2173
 */
 
2174
int
 
2175
sp_flagged(MAILSTREAM *stream, long unsigned int flags)
 
2176
{
 
2177
    return(sp_flags(stream) & flags);
 
2178
}
 
2179
 
 
2180
 
 
2181
void
 
2182
sp_set_fldr(MAILSTREAM *stream, char *folder)
 
2183
{
 
2184
    PER_STREAM_S **pss;
 
2185
 
 
2186
    pss = sp_data(stream);
 
2187
    if(pss && *pss){
 
2188
        if((*pss)->fldr)
 
2189
          fs_give((void **) &(*pss)->fldr);
 
2190
        
 
2191
        if(folder)
 
2192
          (*pss)->fldr = cpystr(folder);
 
2193
    }
 
2194
}
 
2195
 
 
2196
 
 
2197
void
 
2198
sp_set_saved_cur_msg_id(MAILSTREAM *stream, char *id)
 
2199
{
 
2200
    PER_STREAM_S **pss;
 
2201
 
 
2202
    pss = sp_data(stream);
 
2203
    if(pss && *pss){
 
2204
        if((*pss)->saved_cur_msg_id)
 
2205
          fs_give((void **) &(*pss)->saved_cur_msg_id);
 
2206
        
 
2207
        if(id)
 
2208
          (*pss)->saved_cur_msg_id = cpystr(id);
 
2209
    }
 
2210
}
 
2211
 
 
2212
 
 
2213
/*
 
2214
 * Sets flags absolutely, erasing old flags.
 
2215
 */
 
2216
void
 
2217
sp_flag(MAILSTREAM *stream, long unsigned int flags)
 
2218
{
 
2219
    if(!stream)
 
2220
      return;
 
2221
 
 
2222
    dprint((9, "sp_flag(%s, 0x%x): %s%s%s%s%s%s%s%s\n",
 
2223
            (stream && stream->mailbox) ? stream->mailbox : "?",
 
2224
            flags,
 
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" : ""));
 
2233
 
 
2234
    sp_set_flags(stream, flags);
 
2235
}
 
2236
 
 
2237
 
 
2238
/*
 
2239
 * Clear individual stream flags.
 
2240
 */
 
2241
void
 
2242
sp_unflag(MAILSTREAM *stream, long unsigned int flags)
 
2243
{
 
2244
    if(!stream || !flags)
 
2245
      return;
 
2246
 
 
2247
    dprint((9, "sp_unflag(%s, 0x%x): unset%s%s%s%s%s%s\n",
 
2248
            (stream && stream->mailbox) ? stream->mailbox : "?",
 
2249
            flags,
 
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"    : ""));
 
2256
 
 
2257
    sp_set_flags(stream, sp_flags(stream) & ~flags);
 
2258
 
 
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 : "?",
 
2262
            flags,
 
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"    : ""));
 
2269
}
 
2270
 
 
2271
 
 
2272
/*
 
2273
 * Set dead stream indicator and close if not locked.
 
2274
 */
 
2275
void
 
2276
sp_mark_stream_dead(MAILSTREAM *stream)
 
2277
{
 
2278
    if(!stream)
 
2279
      return;
 
2280
 
 
2281
    dprint((9, "sp_mark_stream_dead(%s)\n",
 
2282
            (stream && stream->mailbox) ? stream->mailbox : "?"));
 
2283
 
 
2284
    /*
 
2285
     * If the stream isn't locked, it is no longer useful. Get rid of it.
 
2286
     */
 
2287
    if(!sp_flagged(stream, SP_LOCKED))
 
2288
      pine_mail_actually_close(stream);
 
2289
    else{
 
2290
        /*
 
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.
 
2296
         */
 
2297
        sp_set_dead_stream(stream, 1);
 
2298
    }
 
2299
}
 
2300
 
 
2301
 
 
2302
/*
 
2303
 * Returns the number of streams in the stream pool which are
 
2304
 * SP_USEPOOL but not SP_PERMLOCKED.
 
2305
 */
 
2306
int
 
2307
sp_nusepool_notperm(void)
 
2308
{
 
2309
    int         i, cnt = 0;
 
2310
    MAILSTREAM *m;
 
2311
 
 
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))
 
2315
          cnt++;
 
2316
    }
 
2317
 
 
2318
    return(cnt);
 
2319
}
 
2320
 
 
2321
 
 
2322
/*
 
2323
 * Returns the number of folders that the user has marked to be PERMLOCKED
 
2324
 * folders (plus INBOX) that are remote IMAP folders.
 
2325
 *
 
2326
 * This routine depends on the fact that VAR_INBOX_PATH, VAR_PERMLOCKED,
 
2327
 * and the ps_global->context_list are correctly set.
 
2328
 */
 
2329
int
 
2330
sp_nremote_permlocked(void)
 
2331
{
 
2332
    int         cnt = 0;
 
2333
    char      **lock_these, *p = NULL, *dummy = NULL, *lt;
 
2334
    DRIVER     *d;
 
2335
 
 
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"))
 
2340
      cnt++;
 
2341
 
 
2342
    /* then count the user-specified permlocked folders */
 
2343
    for(lock_these = ps_global->VAR_PERMLOCKED; lock_these && *lock_these;
 
2344
        lock_these++){
 
2345
 
 
2346
        /*
 
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.
 
2350
         */
 
2351
        if(*lock_these && !strucmp(*lock_these, ps_global->inbox_name))
 
2352
          continue;
 
2353
 
 
2354
        /* there isn't really a pair, it just dequotes for us */
 
2355
        get_pair(*lock_these, &dummy, &p, 0, 0);
 
2356
 
 
2357
        /*
 
2358
         * Check to see if this is an incoming nickname and replace it
 
2359
         * with the full name.
 
2360
         */
 
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),
 
2364
                                   FN_WHOLE_NAME))))
 
2365
          lt = p;
 
2366
 
 
2367
        if(dummy)
 
2368
          fs_give((void **) &dummy);
 
2369
 
 
2370
        if(lt && (d=mail_valid(NIL, lt, (char *) NIL))
 
2371
           && !strcmp(d->name, "imap"))
 
2372
          cnt++;
 
2373
 
 
2374
        if(p)
 
2375
          fs_give((void **) &p);
 
2376
    }
 
2377
 
 
2378
    return(cnt);
 
2379
}
 
2380
 
 
2381
 
 
2382
/*
 
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.)
 
2385
 *
 
2386
 * Args:   mailbox
 
2387
 *           flags
 
2388
 *
 
2389
 * Flags is a set of values or'd together which tells us what the request
 
2390
 * is looking for.
 
2391
 *
 
2392
 *  Returns: a live stream from the stream pool or NULL.
 
2393
 */
 
2394
MAILSTREAM *
 
2395
sp_stream_get(char *mailbox, long unsigned int flags)
 
2396
{
 
2397
    int         i;
 
2398
    MAILSTREAM *m;
 
2399
 
 
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" : ""));
 
2407
 
 
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)){
 
2417
                    dprint((7,
 
2418
                       "sp_stream_get: found exact match, slot %d\n", i));
 
2419
                    if(!sp_flagged(m, SP_LOCKED)){
 
2420
                        dprint((7,
 
2421
                               "reset idle timer1: next TAG %08lx (%s)\n",
 
2422
                               m->gensym,
 
2423
                               debug_time(1, ps_global->debug_timestamp)));
 
2424
                        sp_set_last_use_time(m, time(0));
 
2425
                    }
 
2426
 
 
2427
                    return(m);
 
2428
                }
 
2429
 
 
2430
                sp_mark_stream_dead(m);
 
2431
            }
 
2432
        }
 
2433
    }
 
2434
 
 
2435
    /*
 
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
 
2439
     * routine.
 
2440
     */
 
2441
    if(flags & SP_SAME){
 
2442
        /*
 
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.
 
2446
         */
 
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)){
 
2455
                        dprint((7,
 
2456
                            "sp_stream_get: found SAME match, slot %d\n", i));
 
2457
                        return(m);
 
2458
                    }
 
2459
 
 
2460
                    sp_mark_stream_dead(m);
 
2461
                }
 
2462
            }
 
2463
 
 
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)){
 
2473
                        dprint((7,
 
2474
                            "sp_stream_get: found SAME match, slot %d\n", i));
 
2475
                        dprint((7,
 
2476
                           "reset idle timer4: next TAG %08lx (%s)\n",
 
2477
                           m->gensym,
 
2478
                           debug_time(1, ps_global->debug_timestamp)));
 
2479
                        sp_set_last_use_time(m, time(0));
 
2480
 
 
2481
                        return(m);
 
2482
                    }
 
2483
 
 
2484
                    sp_mark_stream_dead(m);
 
2485
                }
 
2486
            }
 
2487
        }
 
2488
 
 
2489
        /*
 
2490
         * Prefer streams marked SP_TEMPUSE and not LOCKED.
 
2491
         * If SP_TEMPUSE is set in the flags arg then this is the
 
2492
         * only loop we try.
 
2493
         */
 
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)){
 
2501
                    dprint((7,
 
2502
                      "sp_stream_get: found SAME/TEMPUSE match, slot %d\n", i));
 
2503
                    dprint((7,
 
2504
                           "reset idle timer2: next TAG %08lx (%s)\n",
 
2505
                           m->gensym,
 
2506
                           debug_time(1, ps_global->debug_timestamp)));
 
2507
                    sp_set_last_use_time(m, time(0));
 
2508
                    return(m);
 
2509
                }
 
2510
 
 
2511
                sp_mark_stream_dead(m);
 
2512
            }
 
2513
        }
 
2514
 
 
2515
        /*
 
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.
 
2525
         */
 
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)){
 
2534
                        dprint((7,
 
2535
                    "sp_stream_get: found SAME/UNLOCKED match, slot %d\n", i));
 
2536
                        dprint((7,
 
2537
                           "reset idle timer3: next TAG %08lx (%s)\n",
 
2538
                           m->gensym,
 
2539
                           debug_time(1, ps_global->debug_timestamp)));
 
2540
                        sp_set_last_use_time(m, time(0));
 
2541
                        return(m);
 
2542
                    }
 
2543
 
 
2544
                    sp_mark_stream_dead(m);
 
2545
                }
 
2546
            }
 
2547
        }
 
2548
    }
 
2549
 
 
2550
    /*
 
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
 
2554
     * the slot.
 
2555
     */
 
2556
    if(!(flags & (SP_SAME | SP_MATCH))){
 
2557
        /*
 
2558
         * Prefer streams marked SP_TEMPUSE and not LOCKED.
 
2559
         * If SP_TEMPUSE is set in the flags arg then this is the
 
2560
         * only loop we try.
 
2561
         */
 
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)){
 
2566
                dprint((7,
 
2567
    "sp_stream_get: found Not-SAME/TEMPUSE match, slot %d\n", i));
 
2568
                /*
 
2569
                 * We ping it in case there is new mail that we should
 
2570
                 * pass through our filters. Pine_mail_actually_close will
 
2571
                 * do that.
 
2572
                 */
 
2573
                (void) pine_mail_ping(m);
 
2574
                return(m);
 
2575
            }
 
2576
        }
 
2577
 
 
2578
        /*
 
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.
 
2584
         */
 
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)){
 
2589
                    dprint((7,
 
2590
        "sp_stream_get: found Not-SAME/UNLOCKED match, slot %d\n", i));
 
2591
                    /*
 
2592
                     * We ping it in case there is new mail that we should
 
2593
                     * pass through our filters. Pine_mail_actually_close will
 
2594
                     * do that.
 
2595
                     */
 
2596
                    (void) pine_mail_ping(m);
 
2597
                    return(m);
 
2598
                }
 
2599
            }
 
2600
        }
 
2601
    }
 
2602
 
 
2603
    dprint((7, "sp_stream_get: no match found\n"));
 
2604
 
 
2605
    return(NULL);
 
2606
}
 
2607
 
 
2608
 
 
2609
void
 
2610
sp_end(void)
 
2611
{
 
2612
    int         i;
 
2613
    MAILSTREAM *m;
 
2614
 
 
2615
    dprint((7, "sp_end\n"));
 
2616
 
 
2617
    for(i = 0; i < ps_global->s_pool.nstream; i++){
 
2618
        m = ps_global->s_pool.streams[i];
 
2619
        if(m)
 
2620
          pine_mail_actually_close(m);
 
2621
    }
 
2622
 
 
2623
    if(ps_global->s_pool.streams)
 
2624
      fs_give((void **) &ps_global->s_pool.streams);
 
2625
 
 
2626
    ps_global->s_pool.nstream = 0;
 
2627
}
 
2628
 
 
2629
 
 
2630
/*
 
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.
 
2636
 */
 
2637
int
 
2638
sp_add(MAILSTREAM *stream, int usepool)
 
2639
{
 
2640
    int         i, slot = -1;
 
2641
    MAILSTREAM *m;
 
2642
 
 
2643
    dprint((7, "sp_add(%s, %d)\n",
 
2644
            (stream && stream->mailbox) ? stream->mailbox : "?", usepool));
 
2645
 
 
2646
    if(!stream){
 
2647
        dprint((7, "sp_add: NULL stream\n"));
 
2648
        return -1;
 
2649
    }
 
2650
 
 
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];
 
2654
        if(m == stream){
 
2655
            slot = i;
 
2656
            dprint((7,
 
2657
                    "sp_add: stream was already in slot %d\n", slot));
 
2658
            return 0;
 
2659
        }
 
2660
    }
 
2661
 
 
2662
    if(usepool && !sp_flagged(stream, SP_PERMLOCKED)
 
2663
       && sp_nusepool_notperm() >= ps_global->s_pool.max_remstream){
 
2664
        dprint((7,
 
2665
                "sp_add: reached max implicit SP_USEPOOL of %d\n",
 
2666
                ps_global->s_pool.max_remstream));
 
2667
        return -1;
 
2668
    }
 
2669
 
 
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];
 
2673
        if(!m){
 
2674
            slot = i;
 
2675
            dprint((7,
 
2676
                    "sp_add: using empty slot %d\n", slot));
 
2677
            break;
 
2678
        }
 
2679
    }
 
2680
 
 
2681
    /* else, allocate more space */
 
2682
    if(slot < 0){
 
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;
 
2690
        }
 
2691
        else{
 
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));
 
2698
        }
 
2699
 
 
2700
        dprint((7,
 
2701
            "sp_add: allocate more space, using new slot %d\n", slot));
 
2702
    }
 
2703
 
 
2704
    if(slot >= 0 && slot < ps_global->s_pool.nstream){
 
2705
        ps_global->s_pool.streams[slot] = stream;
 
2706
        return 0;
 
2707
    }
 
2708
    else{
 
2709
        dprint((7, "sp_add: failed to find a slot!\n"));
 
2710
        return -1;
 
2711
    }
 
2712
}
 
2713
 
 
2714
 
 
2715
/*
 
2716
 * Simply remove this stream from the stream pool.
 
2717
 */
 
2718
void
 
2719
sp_delete(MAILSTREAM *stream)
 
2720
{
 
2721
    int         i;
 
2722
    MAILSTREAM *m;
 
2723
 
 
2724
    if(!stream)
 
2725
      return;
 
2726
 
 
2727
    dprint((7, "sp_delete(%s)\n",
 
2728
            (stream && stream->mailbox) ? stream->mailbox : "?"));
 
2729
 
 
2730
    /*
 
2731
     * There are some global stream pointers that we have to worry
 
2732
     * about before deleting the stream.
 
2733
     */
 
2734
 
 
2735
    /* first, mail_stream is the global currently open folder */
 
2736
    if(ps_global->mail_stream == stream)
 
2737
      ps_global->mail_stream = NULL;
 
2738
    
 
2739
    /* remote address books may have open stream pointers */
 
2740
    note_closed_adrbk_stream(stream);
 
2741
 
 
2742
    for(i = 0; i < ps_global->s_pool.nstream; i++){
 
2743
        m = ps_global->s_pool.streams[i];
 
2744
        if(m == stream){
 
2745
            ps_global->s_pool.streams[i] = NULL;
 
2746
            dprint((7,
 
2747
                    "sp_delete: stream removed from slot %d\n", i));
 
2748
            return;
 
2749
        }
 
2750
    }
 
2751
}
 
2752
 
 
2753
 
 
2754
/*
 
2755
 * Returns 1 if any locked userfldr is dead, 0 if all alive.
 
2756
 */
 
2757
int
 
2758
sp_a_locked_stream_is_dead(void)
 
2759
{
 
2760
    int         i, ret = 0;
 
2761
    MAILSTREAM *m;
 
2762
 
 
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))
 
2767
          ret++;
 
2768
    }
 
2769
 
 
2770
    return(ret);
 
2771
}
 
2772
 
 
2773
 
 
2774
/*
 
2775
 * Returns 1 if any locked stream is changed, 0 otherwise
 
2776
 */
 
2777
int
 
2778
sp_a_locked_stream_changed(void)
 
2779
{
 
2780
    int         i, ret = 0;
 
2781
    MAILSTREAM *m;
 
2782
 
 
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))
 
2787
          ret++;
 
2788
    }
 
2789
 
 
2790
    return(ret);
 
2791
}
 
2792
 
 
2793
 
 
2794
/*
 
2795
 * Returns the inbox stream or NULL.
 
2796
 */
 
2797
MAILSTREAM *
 
2798
sp_inbox_stream(void)
 
2799
{
 
2800
    int         i;
 
2801
    MAILSTREAM *m, *ret = NULL;
 
2802
 
 
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))
 
2806
          ret = m;
 
2807
    }
 
2808
 
 
2809
    return(ret);
 
2810
}
 
2811
 
 
2812
 
 
2813
/*
 
2814
 * Make sure that the sp_data per-stream data storage area exists.
 
2815
 *
 
2816
 * Returns a handle to the sp_data data unless stream is NULL,
 
2817
 *         in which case NULL is returned
 
2818
 */
 
2819
PER_STREAM_S **
 
2820
sp_data(MAILSTREAM *stream)
 
2821
{
 
2822
    PER_STREAM_S **pss = NULL;
 
2823
 
 
2824
    if(stream){
 
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);
 
2829
        }
 
2830
    }
 
2831
 
 
2832
    return(pss);
 
2833
}
 
2834
 
 
2835
 
 
2836
/*
 
2837
 * Returns a pointer to the msgmap associated with the argument stream.
 
2838
 *
 
2839
 * If the PER_STREAM_S data or the msgmap does not already exist, it will be
 
2840
 * created.
 
2841
 */
 
2842
MSGNO_S *
 
2843
sp_msgmap(MAILSTREAM *stream)
 
2844
{
 
2845
    MSGNO_S      **msgmap = NULL;
 
2846
    PER_STREAM_S **pss    = NULL;
 
2847
 
 
2848
    pss = sp_data(stream);
 
2849
 
 
2850
    if(pss && *pss
 
2851
       && (*(msgmap = (MSGNO_S **) &(*pss)->msgmap) == NULL))
 
2852
      mn_init(msgmap, stream->nmsgs);
 
2853
 
 
2854
    return(msgmap ? *msgmap : NULL);
 
2855
}
 
2856
 
 
2857
 
 
2858
void
 
2859
sp_free_callback(void **sparep)
 
2860
{
 
2861
    PER_STREAM_S **pss;
 
2862
    MAILSTREAM    *stream = NULL, *m;
 
2863
    int            i;
 
2864
 
 
2865
    pss = (PER_STREAM_S **) sparep;
 
2866
 
 
2867
    if(pss && *pss){
 
2868
        /*
 
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.
 
2873
         */
 
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)
 
2877
              stream = m;
 
2878
        }
 
2879
 
 
2880
        if(stream){
 
2881
            if(ps_global->mail_stream == stream)
 
2882
              ps_global->mail_stream = NULL;
 
2883
 
 
2884
            sp_delete(stream);
 
2885
        }
 
2886
 
 
2887
        sp_free(pss);
 
2888
    }
 
2889
}
 
2890
 
 
2891
 
 
2892
/*
 
2893
 * Free the data but don't mess with the stream pool.
 
2894
 */
 
2895
void
 
2896
sp_free(PER_STREAM_S **pss)
 
2897
{
 
2898
    if(pss && *pss){
 
2899
        if((*pss)->msgmap){
 
2900
            if(ps_global->msgmap == (*pss)->msgmap)
 
2901
              ps_global->msgmap = NULL;
 
2902
 
 
2903
            mn_give(&(*pss)->msgmap);
 
2904
        }
 
2905
 
 
2906
        if((*pss)->fldr)
 
2907
          fs_give((void **) &(*pss)->fldr);
 
2908
        
 
2909
        fs_give((void **) pss);
 
2910
    }
 
2911
}
 
2912
 
 
2913
 
 
2914
 
 
2915
/*----------------------------------------------------------------------
 
2916
  See if stream can be used for a mailbox name
 
2917
 
 
2918
   Accepts: mailbox name
 
2919
            candidate stream
 
2920
   Returns: stream if it can be used, else NIL
 
2921
 
 
2922
  This is called to weed out unnecessary use of c-client streams. In other
 
2923
  words, to help facilitate re-use of streams.
 
2924
 
 
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.
 
2939
  ----*/
 
2940
MAILSTREAM *
 
2941
same_stream(char *name, MAILSTREAM *stream)
 
2942
{
 
2943
    NETMBX mb_s, mb_n, mb_o;
 
2944
 
 
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))
 
2957
           ||
 
2958
           ((!mb_n.user || !*mb_n.user)
 
2959
            && mb_s.user
 
2960
            && ((ps_global->VAR_USER_ID
 
2961
                 && !strcmp(ps_global->VAR_USER_ID, mb_s.user))
 
2962
                ||
 
2963
                (!ps_global->VAR_USER_ID
 
2964
                 && ps_global->ui.login[0]
 
2965
                 && !strcmp(ps_global->ui.login, mb_s.user))))
 
2966
           ||
 
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",
 
2971
               name ? name : "?",
 
2972
               (stream && stream->mailbox) ? stream->mailbox : "NULL"));
 
2973
        return(stream);
 
2974
    }
 
2975
 
 
2976
    dprint((7, "same_stream: name->%s == stream->%s: no dice\n",
 
2977
           name ? name : "?",
 
2978
           (stream && stream->mailbox) ? stream->mailbox : "NULL"));
 
2979
    return(NULL);
 
2980
}
 
2981
 
 
2982
 
 
2983
 
 
2984
/*----------------------------------------------------------------------
 
2985
  See if this stream has the named mailbox selected.
 
2986
 
 
2987
   Accepts: mailbox name
 
2988
            candidate stream
 
2989
   Returns: stream if it can be used, else NIL
 
2990
  ----*/
 
2991
MAILSTREAM *
 
2992
same_stream_and_mailbox(char *name, MAILSTREAM *stream)
 
2993
{
 
2994
    NETMBX mb_s, mb_n;
 
2995
 
 
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"))))){
 
3003
        dprint((7,
 
3004
               "same_stream_and_mailbox: name->%s == stream->%s: yes\n",
 
3005
               name ? name : "?",
 
3006
               (stream && stream->mailbox) ? stream->mailbox : "NULL"));
 
3007
        return(stream);
 
3008
    }
 
3009
 
 
3010
    dprint((7,
 
3011
           "same_stream_and_mailbox: name->%s == stream->%s: no dice\n",
 
3012
           name ? name : "?",
 
3013
           (stream && stream->mailbox) ? stream->mailbox : "NULL"));
 
3014
    return(NULL);
 
3015
}
 
3016
 
 
3017
/*
 
3018
 * Args -- name1 and name2 are remote mailbox names.
 
3019
 *
 
3020
 * Returns --  True  if names refer to same mailbox accessed in same way
 
3021
 *             False if not
 
3022
 *
 
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.
 
3026
 */
 
3027
int
 
3028
same_remote_mailboxes(char *name1, char *name2)
 
3029
{
 
3030
    NETMBX mb1, mb2;
 
3031
    char *cn1;
 
3032
 
 
3033
    /*
 
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.
 
3037
     */
 
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))
 
3054
               ||
 
3055
               (!(mb1.user && *mb1.user) && !(mb2.user && *mb2.user))
 
3056
               ||
 
3057
               (!(mb1.user && *mb1.user)
 
3058
                && ((ps_global->VAR_USER_ID
 
3059
                     && !strcmp(ps_global->VAR_USER_ID, mb2.user))
 
3060
                    ||
 
3061
                    (!ps_global->VAR_USER_ID
 
3062
                     && ps_global->ui.login[0]
 
3063
                     && !strcmp(ps_global->ui.login, mb2.user))))
 
3064
               ||
 
3065
               (!(mb2.user && *mb2.user)
 
3066
                && ((ps_global->VAR_USER_ID
 
3067
                     && !strcmp(ps_global->VAR_USER_ID, mb1.user))
 
3068
                    ||
 
3069
                    (!ps_global->VAR_USER_ID
 
3070
                     && ps_global->ui.login[0]
 
3071
                     && !strcmp(ps_global->ui.login, mb1.user))))));
 
3072
}
 
3073
 
 
3074
 
 
3075
int
 
3076
is_imap_stream(MAILSTREAM *stream)
 
3077
{
 
3078
    return(stream && stream->dtb && stream->dtb->name
 
3079
           && !strcmp(stream->dtb->name, "imap"));
 
3080
}
 
3081
 
 
3082
 
 
3083
int
 
3084
modern_imap_stream(MAILSTREAM *stream)
 
3085
{
 
3086
    return(is_imap_stream(stream) && LEVELIMAP4rev1(stream));
 
3087
}
 
3088
 
 
3089
 
 
3090
/*----------------------------------------------------------------------
 
3091
     Check and see if all the stream are alive
 
3092
 
 
3093
Returns:  0 if there was no change
 
3094
         >0 if streams have died since last call
 
3095
 
 
3096
Also outputs a message that the streams have died
 
3097
 ----*/
 
3098
int
 
3099
streams_died(void)
 
3100
{
 
3101
    int rv = 0;
 
3102
    int         i;
 
3103
    MAILSTREAM *m;
 
3104
    char       *folder;
 
3105
 
 
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)){
 
3111
                    rv++;
 
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",
 
3119
                            folder));
 
3120
                    if(rv == 1){
 
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);
 
3126
                    }
 
3127
                }
 
3128
            }
 
3129
            else{
 
3130
                if(!sp_noticed_dead_stream(m)){
 
3131
                    sp_set_noticed_dead_stream(m, 1);
 
3132
                    folder = STREAMNAME(m);
 
3133
                    /*
 
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.
 
3139
                     */
 
3140
#if 0
 
3141
                    if(sp_flagged(m, SP_USEPOOL))
 
3142
                      q_status_message(SM_ORDER, 3, 3,
 
3143
        "Warning: Possible problem accessing remote data, connection died.");
 
3144
#endif
 
3145
 
 
3146
                    dprint((6, "streams_died: not locked: \"%s\"\n",
 
3147
                            folder));
 
3148
                }
 
3149
 
 
3150
                pine_mail_actually_close(m);
 
3151
            }
 
3152
        }
 
3153
    }
 
3154
 
 
3155
    return(rv);
 
3156
}
 
3157
 
 
3158
 
 
3159
/*
 
3160
 * mail_cmd_stream - return a stream suitable for mail_lsub,
 
3161
 *                    mail_subscribe, and mail_unsubscribe
 
3162
 *
 
3163
 */
 
3164
MAILSTREAM *
 
3165
mail_cmd_stream(CONTEXT_S *context, int *closeit)
 
3166
{
 
3167
    char        tmp[MAILTMPLEN];
 
3168
 
 
3169
    *closeit = 1;
 
3170
    (void) context_apply(tmp, context, "x", sizeof(tmp));
 
3171
 
 
3172
    return(pine_mail_open(NULL, tmp,
 
3173
                          OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE,
 
3174
                          NULL));
 
3175
}
 
3176
 
 
3177
 
 
3178
/*
 
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.
 
3183
 */
 
3184
long
 
3185
dummy_soutr(void *stream, char *string)
 
3186
{
 
3187
    dprint((2, "dummy_soutr unexpected call, caught overflow\n"));
 
3188
    return LONGT;
 
3189
}