1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: stream.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
3 #endif
4
5 /*
6 * ========================================================================
7 * Copyright 2013-2021 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * ========================================================================
17 */
18
19 /*======================================================================
20 stream.c
21 Implements the Pine mail stream management routines
22 and c-client wrapper functions
23 ====*/
24
25
26 #include "../pith/headers.h"
27 #include "../pith/stream.h"
28 #include "../pith/state.h"
29 #include "../pith/conf.h"
30 #include "../pith/flag.h"
31 #include "../pith/msgno.h"
32 #include "../pith/adrbklib.h"
33 #include "../pith/status.h"
34 #include "../pith/newmail.h"
35 #include "../pith/detach.h"
36 #include "../pith/folder.h"
37 #include "../pith/mailcmd.h"
38 #include "../pith/util.h"
39 #include "../pith/news.h"
40 #include "../pith/sequence.h"
41 #include "../pith/options.h"
42 #include "../pith/mimedesc.h"
43
44
45 void (*pith_opt_closing_stream)(MAILSTREAM *);
46
47
48 /*
49 * Internal prototypes
50 */
51 void reset_stream_view_state(MAILSTREAM *);
52 void carefully_reset_sp_flags(MAILSTREAM *, unsigned long);
53 char *partial_text_gets(readfn_t, void *, unsigned long, GETS_DATA *);
54 void mail_list_internal(MAILSTREAM *, char *, char *);
55 int recent_activity(MAILSTREAM *);
56 int hibit_in_searchpgm(SEARCHPGM *);
57 int hibit_in_strlist(STRINGLIST *);
58 int hibit_in_header(SEARCHHEADER *);
59 int hibit_in_sizedtext(SIZEDTEXT *);
60 int sp_nusepool_notperm(void);
61 int sp_add(MAILSTREAM *, int);
62 void sp_delete(MAILSTREAM *);
63 void sp_free(PER_STREAM_S **);
64
65
66 static FETCH_READC_S *g_pft_desc;
67
68
69 MAILSTATUS *pine_cached_status; /* implement status for #move folder */
70
71
72
73 /*
74 * Pine wrapper around mail_open. If we have the PREFER_ALT_AUTH flag turned
75 * on, we need to set the TRYALT flag before trying the open.
76 * This routine manages the stream pool, too. It tries to re-use existing
77 * streams instead of opening new ones, or maybe it will leave one open and
78 * use a new one if that seems to make more sense. Pine_mail_close leaves
79 * streams open so that they may be re-used. Each pine_mail_open should have
80 * a matching pine_mail_close (or possible pine_mail_actually_close) somewhere
81 * that goes with it.
82 *
83 * Args:
84 * stream -- A possible stream for recycling. This isn't usually the
85 * way recycling happens. Usually it is automatic.
86 * mailbox -- The mailbox to be opened.
87 * openflags -- Flags passed here to modify the behavior.
88 * retflags -- Flags returned from here. SP_MATCH will be lit if that is
89 * what happened. If SP_MATCH is lit then SP_LOCKED may also
90 * be lit if the matched stream was already locked when
91 * we got here.
92 */
93 MAILSTREAM *
pine_mail_open(MAILSTREAM * stream,char * mailbox,long int openflags,long int * retflags)94 pine_mail_open(MAILSTREAM *stream, char *mailbox, long int openflags, long int *retflags)
95 {
96 MAILSTREAM *retstream = NULL;
97 DRIVER *d;
98 int permlocked = 0, is_inbox = 0, usepool = 0, tempuse = 0, uf = 0;
99 unsigned long flags;
100 char **lock_these, *target = NULL;
101 static unsigned long streamcounter = 0;
102
103 dprint((7,
104 "pine_mail_open: opening \"%s\"%s openflags=0x%x %s%s%s%s%s%s%s%s%s (%s)\n",
105 mailbox ? mailbox : "(NULL)",
106 stream ? "" : " (stream was NULL)",
107 openflags,
108 openflags & OP_HALFOPEN ? " OP_HALFOPEN" : "",
109 openflags & OP_READONLY ? " OP_READONLY" : "",
110 openflags & OP_SILENT ? " OP_SILENT" : "",
111 openflags & OP_DEBUG ? " OP_DEBUG" : "",
112 openflags & SP_PERMLOCKED ? " SP_PERMLOCKED" : "",
113 openflags & SP_INBOX ? " SP_INBOX" : "",
114 openflags & SP_USERFLDR ? " SP_USERFLDR" : "",
115 openflags & SP_USEPOOL ? " SP_USEPOOL" : "",
116 openflags & SP_TEMPUSE ? " SP_TEMPUSE" : "",
117 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
118
119 if(retflags)
120 *retflags = 0L;
121
122 if(ps_global->user_says_cancel){
123 dprint((7, "pine_mail_open: cancelled by user\n"));
124 return(retstream);
125 }
126
127 is_inbox = openflags & SP_INBOX;
128 uf = openflags & SP_USERFLDR;
129
130 /* inbox is still special, assume that we want to permlock it */
131 permlocked = (is_inbox || openflags & SP_PERMLOCKED) ? 1 : 0;
132
133 /* check to see if user wants this folder permlocked */
134 for(lock_these = ps_global->VAR_PERMLOCKED;
135 uf && !permlocked && lock_these && *lock_these; lock_these++){
136 char *p = NULL, *dummy = NULL, *lt, *lname, *mname;
137 char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN];
138
139 /* there isn't really a pair, it just dequotes for us */
140 get_pair(*lock_these, &dummy, &p, 0, 0);
141
142 /*
143 * Check to see if this is an incoming nickname and replace it
144 * with the full name.
145 */
146 if(!(p && ps_global->context_list
147 && ps_global->context_list->use & CNTXT_INCMNG
148 && (lt=folder_is_nick(p, FOLDERS(ps_global->context_list), 0))))
149 lt = p;
150
151 if(dummy)
152 fs_give((void **) &dummy);
153
154 if(lt && mailbox
155 && (same_remote_mailboxes(mailbox, lt)
156 ||
157 (!IS_REMOTE(mailbox)
158 && (lname=mailboxfile(tmp1, lt))
159 && (mname=mailboxfile(tmp2, mailbox))
160 && !strcmp(lname, mname))))
161 permlocked++;
162
163 if(p)
164 fs_give((void **) &p);
165 }
166
167 /*
168 * Only cache if remote, not nntp, not pop, and caller asked us to.
169 * It might make sense to do some caching for nntp and pop, as well, but
170 * we aren't doing it right now. For example, an nntp stream open to
171 * one group could be reused for another group. An open pop stream could
172 * be used for mail_copy_full.
173 *
174 * An implication of doing only imap here is that sp_stream_get will only
175 * be concerned with imap streams.
176 */
177 if((d = mail_valid (NIL, mailbox, (char *) NIL)) && !strcmp(d->name, "imap")){
178 usepool = openflags & SP_USEPOOL;
179 tempuse = openflags & SP_TEMPUSE;
180 }
181 else{
182 if(IS_REMOTE(mailbox)){
183 dprint((9, "pine_mail_open: not cacheable: %s\n", !d ? "no driver?" : d->name ? d->name : "?" ));
184 }
185 else{
186 if(permlocked || (openflags & OP_READONLY)){
187 /*
188 * This is a strange case. We want to allow stay-open local
189 * folders, but they don't fit into the rest of the framework
190 * well. So we'll look for it being already open in this case
191 * and special-case it (the already_open_stream() case
192 * below).
193 */
194 dprint((9,
195 "pine_mail_open: not cacheable: not remote, but check for local stream\n"));
196 }
197 else{
198 dprint((9,
199 "pine_mail_open: not cacheable: not remote\n"));
200 }
201 }
202 }
203
204 /* If driver doesn't support halfopen, just open it. */
205 if(d && (openflags & OP_HALFOPEN) && !(d->flags & DR_HALFOPEN)){
206 openflags &= ~OP_HALFOPEN;
207 dprint((9,
208 "pine_mail_open: turning off OP_HALFOPEN flag\n"));
209 }
210
211 /*
212 * Some of the flags are pine's, the rest are meant for mail_open.
213 * We've noted the pine flags, now remove them before we call mail_open.
214 */
215 openflags &= ~(SP_USEPOOL | SP_TEMPUSE | SP_INBOX
216 | SP_PERMLOCKED | SP_USERFLDR);
217
218 #ifdef DEBUG
219 if(ps_global->debug_imap > 3 || ps_global->debugmem)
220 openflags |= OP_DEBUG;
221 #endif
222
223 if(F_ON(F_PREFER_ALT_AUTH, ps_global)){
224 if((d = mail_valid (NIL, mailbox, (char *) NIL))
225 && !strcmp(d->name, "imap"))
226 openflags |= OP_TRYALT;
227 }
228
229 if(F_ON(F_ENABLE_MULNEWSRCS, ps_global)){
230 char source[MAILTMPLEN];
231 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
232 DRIVER *d;
233 if((d = mail_valid(NIL, source, (char *) NIL))
234 && (!strcmp(d->name, "news")
235 || !strcmp(d->name, "nntp")))
236 openflags |= OP_MULNEWSRC;
237 }
238 else if((d = mail_valid(NIL, mailbox, (char *) NIL))
239 && !strcmp(d->name, "nntp"))
240 openflags |= OP_MULNEWSRC;
241 }
242
243 /*
244 * One of the problems is that the new-style stream caching (the
245 * sp_stream_get stuff) may conflict with some of the old-style caching
246 * (the passed in argument stream) that is still in the code. We should
247 * probably eliminate the old-style caching, but some of it is still useful,
248 * especially if it deals with something other than IMAP. We want to prevent
249 * mistakes caused by conflicts between the two styles. In particular, we
250 * don't want to have a new-style cached stream re-opened because of the
251 * old-style caching code. This can happen if a stream is passed in that
252 * is not usable, and then a new stream is opened because the passed in
253 * stream causes us to bypass the new caching code. Play it safe. If it
254 * is an IMAP stream, just close it. This should leave it in the new-style
255 * cache anyway, causing no loss. Maybe not if the cache wasn't large
256 * enough to have it in there in the first place, in which case we get
257 * a possibly unnecessary close and open. If it isn't IMAP we still have
258 * to worry about it because it will cause us to bypass the caching code.
259 * So if the stream isn't IMAP but the mailbox we're opening is, close it.
260 * The immediate alternative would be to try to emulate the code in
261 * mail_open that checks whether it is re-usable or not, but that is
262 * dangerous if that code changes on us.
263 */
264 if(stream){
265 if(is_imap_stream(stream)
266 || ((d = mail_valid (NIL, mailbox, (char *) NIL))
267 && !strcmp(d->name, "imap"))){
268 if(is_imap_stream(stream)){
269 dprint((7,
270 "pine_mail_open: closing passed in IMAP stream %s\n",
271 stream->mailbox ? stream->mailbox : "?"));
272 }
273 else{
274 dprint((7,
275 "pine_mail_open: closing passed in non-IMAP stream %s\n",
276 stream->mailbox ? stream->mailbox : "?"));
277 }
278
279 pine_mail_close(stream);
280 stream = NULL;
281 }
282 }
283
284 /*
285 * Maildrops are special. The mailbox name will be a #move name. If the
286 * target of the maildrop is an IMAP folder we want to be sure it isn't
287 * already open in another cached stream, to avoid double opens. This
288 * could have happened if the user opened it manually as the target
289 * instead of as a maildrop. It could also be a side-effect of marking
290 * an answered flag after a reply.
291 */
292 target = NULL;
293 if(check_for_move_mbox(mailbox, NULL, 0, &target)){
294 MAILSTREAM *targetstream = NULL;
295
296 if((d = mail_valid (NIL, target, (char *) NIL)) && !strcmp(d->name, "imap")){
297 targetstream = sp_stream_get(target, SP_MATCH | SP_RO_OK);
298 if(targetstream){
299 dprint((9, "pine_mail_open: close previously opened target of maildrop\n"));
300 pine_mail_actually_close(targetstream);
301 }
302 }
303 }
304
305 if((usepool && !stream && permlocked)
306 || (!usepool && (permlocked || (openflags & OP_READONLY))
307 && (retstream = already_open_stream(mailbox, AOS_NONE)))){
308 if(retstream)
309 stream = retstream;
310 else
311 stream = sp_stream_get(mailbox,
312 SP_MATCH | ((openflags & OP_READONLY) ? SP_RO_OK : 0));
313 if(stream){
314 flags = SP_LOCKED
315 | (usepool ? SP_USEPOOL : 0)
316 | (permlocked ? SP_PERMLOCKED : 0)
317 | (is_inbox ? SP_INBOX : 0)
318 | (uf ? SP_USERFLDR : 0)
319 | (tempuse ? SP_TEMPUSE : 0);
320
321 /*
322 * If the stream wasn't already locked, then we reset it so it
323 * looks like we are reopening it. We have to worry about recent
324 * messages since they will still be recent, if that affects us.
325 */
326 if(!(sp_flags(stream) & SP_LOCKED))
327 reset_stream_view_state(stream);
328
329 if(retflags){
330 *retflags |= SP_MATCH;
331 if(sp_flags(stream) & SP_LOCKED)
332 *retflags |= SP_LOCKED;
333 }
334
335 if(sp_flags(stream) & SP_LOCKED
336 && sp_flags(stream) & SP_USERFLDR
337 && !(flags & SP_USERFLDR)){
338 sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
339 dprint((7,
340 "pine_mail_open: permlocked: ref cnt up to %d\n",
341 sp_ref_cnt(stream)));
342 }
343 else if(sp_ref_cnt(stream) <= 0){
344 sp_set_ref_cnt(stream, 1);
345 dprint((7,
346 "pine_mail_open: permexact: ref cnt set to %d\n",
347 sp_ref_cnt(stream)));
348 }
349
350 carefully_reset_sp_flags(stream, flags);
351
352 if(stream->silent && !(openflags & OP_SILENT))
353 stream->silent = NIL;
354
355 dprint((9, "pine_mail_open: stream was already open\n"));
356 if(stream && stream->dtb && stream->dtb->name
357 && !strcmp(stream->dtb->name, "imap")){
358 dprint((7, "pine_mail_open: next TAG %08lx\n",
359 stream->gensym));
360 }
361
362 return(stream);
363 }
364 }
365
366 if(usepool && !stream){
367 /*
368 * First, we look for an exact match, a stream which is already
369 * open to the mailbox we are trying to re-open, and we use that.
370 * Skip permlocked only because we did it above already.
371 */
372 if(!permlocked)
373 stream = sp_stream_get(mailbox,
374 SP_MATCH | ((openflags & OP_READONLY) ? SP_RO_OK : 0));
375
376 if(stream){
377 flags = SP_LOCKED
378 | (usepool ? SP_USEPOOL : 0)
379 | (permlocked ? SP_PERMLOCKED : 0)
380 | (is_inbox ? SP_INBOX : 0)
381 | (uf ? SP_USERFLDR : 0)
382 | (tempuse ? SP_TEMPUSE : 0);
383
384 /*
385 * If the stream wasn't already locked, then we reset it so it
386 * looks like we are reopening it. We have to worry about recent
387 * messages since they will still be recent, if that affects us.
388 */
389 if(!(sp_flags(stream) & SP_LOCKED))
390 reset_stream_view_state(stream);
391
392 if(retflags){
393 *retflags |= SP_MATCH;
394 if(sp_flags(stream) & SP_LOCKED)
395 *retflags |= SP_LOCKED;
396 }
397
398 if(sp_flags(stream) & SP_LOCKED
399 && sp_flags(stream) & SP_USERFLDR
400 && !(flags & SP_USERFLDR)){
401 sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
402 dprint((7,
403 "pine_mail_open: matched: ref cnt up to %d\n",
404 sp_ref_cnt(stream)));
405 }
406 else if(sp_ref_cnt(stream) <= 0){
407 sp_set_ref_cnt(stream, 1);
408 dprint((7,
409 "pine_mail_open: exact: ref cnt set to %d\n",
410 sp_ref_cnt(stream)));
411 }
412
413 carefully_reset_sp_flags(stream, flags);
414
415 /*
416 * We may be re-using a stream that was previously open
417 * with OP_SILENT and now we don't want OP_SILENT.
418 * We don't turn !silent into silent because the !silentness
419 * could be important in the original context (for example,
420 * silent suppresses mm_expunged calls).
421 *
422 * WARNING: we're messing with c-client internals.
423 */
424 if(stream->silent && !(openflags & OP_SILENT))
425 stream->silent = NIL;
426
427 dprint((9, "pine_mail_open: stream already open\n"));
428 if(stream && stream->dtb && stream->dtb->name
429 && !strcmp(stream->dtb->name, "imap")){
430 dprint((7, "pine_mail_open: next TAG %08lx\n",
431 stream->gensym));
432 }
433
434 return(stream);
435 }
436
437 /*
438 * No exact match, look for a stream which is open to the same
439 * server and marked for TEMPUSE.
440 */
441 stream = sp_stream_get(mailbox, SP_SAME | SP_TEMPUSE);
442 if(stream){
443 dprint((9,
444 "pine_mail_open: attempting to re-use TEMP stream\n"));
445 }
446 /*
447 * No SAME/TEMPUSE stream so we look to see if there is an
448 * open slot available (we're not yet at max_remstream). If there
449 * is an open slot, we'll just open a new stream and put it there.
450 * If not, we'll go inside this conditional.
451 */
452 else if(!permlocked
453 && sp_nusepool_notperm() >= ps_global->s_pool.max_remstream){
454 dprint((9,
455 "pine_mail_open: no empty slots\n"));
456 /*
457 * No empty remote slots available. See if there is a
458 * TEMPUSE stream that is open but to the wrong server.
459 */
460 stream = sp_stream_get(mailbox, SP_TEMPUSE);
461 if(stream){
462 /*
463 * We will close this stream and use the empty slot
464 * that that creates.
465 */
466 dprint((9,
467 "pine_mail_open: close a TEMPUSE stream and re-use that slot\n"));
468 pine_mail_actually_close(stream);
469 stream = NULL;
470 }
471 else{
472
473 /*
474 * Still no luck. Look for a stream open to the same
475 * server that is just not locked. This would be a
476 * stream that might be reusable in the future, but we
477 * need it now instead.
478 */
479 stream = sp_stream_get(mailbox, SP_SAME | SP_UNLOCKED);
480 if(stream){
481 dprint((9,
482 "pine_mail_open: attempting to re-use stream\n"));
483 }
484 else{
485 /*
486 * We'll take any UNLOCKED stream and re-use it.
487 */
488 stream = sp_stream_get(mailbox, 0);
489 if(stream){
490 /*
491 * We will close this stream and use the empty slot
492 * that that creates.
493 */
494 dprint((9,
495 "pine_mail_open: close an UNLOCKED stream and re-use the slot\n"));
496 pine_mail_actually_close(stream);
497 stream = NULL;
498 }
499 else{
500 if(ps_global->s_pool.max_remstream){
501 dprint((9, "pine_mail_open: all USEPOOL slots full of LOCKED streams, nothing to use\n"));
502 }
503 else{
504 dprint((9, "pine_mail_open: no caching, max_remstream == 0\n"));
505 }
506
507 usepool = 0;
508 tempuse = 0;
509 if(permlocked){
510 permlocked = 0;
511 dprint((2,
512 "pine_mail_open5: Can't mark folder Stay-Open: at max-remote limit\n"));
513 q_status_message1(SM_ORDER, 3, 5,
514 "Can't mark folder Stay-Open: reached max-remote limit (%s)",
515 comatose((long) ps_global->s_pool.max_remstream));
516 }
517 }
518 }
519 }
520 }
521 else{
522 dprint((9,
523 "pine_mail_open: there is an empty slot to use\n"));
524 }
525
526 /*
527 * We'll make an assumption here. If we were asked to halfopen a
528 * stream then we'll assume that the caller doesn't really care if
529 * the stream is halfopen or if it happens to be open to some mailbox
530 * already. They are just saying halfopen because they don't need to
531 * SELECT a mailbox. That's the assumption, anyway.
532 */
533 if(openflags & OP_HALFOPEN && stream){
534 dprint((9,
535 "pine_mail_open: asked for HALFOPEN so returning stream as is\n"));
536 sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
537 dprint((7,
538 "pine_mail_open: halfopen: ref cnt up to %d\n",
539 sp_ref_cnt(stream)));
540 if(stream && stream->dtb && stream->dtb->name
541 && !strcmp(stream->dtb->name, "imap")){
542 dprint((7, "pine_mail_open: next TAG %08lx\n",
543 stream->gensym));
544 }
545
546 if(stream->silent && !(openflags & OP_SILENT))
547 stream->silent = NIL;
548
549 return(stream);
550 }
551
552 /*
553 * We're going to SELECT another folder with this open stream.
554 */
555 if(stream){
556 /*
557 * We will have just pinged the stream to make sure it is
558 * still alive. That ping may have discovered some new mail.
559 * Before unselecting the folder, we should process the filters
560 * for that new mail.
561 */
562 if(!sp_flagged(stream, SP_LOCKED)
563 && !sp_flagged(stream, SP_USERFLDR)
564 && sp_new_mail_count(stream))
565 process_filter_patterns(stream, sp_msgmap(stream),
566 sp_new_mail_count(stream));
567
568 if(stream && stream->dtb && stream->dtb->name
569 && !strcmp(stream->dtb->name, "imap")){
570 dprint((7,
571 "pine_mail_open: cancel idle timer: TAG %08lx (%s)\n",
572 stream->gensym, debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
573 }
574
575 /*
576 * We need to reset the counters and everything.
577 * The easiest way to do that is just to delete all of the
578 * sp_s data and let the open fill it in correctly for
579 * the new folder.
580 */
581 sp_free((PER_STREAM_S **) &stream->sparep);
582 }
583 }
584
585 /*
586 * When we pass a stream to mail_open, it will either re-use it or
587 * close it.
588 */
589 retstream = mail_open(stream, mailbox, openflags);
590
591 if(retstream){
592
593 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));
594
595 /*
596 * So it is easier to figure out which command goes with which
597 * stream when debugging, change the tag associated with each stream.
598 * Of course, this will come after the SELECT, so the startup IMAP
599 * commands will have confusing tags.
600 */
601 if(retstream != stream && retstream->dtb && retstream->dtb->name
602 && !strcmp(retstream->dtb->name, "imap")){
603 retstream->gensym += (streamcounter * 0x1000000);
604 streamcounter = (streamcounter + 1) % (8 * 16);
605 }
606
607 if(retstream && retstream->dtb && retstream->dtb->name
608 && !strcmp(retstream->dtb->name, "imap")){
609 dprint((7, "pine_mail_open: next TAG %08lx\n",
610 retstream->gensym));
611 }
612
613 /*
614 * Catch the case where our test up above (where usepool was set)
615 * did not notice that this was news, but that we can tell once
616 * we've opened the stream. (One such case would be an imap proxy
617 * to an nntp server.) Remove it from being cached here. There was
618 * a possible penalty for not noticing sooner. If all the usepool
619 * slots were full, we will have closed one of the UNLOCKED streams
620 * above, preventing us from future re-use of that stream.
621 * We could figure out how to do the test better with just the
622 * name. We could open the stream and then close the other one
623 * after the fact. Or we could just not worry about it since it is
624 * rare.
625 */
626 if(IS_NEWS(retstream)){
627 usepool = 0;
628 tempuse = 0;
629 }
630
631 /* make sure the message map has been instantiated */
632 (void) sp_msgmap(retstream);
633
634 /*
635 * If a referral during the mail_open above causes a stream
636 * re-use which involves a BYE on an IMAP stream, then that
637 * BYE will have caused an mm_notify which will have
638 * instantiated the sp_data and set dead_stream! Trust that
639 * it is alive up to here and reset it to zero.
640 */
641 sp_set_dead_stream(retstream, 0);
642
643 flags = SP_LOCKED
644 | (usepool ? SP_USEPOOL : 0)
645 | (permlocked ? SP_PERMLOCKED : 0)
646 | (is_inbox ? SP_INBOX : 0)
647 | (uf ? SP_USERFLDR : 0)
648 | (tempuse ? SP_TEMPUSE : 0);
649
650 sp_flag(retstream, flags);
651 sp_set_recent_since_visited(retstream, retstream->recent);
652
653 /* initialize the reference count */
654 sp_set_ref_cnt(retstream, 1);
655 dprint((7, "pine_mail_open: reset: ref cnt set to %d\n",
656 sp_ref_cnt(retstream)));
657
658 if(sp_add(retstream, usepool) != 0 && usepool){
659 usepool = 0;
660 tempuse = 0;
661 flags = SP_LOCKED
662 | (usepool ? SP_USEPOOL : 0)
663 | (permlocked ? SP_PERMLOCKED : 0)
664 | (is_inbox ? SP_INBOX : 0)
665 | (uf ? SP_USERFLDR : 0)
666 | (tempuse ? SP_TEMPUSE : 0);
667
668 sp_flag(retstream, flags);
669 (void) sp_add(retstream, usepool);
670 }
671
672
673 /*
674 * When opening a newsgroup, c-client marks the messages up to the
675 * last Deleted as Unseen. If the feature news-approximates-new-status
676 * is on, we'd rather they be treated as Seen. That way, selecting New
677 * messages will give us the ones past the last Deleted. So we're going
678 * to change them to Seen. Since Seen is a session flag for news, making
679 * this change won't have any permanent effect. C-client also marks the
680 * messages after the last deleted Recent, which is the bit of
681 * information we'll use to find the messages we want to change.
682 */
683 if(F_ON(F_FAKE_NEW_IN_NEWS, ps_global) &&
684 retstream->nmsgs > 0 && IS_NEWS(retstream)){
685 char *seq;
686 long i, mflags = ST_SET;
687 MESSAGECACHE *mc;
688
689 /*
690 * Search for !recent messages to set the searched bit for
691 * those messages we want to change. Then we'll flip the bits.
692 */
693 (void)count_flagged(retstream, F_UNRECENT);
694
695 for(i = 1L; i <= retstream->nmsgs; i++)
696 if((mc = mail_elt(retstream,i)) && mc->searched)
697 mc->sequence = 1;
698 else
699 mc->sequence = 0;
700
701 if(!is_imap_stream(retstream))
702 mflags |= ST_SILENT;
703 if((seq = build_sequence(retstream, NULL, NULL)) != NULL){
704 mail_flag(retstream, seq, "\\SEEN", mflags);
705 fs_give((void **)&seq);
706 }
707 }
708 }
709
710 return(retstream);
711 }
712
713
714 void
reset_stream_view_state(MAILSTREAM * stream)715 reset_stream_view_state(MAILSTREAM *stream)
716 {
717 MSGNO_S *mm;
718
719 if(!stream)
720 return;
721
722 mm = sp_msgmap(stream);
723
724 if(!mm)
725 return;
726
727 sp_set_viewing_a_thread(stream, 0);
728 sp_set_need_to_rethread(stream, 0);
729
730 mn_reset_cur(mm, stream->nmsgs > 0L ? stream->nmsgs : 0L); /* default */
731
732 mm->visible_threads = -1L;
733 mm->top = 0L;
734 mm->max_thrdno = 0L;
735 mm->top_after_thrd = 0L;
736
737 mn_set_mansort(mm, 0);
738
739 /*
740 * Get rid of zooming and selections, but leave filtering flags. All the
741 * flag counts and everything should still be correct because set_lflag
742 * preserves them correctly.
743 */
744 if(any_lflagged(mm, MN_SLCT | MN_HIDE)){
745 long i;
746
747 for(i = 1L; i <= mn_get_total(mm); i++)
748 set_lflag(stream, mm, i, MN_SLCT | MN_HIDE, 0);
749 }
750
751 /*
752 * We could try to set up a default sort order, but the caller is going
753 * to re-sort anyway if they are interested in sorting. So we won't do
754 * it here.
755 */
756 }
757
758
759 /*
760 * We have to be careful when we change the flags of an already
761 * open stream, because there may be more than one section of
762 * code actively using the stream.
763 * We allow turning on (but not off) SP_LOCKED
764 * SP_PERMLOCKED
765 * SP_USERFLDR
766 * SP_INBOX
767 * We allow turning off (but not on) SP_TEMPUSE
768 */
769 void
carefully_reset_sp_flags(MAILSTREAM * stream,long unsigned int flags)770 carefully_reset_sp_flags(MAILSTREAM *stream, long unsigned int flags)
771 {
772 if(sp_flags(stream) != flags){
773 /* allow turning on but not off */
774 if(sp_flags(stream) & SP_LOCKED && !(flags & SP_LOCKED))
775 flags |= SP_LOCKED;
776
777 if(sp_flags(stream) & SP_PERMLOCKED && !(flags & SP_PERMLOCKED))
778 flags |= SP_PERMLOCKED;
779
780 if(sp_flags(stream) & SP_USERFLDR && !(flags & SP_USERFLDR))
781 flags |= SP_USERFLDR;
782
783 if(sp_flags(stream) & SP_INBOX && !(flags & SP_INBOX))
784 flags |= SP_INBOX;
785
786
787 /* allow turning off but not on */
788 if(!(sp_flags(stream) & SP_TEMPUSE) && flags & SP_TEMPUSE)
789 flags &= ~SP_TEMPUSE;
790
791
792 /* leave the way they already are */
793 if((sp_flags(stream) & SP_FILTERED) != (flags & SP_FILTERED))
794 flags = (flags & ~SP_FILTERED) | (sp_flags(stream) & SP_FILTERED);
795
796
797 if(sp_flags(stream) != flags)
798 sp_flag(stream, flags);
799 }
800 }
801
802
803 /*
804 * Pine wrapper around mail_create. If we have the PREFER_ALT_AUTH flag turned
805 * on we don't want to pass a NULL stream to c-client because it will open
806 * a non-ssl connection when we want it to be ssl.
807 */
808 long
pine_mail_create(MAILSTREAM * stream,char * mailbox)809 pine_mail_create(MAILSTREAM *stream, char *mailbox)
810 {
811 MAILSTREAM *ourstream = NULL;
812 long return_val;
813 long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
814 char source[MAILTMPLEN], *target = NULL;
815 DRIVER *d;
816
817 dprint((7, "pine_mail_create: creating \"%s\"%s\n",
818 mailbox ? mailbox : "(NULL)",
819 stream ? "" : " (stream was NULL)"));
820
821 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
822 mailbox = target;
823 dprint((7,
824 "pine_mail_create: #move special case, creating \"%s\"\n",
825 mailbox ? mailbox : "(NULL)"));
826 }
827
828 /*
829 * We don't really need this anymore, since we are now using IMAPTRYALT.
830 * We'll leave it since it works.
831 */
832 if((F_ON(F_PREFER_ALT_AUTH, ps_global)
833 || (ps_global->debug_imap > 3 || ps_global->debugmem))){
834
835 if((d = mail_valid (NIL, mailbox, (char *) NIL))
836 && !strcmp(d->name, "imap")){
837
838 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
839 openflags |= OP_TRYALT;
840 }
841 }
842
843 if(!stream)
844 stream = sp_stream_get(mailbox, SP_MATCH);
845 if(!stream)
846 stream = sp_stream_get(mailbox, SP_SAME);
847
848 if(!stream){
849 /*
850 * It is only useful to open a stream in the imap case.
851 */
852 if((d = mail_valid (NIL, mailbox, (char *) NIL))
853 && !strcmp(d->name, "imap")){
854
855 stream = pine_mail_open(NULL, mailbox, openflags, NULL);
856 ourstream = stream;
857 }
858 }
859
860 return_val = mail_create(stream, mailbox);
861
862 if(ourstream)
863 pine_mail_close(ourstream);
864
865 return(return_val);
866 }
867
868
869 /*
870 * Pine wrapper around mail_delete.
871 */
872 long
pine_mail_delete(MAILSTREAM * stream,char * mailbox)873 pine_mail_delete(MAILSTREAM *stream, char *mailbox)
874 {
875 MAILSTREAM *ourstream = NULL;
876 long return_val;
877 long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
878 char source[MAILTMPLEN], *target = NULL;
879 DRIVER *d;
880
881 dprint((7, "pine_mail_delete: deleting \"%s\"%s\n",
882 mailbox ? mailbox : "(NULL)",
883 stream ? "" : " (stream was NULL)"));
884
885 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
886 mailbox = target;
887 dprint((7,
888 "pine_mail_delete: #move special case, deleting \"%s\"\n",
889 mailbox ? mailbox : "(NULL)"));
890 }
891
892 /*
893 * We don't really need this anymore, since we are now using IMAPTRYALT.
894 */
895 if((F_ON(F_PREFER_ALT_AUTH, ps_global)
896 || (ps_global->debug_imap > 3 || ps_global->debugmem))){
897
898 if((d = mail_valid (NIL, mailbox, (char *) NIL))
899 && !strcmp(d->name, "imap")){
900
901 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
902 openflags |= OP_TRYALT;
903 }
904 }
905
906 /* oops, we seem to be deleting a selected stream */
907 if(!stream && (stream = sp_stream_get(mailbox, SP_MATCH))){
908 pine_mail_actually_close(stream);
909 stream = NULL;
910 }
911
912 if(!stream)
913 stream = sp_stream_get(mailbox, SP_SAME);
914
915 if(!stream){
916 /*
917 * It is only useful to open a stream in the imap case.
918 */
919 if((d = mail_valid (NIL, mailbox, (char *) NIL))
920 && !strcmp(d->name, "imap")){
921
922 stream = pine_mail_open(NULL, mailbox, openflags, NULL);
923 ourstream = stream;
924 }
925 }
926
927 return_val = mail_delete(stream, mailbox);
928
929 if(ourstream)
930 pine_mail_close(ourstream);
931
932 return(return_val);
933 }
934
935
936 /*
937 * Pine wrapper around mail_append.
938 */
939 long
pine_mail_append_full(MAILSTREAM * stream,char * mailbox,char * flags,char * date,STRING * message)940 pine_mail_append_full(MAILSTREAM *stream, char *mailbox, char *flags, char *date, STRING *message)
941 {
942 MAILSTREAM *ourstream = NULL;
943 long return_val;
944 long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
945 char source[MAILTMPLEN], *target = NULL;
946 char mailbox_nodelim[MAILTMPLEN];
947 int delim;
948 DRIVER *d;
949
950 dprint((7, "pine_mail_append_full: appending to \"%s\"%s\n",
951 mailbox ? mailbox : "(NULL)",
952 stream ? "" : " (stream was NULL)"));
953
954 /* strip delimiter, it has no meaning in an APPEND and could cause trouble */
955 if(mailbox && (delim = get_folder_delimiter(mailbox)) != '\0'){
956 size_t len;
957
958 len = strlen(mailbox);
959 if(mailbox[len-1] == delim){
960 strncpy(mailbox_nodelim, mailbox, MIN(len-1,sizeof(mailbox_nodelim)-1));
961 mailbox_nodelim[MIN(len-1,sizeof(mailbox_nodelim)-1)] = '\0';
962 mailbox = mailbox_nodelim;
963 }
964 }
965
966 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
967 mailbox = target;
968 dprint((7,
969 "pine_mail_append_full: #move special case, appending to \"%s\"\n",
970 mailbox ? mailbox : "(NULL)"));
971 }
972
973 /*
974 * We don't really need this anymore, since we are now using IMAPTRYALT.
975 */
976 if((F_ON(F_PREFER_ALT_AUTH, ps_global)
977 || (ps_global->debug_imap > 3 || ps_global->debugmem))){
978
979 if((d = mail_valid (NIL, mailbox, (char *) NIL))
980 && !strcmp(d->name, "imap")){
981
982 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
983 openflags |= OP_TRYALT;
984 }
985 }
986
987 if(!stream)
988 stream = sp_stream_get(mailbox, SP_MATCH);
989 if(!stream)
990 stream = sp_stream_get(mailbox, SP_SAME);
991
992 if(!stream){
993 /*
994 * It is only useful to open a stream in the imap case.
995 */
996 if((d = mail_valid (NIL, mailbox, (char *) NIL))
997 && !strcmp(d->name, "imap")){
998
999 stream = pine_mail_open(NULL, mailbox, openflags, NULL);
1000 ourstream = stream;
1001 }
1002 }
1003
1004 return_val = mail_append_full(stream, mailbox, flags, date, message);
1005
1006 if(ourstream)
1007 pine_mail_close(ourstream);
1008
1009 return(return_val);
1010 }
1011
1012
1013 /*
1014 * Pine wrapper around mail_append.
1015 */
1016 long
pine_mail_append_multiple(MAILSTREAM * stream,char * mailbox,append_t af,APPENDPACKAGE * data,MAILSTREAM * not_this_stream)1017 pine_mail_append_multiple(MAILSTREAM *stream, char *mailbox, append_t af,
1018 APPENDPACKAGE *data, MAILSTREAM *not_this_stream)
1019 {
1020 MAILSTREAM *ourstream = NULL;
1021 long return_val;
1022 long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
1023 char source[MAILTMPLEN], *target = NULL;
1024 DRIVER *d;
1025 int we_blocked_reuse = 0;
1026
1027 dprint((7, "pine_mail_append_multiple: appending to \"%s\"%s\n",
1028 mailbox ? mailbox : "(NULL)",
1029 stream ? "" : " (stream was NULL)"));
1030
1031 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
1032 mailbox = target;
1033 dprint((7,
1034 "pine_mail_append_multiple: #move special case, appending to \"%s\"\n",
1035 mailbox ? mailbox : "(NULL)"));
1036 }
1037
1038 if((F_ON(F_PREFER_ALT_AUTH, ps_global)
1039 || (ps_global->debug_imap > 3 || ps_global->debugmem))){
1040
1041 if((d = mail_valid (NIL, mailbox, (char *) NIL))
1042 && !strcmp(d->name, "imap")){
1043
1044 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1045 openflags |= OP_TRYALT;
1046 }
1047 }
1048
1049 /*
1050 * We have to be careful re-using streams for multiappend, because of
1051 * the way it works. We call into c-client below but part of the call
1052 * is data containing a callback function to us to supply the data to
1053 * be appended. That function may need to get the data from the server.
1054 * If that uses the same stream as we're trying to append on, we're
1055 * in trouble. We can't call back into c-client from c-client on the same
1056 * stream. (Just think about it, we're in the middle of an APPEND command.
1057 * We can't issue a FETCH before the APPEND completes in order to complete
1058 * the APPEND.) We can re-use a stream if it is a different stream from
1059 * the one we are reading from, so that's what the not_this_stream
1060 * stuff is for. If we mark it !SP_USEPOOL, it won't get reused.
1061 */
1062 if(sp_flagged(not_this_stream, SP_USEPOOL)){
1063 we_blocked_reuse++;
1064 sp_unflag(not_this_stream, SP_USEPOOL);
1065 }
1066
1067 if(!stream)
1068 stream = sp_stream_get(mailbox, SP_MATCH);
1069 if(!stream)
1070 stream = sp_stream_get(mailbox, SP_SAME);
1071
1072 if(!stream){
1073 /*
1074 * It is only useful to open a stream in the imap case.
1075 */
1076 if((d = mail_valid (NIL, mailbox, (char *) NIL))
1077 && !strcmp(d->name, "imap")){
1078
1079 stream = pine_mail_open(NULL, mailbox, openflags, NULL);
1080 ourstream = stream;
1081 }
1082 }
1083
1084 if(we_blocked_reuse)
1085 sp_set_flags(not_this_stream, sp_flags(not_this_stream) | SP_USEPOOL);
1086
1087 return_val = mail_append_multiple(stream, mailbox, af, (void *) data);
1088
1089 if(ourstream)
1090 pine_mail_close(ourstream);
1091
1092 return(return_val);
1093 }
1094
1095
1096 /*
1097 * Pine wrapper around mail_copy.
1098 */
1099 long
pine_mail_copy_full(MAILSTREAM * stream,char * sequence,char * mailbox,long int options)1100 pine_mail_copy_full(MAILSTREAM *stream, char *sequence, char *mailbox, long int options)
1101 {
1102 MAILSTREAM *ourstream = NULL;
1103 long return_val;
1104 long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
1105 char source[MAILTMPLEN], *target = NULL;
1106 char mailbox_nodelim[MAILTMPLEN];
1107 int delim;
1108 DRIVER *d;
1109
1110 dprint((7, "pine_mail_copy_full: copying to \"%s\"%s\n",
1111 mailbox ? mailbox : "(NULL)",
1112 stream ? "" : " (stream was NULL)"));
1113
1114 /* strip delimiter, it has no meaning in a COPY and could cause trouble */
1115 if(mailbox && (delim = get_folder_delimiter(mailbox)) != '\0'){
1116 size_t len;
1117
1118 len = strlen(mailbox);
1119 if(mailbox[len-1] == delim){
1120 strncpy(mailbox_nodelim, mailbox, MIN(len-1,sizeof(mailbox_nodelim)-1));
1121 mailbox_nodelim[MIN(len-1,sizeof(mailbox_nodelim)-1)] = '\0';
1122 mailbox = mailbox_nodelim;
1123 }
1124 }
1125
1126 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
1127 mailbox = target;
1128 dprint((7,
1129 "pine_mail_copy_full: #move special case, copying to \"%s\"\n",
1130 mailbox ? mailbox : "(NULL)"));
1131 }
1132
1133 /*
1134 * We don't really need this anymore, since we are now using IMAPTRYALT.
1135 */
1136 if((F_ON(F_PREFER_ALT_AUTH, ps_global)
1137 || (ps_global->debug_imap > 3 || ps_global->debugmem))){
1138
1139 if((d = mail_valid (NIL, mailbox, (char *) NIL))
1140 && !strcmp(d->name, "imap")){
1141
1142 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1143 openflags |= OP_TRYALT;
1144 }
1145 }
1146
1147 if(!stream)
1148 stream = sp_stream_get(mailbox, SP_MATCH);
1149 if(!stream)
1150 stream = sp_stream_get(mailbox, SP_SAME);
1151
1152 if(!stream){
1153 /*
1154 * It is only useful to open a stream in the imap case.
1155 * Actually, mail_copy_full is the case where it might also be
1156 * useful to provide a stream in the nntp and pop3 cases. If we
1157 * cache such streams, then we will probably want to open one
1158 * here so that it gets cached.
1159 */
1160 if((d = mail_valid (NIL, mailbox, (char *) NIL))
1161 && !strcmp(d->name, "imap")){
1162
1163 stream = pine_mail_open(NULL, mailbox, openflags, NULL);
1164 ourstream = stream;
1165 }
1166 }
1167
1168 return_val = mail_copy_full(stream, sequence, mailbox, options);
1169
1170 if(ourstream)
1171 pine_mail_close(ourstream);
1172
1173 return(return_val);
1174 }
1175
1176
1177 /*
1178 * Pine wrapper around mail_rename.
1179 */
1180 long
pine_mail_rename(MAILSTREAM * stream,char * old,char * new)1181 pine_mail_rename(MAILSTREAM *stream, char *old, char *new)
1182 {
1183 MAILSTREAM *ourstream = NULL;
1184 long return_val;
1185 long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
1186 DRIVER *d;
1187
1188 dprint((7, "pine_mail_rename(%s,%s)\n", old ? old : "",
1189 new ? new : ""));
1190
1191 /*
1192 * We don't really need this anymore, since we are now using IMAPTRYALT.
1193 */
1194 if((F_ON(F_PREFER_ALT_AUTH, ps_global)
1195 || (ps_global->debug_imap > 3 || ps_global->debugmem))){
1196
1197 if((d = mail_valid (NIL, old, (char *) NIL))
1198 && !strcmp(d->name, "imap")){
1199
1200 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1201 openflags |= OP_TRYALT;
1202 }
1203 }
1204
1205 /* oops, we seem to be renaming a selected stream */
1206 if(!stream && (stream = sp_stream_get(old, SP_MATCH))){
1207 pine_mail_actually_close(stream);
1208 stream = NULL;
1209 }
1210
1211 if(!stream)
1212 stream = sp_stream_get(old, SP_SAME);
1213
1214 if(!stream){
1215 /*
1216 * It is only useful to open a stream in the imap case.
1217 */
1218 if((d = mail_valid (NIL, old, (char *) NIL))
1219 && !strcmp(d->name, "imap")){
1220
1221 stream = pine_mail_open(NULL, old, openflags, NULL);
1222 ourstream = stream;
1223 }
1224 }
1225
1226 return_val = mail_rename(stream, old, new);
1227
1228 if(ourstream)
1229 pine_mail_close(ourstream);
1230
1231 return(return_val);
1232 }
1233
1234
1235 /*----------------------------------------------------------------------
1236 Our mail_close wrapper to clean up anything on the mailstream we may have
1237 added to it. mostly in the unused bits of the elt's.
1238 ----*/
1239 void
pine_mail_close(MAILSTREAM * stream)1240 pine_mail_close(MAILSTREAM *stream)
1241 {
1242 unsigned long uid_last, last_uid;
1243 int refcnt;
1244
1245 if(!stream)
1246 return;
1247
1248 dprint((7, "pine_mail_close: %s (%s)\n",
1249 stream && stream->mailbox ? stream->mailbox : "(NULL)",
1250 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
1251
1252 if(sp_flagged(stream, SP_USEPOOL) && !sp_dead_stream(stream)){
1253
1254 refcnt = sp_ref_cnt(stream);
1255 dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
1256
1257 /*
1258 * Instead of checkpointing here, which takes time that the user
1259 * definitely notices, we checkpoint in new_mail at the next
1260 * opportune time, hopefully when the user is idle.
1261 */
1262 #if 0
1263 if(sp_flagged(stream, SP_LOCKED) && sp_flagged(stream, SP_USERFLDR)
1264 && !stream->halfopen && refcnt <= 1){
1265 if(changes_to_checkpoint(stream))
1266 pine_mail_check(stream);
1267 else{
1268 dprint((7,
1269 "pine_mail_close: dont think we need to checkpoint\n"));
1270 }
1271 }
1272 #endif
1273
1274 /*
1275 * Uid_last is valid when we first open a stream, but not always
1276 * valid after that. So if we know the last uid should be higher
1277 * than uid_last (!#%) use that instead.
1278 */
1279 uid_last = stream->uid_last;
1280 if(stream->nmsgs > 0L
1281 && (last_uid=mail_uid(stream,stream->nmsgs)) > uid_last)
1282 uid_last = last_uid;
1283
1284 sp_set_saved_uid_validity(stream, stream->uid_validity);
1285 sp_set_saved_uid_last(stream, uid_last);
1286
1287 /*
1288 * If the reference count is down to 0, unlock it.
1289 * In any case, don't actually do any real closing.
1290 */
1291 if(refcnt > 0)
1292 sp_set_ref_cnt(stream, refcnt-1);
1293
1294 refcnt = sp_ref_cnt(stream);
1295 dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
1296 if(refcnt <= 0){
1297 dprint((7,
1298 "pine_mail_close: unlocking: start idle timer: TAG %08lx (%s)\n",
1299 stream->gensym, debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
1300 sp_set_last_use_time(stream, time(0));
1301
1302 /*
1303 * Logically, we ought to be unflagging SP_INBOX, too. However,
1304 * the filtering code uses SP_INBOX when deciding if it should
1305 * filter some things, and we keep filtering after the mailbox
1306 * is closed. So leave SP_INBOX alone. This (the closing of INBOX)
1307 * usually only happens in goodnight_gracey when we're
1308 * shutting everything down.
1309 */
1310 sp_unflag(stream, SP_LOCKED | SP_PERMLOCKED | SP_USERFLDR);
1311 }
1312 else{
1313 dprint((7, "pine_mail_close: ref cnt is now %d\n",
1314 refcnt));
1315 }
1316 }
1317 else{
1318 dprint((7, "pine_mail_close: %s\n",
1319 sp_flagged(stream, SP_USEPOOL) ? "dead stream" : "no pool"));
1320
1321 refcnt = sp_ref_cnt(stream);
1322 dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
1323
1324 /*
1325 * If the reference count is down to 0, unlock it.
1326 * In any case, don't actually do any real closing.
1327 */
1328 if(refcnt > 0)
1329 sp_set_ref_cnt(stream, refcnt-1);
1330
1331 refcnt = sp_ref_cnt(stream);
1332 if(refcnt <= 0){
1333 pine_mail_actually_close(stream);
1334 }
1335 else{
1336 dprint((7, "pine_mail_close: ref cnt is now %d\n",
1337 refcnt));
1338 }
1339
1340 }
1341 }
1342
1343
1344 void
pine_mail_actually_close(MAILSTREAM * stream)1345 pine_mail_actually_close(MAILSTREAM *stream)
1346 {
1347 if(!stream)
1348 return;
1349
1350 if(!sp_closing(stream)){
1351 dprint((7, "pine_mail_actually_close: %s (%s)\n",
1352 stream && stream->mailbox ? stream->mailbox : "(NULL)",
1353 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
1354
1355 sp_set_closing(stream, 1);
1356
1357 if(!sp_flagged(stream, SP_LOCKED)
1358 && !sp_flagged(stream, SP_USERFLDR)
1359 && !sp_dead_stream(stream)
1360 && sp_new_mail_count(stream))
1361 process_filter_patterns(stream, sp_msgmap(stream),
1362 sp_new_mail_count(stream));
1363 sp_delete(stream);
1364
1365 /*
1366 * let sp_free_callback() free the sp_s stuff and the callbacks to
1367 * free_pine_elt free the per-elt pine stuff.
1368 */
1369 mail_close(stream);
1370 }
1371 }
1372
1373
1374 /*
1375 * If we haven't used a stream for a while, we may want to logout.
1376 */
1377 void
maybe_kill_old_stream(MAILSTREAM * stream)1378 maybe_kill_old_stream(MAILSTREAM *stream)
1379 {
1380 #define KILL_IF_IDLE_TIME (25 * 60)
1381 if(stream
1382 && !sp_flagged(stream, SP_LOCKED)
1383 && !sp_flagged(stream, SP_USERFLDR)
1384 && time(0) - sp_last_use_time(stream) > KILL_IF_IDLE_TIME){
1385
1386 dprint((7,
1387 "killing idle stream: %s (%s): idle timer = %ld secs\n",
1388 stream && stream->mailbox ? stream->mailbox : "(NULL)",
1389 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress),
1390 (long) (time(0)-sp_last_use_time(stream))));
1391
1392 /*
1393 * Another thing we could do here instead is to unselect the
1394 * mailbox, leaving the stream open to the server.
1395 */
1396 pine_mail_actually_close(stream);
1397 }
1398 }
1399
1400
1401 /*
1402 * Catch searches that don't need to go to the server.
1403 * (Not anymore, now c-client does this for us.)
1404 */
1405 long
pine_mail_search_full(MAILSTREAM * stream,char * charset,SEARCHPGM * pgm,long int flags)1406 pine_mail_search_full(MAILSTREAM *stream, char *charset,
1407 SEARCHPGM *pgm, long int flags)
1408 {
1409 /*
1410 * The charset should either be UTF-8 or NULL for alpine.
1411 * If it is UTF-8 we may be able to change it to NULL if
1412 * everything in the search is actually ascii. We try to
1413 * do this because not all servers understand the CHARSET
1414 * parameter.
1415 */
1416 if(charset && !strucmp(charset, "utf-8")
1417 && is_imap_stream(stream) && !hibit_in_searchpgm(pgm))
1418 charset = NULL;
1419
1420 if(F_ON(F_NNTP_SEARCH_USES_OVERVIEW, ps_global))
1421 flags |= SO_OVERVIEW;
1422
1423 return(stream ? mail_search_full(stream, charset, pgm, flags) : NIL);
1424 }
1425
1426
1427 int
hibit_in_searchpgm(SEARCHPGM * pgm)1428 hibit_in_searchpgm(SEARCHPGM *pgm)
1429 {
1430 SEARCHOR *or;
1431 SEARCHPGMLIST *not;
1432
1433 if(!pgm)
1434 return 0;
1435
1436 if((pgm->subject && hibit_in_strlist(pgm->subject))
1437 || (pgm->text && hibit_in_strlist(pgm->text))
1438 || (pgm->body && hibit_in_strlist(pgm->body))
1439 || (pgm->cc && hibit_in_strlist(pgm->cc))
1440 || (pgm->from && hibit_in_strlist(pgm->from))
1441 || (pgm->to && hibit_in_strlist(pgm->to))
1442 || (pgm->bcc && hibit_in_strlist(pgm->bcc))
1443 || (pgm->keyword && hibit_in_strlist(pgm->keyword))
1444 || (pgm->unkeyword && hibit_in_strlist(pgm->return_path))
1445 || (pgm->sender && hibit_in_strlist(pgm->sender))
1446 || (pgm->reply_to && hibit_in_strlist(pgm->reply_to))
1447 || (pgm->in_reply_to && hibit_in_strlist(pgm->in_reply_to))
1448 || (pgm->message_id && hibit_in_strlist(pgm->message_id))
1449 || (pgm->newsgroups && hibit_in_strlist(pgm->newsgroups))
1450 || (pgm->followup_to && hibit_in_strlist(pgm->followup_to))
1451 || (pgm->references && hibit_in_strlist(pgm->references))
1452 || (pgm->header && hibit_in_header(pgm->header)))
1453 return 1;
1454
1455 for(or = pgm->or; or; or = or->next)
1456 if(hibit_in_searchpgm(or->first) || hibit_in_searchpgm(or->second))
1457 return 1;
1458
1459 for(not = pgm->not; not; not = not->next)
1460 if(hibit_in_searchpgm(not->pgm))
1461 return 1;
1462
1463 return 0;
1464 }
1465
1466
1467 int
hibit_in_strlist(STRINGLIST * sl)1468 hibit_in_strlist(STRINGLIST *sl)
1469 {
1470 for(; sl; sl = sl->next)
1471 if(hibit_in_sizedtext(&sl->text))
1472 return 1;
1473
1474 return 0;
1475 }
1476
1477
1478 int
hibit_in_header(SEARCHHEADER * header)1479 hibit_in_header(SEARCHHEADER *header)
1480 {
1481 SEARCHHEADER *hdr;
1482
1483 for(hdr = header; hdr; hdr = hdr->next)
1484 if(hibit_in_sizedtext(&hdr->line) || hibit_in_sizedtext(&hdr->text))
1485 return 1;
1486
1487 return 0;
1488 }
1489
1490
1491 int
hibit_in_sizedtext(SIZEDTEXT * st)1492 hibit_in_sizedtext(SIZEDTEXT *st)
1493 {
1494 unsigned char *p, *end;
1495
1496 p = st ? st->data : NULL;
1497 if(p)
1498 for(end = p + st->size; p < end; p++)
1499 if(*p & 0x80)
1500 return 1;
1501
1502 return 0;
1503 }
1504
1505
1506 void
pine_mail_fetch_flags(MAILSTREAM * stream,char * sequence,long int flags)1507 pine_mail_fetch_flags(MAILSTREAM *stream, char *sequence, long int flags)
1508 {
1509 ps_global->dont_count_flagchanges = 1;
1510 mail_fetch_flags(stream, sequence, flags);
1511 ps_global->dont_count_flagchanges = 0;
1512 }
1513
1514
1515 ENVELOPE *
pine_mail_fetchenvelope(MAILSTREAM * stream,long unsigned int msgno)1516 pine_mail_fetchenvelope(MAILSTREAM *stream, long unsigned int msgno)
1517 {
1518 ENVELOPE *env = NULL;
1519
1520 ps_global->dont_count_flagchanges = 1;
1521 if(stream && msgno > 0L && msgno <= stream->nmsgs)
1522 env = mail_fetchenvelope(stream, msgno);
1523
1524 ps_global->dont_count_flagchanges = 0;
1525 return(env);
1526 }
1527
1528
1529 ENVELOPE *
pine_mail_fetch_structure(MAILSTREAM * stream,long unsigned int msgno,struct mail_bodystruct ** body,long int flags)1530 pine_mail_fetch_structure(MAILSTREAM *stream, long unsigned int msgno,
1531 struct mail_bodystruct **body, long int flags)
1532 {
1533 ENVELOPE *env = NULL;
1534
1535 ps_global->dont_count_flagchanges = 1;
1536 if(stream && (flags & FT_UID || (msgno > 0L && msgno <= stream->nmsgs)))
1537 env = mail_fetch_structure(stream, msgno, body, flags);
1538
1539 ps_global->dont_count_flagchanges = 0;
1540 return(env);
1541 }
1542
1543
1544 ENVELOPE *
pine_mail_fetchstructure(MAILSTREAM * stream,long unsigned int msgno,struct mail_bodystruct ** body)1545 pine_mail_fetchstructure(MAILSTREAM *stream, long unsigned int msgno, struct mail_bodystruct **body)
1546 {
1547 ENVELOPE *env = NULL;
1548
1549 ps_global->dont_count_flagchanges = 1;
1550 if(stream && msgno > 0L && msgno <= stream->nmsgs)
1551 env = mail_fetchstructure(stream, msgno, body);
1552
1553 ps_global->dont_count_flagchanges = 0;
1554 return(env);
1555 }
1556
1557
1558 /*
1559 * Wrapper around mail_fetch_body.
1560 * Currently only used to turn on partial fetching if trying
1561 * to work around the microsoft ssl bug.
1562 */
1563 char *
pine_mail_fetch_body(MAILSTREAM * stream,long unsigned int msgno,char * section,long unsigned int * len,long int flags)1564 pine_mail_fetch_body(MAILSTREAM *stream, long unsigned int msgno, char *section,
1565 long unsigned int *len, long int flags)
1566 {
1567 #ifdef _WINDOWS
1568 if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global))
1569 return(pine_mail_partial_fetch_wrapper(stream, msgno,
1570 section, len, flags, 0, NULL, 0));
1571 else
1572 #endif
1573 return(mail_fetch_body(stream, msgno, section, len, flags));
1574 }
1575
1576 /*
1577 * Wrapper around mail_fetch_text.
1578 * Currently the only purpose this wrapper serves is to turn
1579 * on partial fetching for quell-ssl-largeblocks.
1580 */
1581 char *
pine_mail_fetch_text(MAILSTREAM * stream,long unsigned int msgno,char * section,long unsigned int * len,long int flags)1582 pine_mail_fetch_text(MAILSTREAM *stream, long unsigned int msgno, char *section,
1583 long unsigned int *len, long int flags)
1584 {
1585 #ifdef _WINDOWS
1586 if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global))
1587 return(pine_mail_partial_fetch_wrapper(stream, msgno,
1588 section, len, flags,
1589 0, NULL, 1));
1590 else
1591 #endif
1592 return(mail_fetch_text(stream, msgno, section, len, flags));
1593 }
1594
1595
1596 /*
1597 * Determine whether to do partial-fetching or not, and do it
1598 * args - same as c-client functions being wrapped around
1599 * get_n_bytes - try to partial fetch for the first n bytes.
1600 * makes no guarantees, might wind up fetching
1601 * the entire text anyway.
1602 * str_to_free - pointer to string to free if we only get
1603 * (non-cachable) partial text (required for
1604 * get_n_bytes).
1605 * is_text_fetch -
1606 * set, tells us to do mail_fetch_text and mail_partial_text
1607 * unset, tells us to do mail_fetch_body and mail_partial_body
1608 */
1609 char *
pine_mail_partial_fetch_wrapper(MAILSTREAM * stream,long unsigned int msgno,char * section,long unsigned int * len,long int flags,long unsigned int get_n_bytes,char ** str_to_free,int is_text_fetch)1610 pine_mail_partial_fetch_wrapper(MAILSTREAM *stream, long unsigned int msgno,
1611 char *section, long unsigned int *len,
1612 long int flags, long unsigned int get_n_bytes,
1613 char **str_to_free, int is_text_fetch)
1614 {
1615 BODY *body;
1616 unsigned long size, firstbyte, lastbyte;
1617 void *old_gets;
1618 FETCH_READC_S *pftc;
1619 char imap_cache_section[MAILTMPLEN];
1620 SIZEDTEXT new_text;
1621 MESSAGECACHE *mc;
1622 char *(*fetch_full)(MAILSTREAM *, unsigned long, char *,
1623 unsigned long *, long);
1624 long (*fetch_partial)(MAILSTREAM *, unsigned long, char *,
1625 unsigned long, unsigned long, long);
1626
1627 fetch_full = is_text_fetch ? mail_fetch_text : mail_fetch_body;
1628 fetch_partial = is_text_fetch ? mail_partial_text : mail_partial_body;
1629 if(str_to_free)
1630 *str_to_free = NULL;
1631 #ifdef _WINDOWS
1632 if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global) || get_n_bytes){
1633 #else
1634 if(get_n_bytes){
1635 #endif /* _WINDOWS */
1636 if(section && *section)
1637 body = mail_body(stream, msgno, (unsigned char *) section);
1638 else
1639 pine_mail_fetch_structure(stream, msgno, &body, flags);
1640 if(!body)
1641 return NULL;
1642 if(body->type != TYPEMULTIPART)
1643 size = body->size.bytes;
1644 else if((!section || !*section) && msgno > 0L
1645 && stream && msgno <= stream->nmsgs
1646 && (mc = mail_elt(stream, msgno)))
1647 size = mc->rfc822_size; /* upper bound */
1648 else /* just a guess, can't get actual size */
1649 size = fcc_size_guess(body) + 2048;
1650
1651 /*
1652 * imap_cache, originally intended for c-client internal use,
1653 * takes a section argument that is different from one we
1654 * would pass to mail_body. Typically in this function
1655 * section is NULL, which translates to "TEXT", but in other
1656 * cases we would want to append ".TEXT" to the section
1657 */
1658 if(is_text_fetch)
1659 snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s%sTEXT", MAILTMPLEN - 10,
1660 section && *section ? section : "",
1661 section && *section ? "." : "");
1662 else
1663 snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s", MAILTMPLEN - 10,
1664 section && *section ? section : "");
1665
1666 if(modern_imap_stream(stream)
1667 #ifdef _WINDOWS
1668 && ((size > AVOID_MICROSOFT_SSL_CHUNKING_BUG)
1669 || (get_n_bytes && size > get_n_bytes))
1670 #else
1671 && (get_n_bytes && size > get_n_bytes)
1672 #endif /* _WINDOWS */
1673 && !imap_cache(stream, msgno, imap_cache_section,
1674 NULL, NULL)){
1675 if(get_n_bytes == 0){
1676 dprint((8,
1677 "fetch_wrapper: doing partial fetching to work around microsoft bug\n"));
1678 }
1679 else{
1680 dprint((8,
1681 "fetch_wrapper: partial fetching due to %lu get_n_bytes\n", get_n_bytes));
1682 }
1683 pftc = (FETCH_READC_S *)fs_get(sizeof(FETCH_READC_S));
1684 memset(g_pft_desc = pftc, 0, sizeof(FETCH_READC_S));
1685 #ifdef _WINDOWS
1686 if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global)){
1687 if(get_n_bytes)
1688 pftc->chunksize = MIN(get_n_bytes,
1689 AVOID_MICROSOFT_SSL_CHUNKING_BUG);
1690 else
1691 pftc->chunksize = AVOID_MICROSOFT_SSL_CHUNKING_BUG;
1692 }
1693 else
1694 #endif /* _WINDOWS */
1695 pftc->chunksize = get_n_bytes;
1696
1697 pftc->chunk = (char *) fs_get((pftc->chunksize+1)
1698 * sizeof(char));
1699 pftc->cache = so_get(CharStar, NULL, EDIT_ACCESS);
1700 pftc->read = 0L;
1701 so_truncate(pftc->cache, size + 1);
1702 old_gets = mail_parameters(stream, GET_GETS, (void *)NULL);
1703 mail_parameters(stream, SET_GETS, (void *) partial_text_gets);
1704 /* start fetching */
1705 do{
1706 firstbyte = pftc->read ;
1707 lastbyte = firstbyte + pftc->chunksize;
1708 if(get_n_bytes > firstbyte && get_n_bytes < lastbyte){
1709 pftc->chunksize = get_n_bytes - firstbyte;
1710 lastbyte = get_n_bytes;
1711 }
1712 (*fetch_partial)(stream, msgno, section, firstbyte,
1713 pftc->chunksize, flags);
1714
1715 if(pftc->read != lastbyte)
1716 break;
1717 } while((pftc->read == lastbyte)
1718 && (!get_n_bytes || (pftc->read < get_n_bytes)));
1719 dprint((8,
1720 "fetch_wrapper: anticipated size=%lu read=%lu\n",
1721 size, pftc->read));
1722 mail_parameters(stream, SET_GETS, old_gets);
1723 new_text.size = pftc->read;
1724 new_text.data = (unsigned char *)so_text(pftc->cache);
1725
1726 if(get_n_bytes && pftc->read == get_n_bytes){
1727 /*
1728 * don't write to cache if we're only dealing with
1729 * partial text.
1730 */
1731 if(!str_to_free)
1732 alpine_panic("Programmer botch: partial fetch attempt w/o string pointer");
1733 else
1734 *str_to_free = (char *) new_text.data;
1735 }
1736 else {
1737 /* ugh, rewrite string in case it got stomped on last call */
1738 if(is_text_fetch)
1739 snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s%sTEXT", MAILTMPLEN - 10,
1740 section && *section ? section : "",
1741 section && *section ? "." : "");
1742 else
1743 snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s", MAILTMPLEN - 10,
1744 section && *section ? section : "");
1745
1746 imap_cache(stream, msgno, imap_cache_section, NULL, &new_text);
1747 }
1748
1749 pftc->cache->txt = (void *)NULL;
1750 so_give(&pftc->cache);
1751 fs_give((void **)&pftc->chunk);
1752 fs_give((void **)&pftc);
1753 g_pft_desc = NULL;
1754 if(len)
1755 *len = new_text.size;
1756 return ((char *)new_text.data);
1757 }
1758 else
1759 return((*fetch_full)(stream, msgno, section, len, flags));
1760 }
1761 else
1762 return((*fetch_full)(stream, msgno, section, len, flags));
1763 }
1764
1765 /*
1766 * c-client callback that handles getting the text
1767 */
1768 char *
1769 partial_text_gets(readfn_t f, void *stream, long unsigned int size, GETS_DATA *md)
1770 {
1771 unsigned long n;
1772
1773 n = MIN(g_pft_desc->chunksize, size);
1774 g_pft_desc->read +=n;
1775
1776 (*f) (stream, n, g_pft_desc->chunk);
1777
1778 if(g_pft_desc->cache)
1779 so_nputs(g_pft_desc->cache, g_pft_desc->chunk, (long) n);
1780
1781
1782 return(NULL);
1783 }
1784
1785
1786 /*
1787 * Pings the stream. Returns 0 if the stream is dead, non-zero otherwise.
1788 */
1789 long
1790 pine_mail_ping(MAILSTREAM *stream)
1791 {
1792 time_t now;
1793 long ret = 0L;
1794
1795 if(!sp_dead_stream(stream)){
1796 ret = mail_ping(stream);
1797 if(ret && sp_dead_stream(stream))
1798 ret = 0L;
1799 }
1800
1801 if(ret){
1802 now = time(0);
1803 sp_set_last_ping(stream, now);
1804 sp_set_last_expunged_reaper(stream, now);
1805 }
1806
1807 return(ret);
1808 }
1809
1810
1811 void
1812 pine_mail_check(MAILSTREAM *stream)
1813 {
1814 reset_check_point(stream);
1815 mail_check(stream);
1816 }
1817
1818
1819 /*
1820 * Unlike mail_list, this version returns a value. The returned value is
1821 * TRUE if the stream is opened ok, and FALSE if we failed opening the
1822 * stream. This allows us to differentiate between a LIST which returns
1823 * no matches and a failure opening the stream. We do this by pre-opening
1824 * the stream ourselves.
1825 */
1826 int
1827 pine_mail_list(MAILSTREAM *stream, char *ref, char *pat, unsigned int *options)
1828 {
1829 int we_open = 0;
1830 char *halfopen_target;
1831 char source[MAILTMPLEN], *target = NULL;
1832 MAILSTREAM *ourstream = NULL;
1833
1834 dprint((7, "pine_mail_list: ref=%s pat=%s%s\n",
1835 ref ? ref : "(NULL)",
1836 pat ? pat : "(NULL)",
1837 stream ? "" : " (stream was NULL)"));
1838
1839 if((!ref && check_for_move_mbox(pat, source, sizeof(source), &target))
1840 ||
1841 check_for_move_mbox(ref, source, sizeof(source), &target)){
1842 ref = NIL;
1843 pat = target;
1844 if(options)
1845 *options |= PML_IS_MOVE_MBOX;
1846
1847 dprint((7,
1848 "pine_mail_list: #move special case, listing \"%s\"%s\n",
1849 pat ? pat : "(NULL)",
1850 stream ? "" : " (stream was NULL)"));
1851 }
1852
1853 if(!stream && ((pat && *pat == '{') || (ref && *ref == '{'))){
1854 we_open++;
1855 if(pat && *pat == '{'){
1856 ref = NIL;
1857 halfopen_target = pat;
1858 }
1859 else
1860 halfopen_target = ref;
1861 }
1862
1863 if(we_open){
1864 long flags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
1865
1866 stream = sp_stream_get(halfopen_target, SP_MATCH);
1867 if(!stream)
1868 stream = sp_stream_get(halfopen_target, SP_SAME);
1869
1870 if(!stream){
1871 DRIVER *d;
1872
1873 /*
1874 * POP is a special case. We don't need to have a stream
1875 * to call mail_list for a POP name. The else part is the
1876 * POP part.
1877 */
1878 if((d = mail_valid(NIL, halfopen_target, (char *) NIL))
1879 && (d->flags & DR_HALFOPEN)){
1880 stream = pine_mail_open(NIL, halfopen_target, flags, NULL);
1881 ourstream = stream;
1882 if(!stream)
1883 return(FALSE);
1884 }
1885 else
1886 stream = NULL;
1887 }
1888
1889 mail_list_internal(stream, ref, pat);
1890 }
1891 else
1892 mail_list_internal(stream, ref, pat);
1893
1894 if(ourstream)
1895 pine_mail_close(ourstream);
1896
1897 return(TRUE);
1898 }
1899
1900
1901 /*
1902 * mail_list_internal -- A monument to software religion and those who
1903 * would force it upon us.
1904 */
1905 void
1906 mail_list_internal(MAILSTREAM *s, char *r, char *p)
1907 {
1908 if(F_ON(F_FIX_BROKEN_LIST, ps_global)
1909 && ((s && s->mailbox && *s->mailbox == '{')
1910 || (!s && ((r && *r == '{') || (p && *p == '{'))))){
1911 char tmp[2*MAILTMPLEN];
1912 /* MAILTMPLEN = sizeof(tmp)/2 */
1913 snprintf(tmp, sizeof(tmp), "%.*s%.*s", MAILTMPLEN-1, r ? r : "",
1914 MAILTMPLEN-1, p);
1915 mail_list(s, "", tmp);
1916 }
1917 else
1918 mail_list(s, r, p);
1919 }
1920
1921
1922 long
1923 pine_mail_status(MAILSTREAM *stream, char *mailbox, long int flags)
1924 {
1925 return(pine_mail_status_full(stream, mailbox, flags, NULL, NULL));
1926 }
1927
1928
1929 long
1930 pine_mail_status_full(MAILSTREAM *stream, char *mailbox, long int flags,
1931 imapuid_t *uidvalidity, imapuid_t *uidnext)
1932 {
1933 char source[MAILTMPLEN], *target = NULL;
1934 long ret = NIL;
1935 MAILSTATUS cache, status;
1936 MAILSTREAM *ourstream = NULL;
1937
1938 if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
1939 memset(&status, 0, sizeof(status));
1940 memset(&cache, 0, sizeof(cache));
1941
1942 /* tell mm_status() to write partial return here */
1943 pine_cached_status = &status;
1944
1945 flags |= (SA_UIDVALIDITY | SA_UIDNEXT | SA_MESSAGES);
1946
1947 /* do status of destination */
1948
1949 stream = sp_stream_get(target, SP_SAME);
1950
1951 /* should never be news, don't worry about mulnewrsc flag*/
1952 if((ret = pine_mail_status_full(stream, target, flags, uidvalidity,
1953 uidnext))
1954 && !status.recent){
1955
1956 /* do status of source */
1957 pine_cached_status = &cache;
1958 stream = sp_stream_get(source, SP_SAME);
1959
1960 if(!stream){
1961 DRIVER *d;
1962
1963 if((d = mail_valid (NIL, source, (char *) NIL))
1964 && !strcmp(d->name, "imap")){
1965 long openflags =OP_HALFOPEN|OP_SILENT|SP_USEPOOL|SP_TEMPUSE;
1966
1967 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
1968 openflags |= OP_TRYALT;
1969
1970 stream = pine_mail_open(NULL, source, openflags, NULL);
1971 ourstream = stream;
1972 }
1973 else if(F_ON(F_ENABLE_MULNEWSRCS, ps_global)
1974 && d && (!strucmp(d->name, "news")
1975 || !strucmp(d->name, "nntp")))
1976 flags |= SA_MULNEWSRC;
1977
1978 }
1979
1980 if(!ps_global->user_says_cancel && mail_status(stream, source, flags)){
1981 DRIVER *d;
1982 int is_news = 0;
1983
1984 /*
1985 * If the target has recent messages, then we don't come
1986 * through here. We just use the answer from the target.
1987 *
1988 * If not, then we leave the target results in "status" and
1989 * run a mail_status on the source that puts its results
1990 * in "cache". Combine the results from cache with the
1991 * results that were already in status.
1992 *
1993 * We count all messages in the source mailbox as recent and
1994 * unseen, even if they are not recent or unseen in the source,
1995 * because they will be recent and unseen in the target
1996 * when we open it. (Not quite true. It is possible that some
1997 * messages from a POP server will end up seen instead
1998 * of unseen.
1999 * It is also possible that it is correct. If we add unseen, or
2000 * if we add messages, we could get it wrong. As far as I
2001 * can tell, Pine doesn't ever even use status.unseen, so it
2002 * is currently academic anyway.) Hubert 2003-03-05
2003 * (Does now 2004-06-02 in next_folder.)
2004 *
2005 * However, we don't want to count all messages as recent if
2006 * the source mailbox is NNTP or NEWS, because we won't be
2007 * deleting those messages from the source.
2008 * We only count recent.
2009 *
2010 * There are other cases that are trouble. One in particular
2011 * is an IMAP-to-NNTP proxy, where the messages can't be removed
2012 * from the mailbox but they can be deleted. If we count
2013 * messages in the source as being recent and it turns out they
2014 * were all deleted already, then we incorrectly say the folder
2015 * has recent messages when it doesn't. We can recover from that
2016 * case at some cost by actually opening the folder the first
2017 * time if there are not recents, and then checking to see if
2018 * everything is deleted. Subsequently, we store the uid values
2019 * (which are returned by status) so that we can know if the
2020 * mailbox changed or not. The problem being solved is that
2021 * the TAB command indicates new messages in a folder when there
2022 * really aren't any. An alternative would be to use the is_news
2023 * half of the if-else in all cases. A problem with that is
2024 * that there could be non-recent messages sitting in the
2025 * source mailbox that we never discover. Hubert 2003-03-28
2026 */
2027
2028 if((d = mail_valid (NIL, source, (char *) NIL))
2029 && (!strcmp(d->name, "nntp") || !strcmp(d->name, "news")))
2030 is_news++;
2031
2032 if(is_news && cache.flags & SA_RECENT){
2033 status.messages += cache.recent;
2034 status.recent += cache.recent;
2035 status.unseen += cache.recent;
2036 status.uidnext += cache.recent;
2037 }
2038 else{
2039 if(uidvalidity && *uidvalidity
2040 && uidnext && *uidnext
2041 && cache.flags & SA_UIDVALIDITY
2042 && cache.uidvalidity == *uidvalidity
2043 && cache.flags & SA_UIDNEXT
2044 && cache.uidnext == *uidnext){
2045 ; /* nothing changed in source mailbox */
2046 }
2047 else if(cache.flags & SA_RECENT && cache.recent){
2048 status.messages += cache.recent;
2049 status.recent += cache.recent;
2050 status.unseen += cache.recent;
2051 status.uidnext += cache.recent;
2052 }
2053 else if(!(cache.flags & SA_MESSAGES) || cache.messages){
2054 long openflags = OP_SILENT | SP_USEPOOL | SP_TEMPUSE;
2055 long delete_count, not_deleted = 0L;
2056
2057 /* Actually open it up and check */
2058 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
2059 openflags |= OP_TRYALT;
2060
2061 if(!ourstream)
2062 stream = NULL;
2063
2064 if(ourstream
2065 && !same_stream_and_mailbox(source, ourstream)){
2066 pine_mail_close(ourstream);
2067 ourstream = stream = NULL;
2068 }
2069
2070 if(!stream){
2071 stream = pine_mail_open(stream, source, openflags,
2072 NULL);
2073 ourstream = stream;
2074 }
2075
2076 if(stream){
2077 delete_count = count_flagged(stream, F_DEL);
2078 not_deleted = stream->nmsgs - delete_count;
2079 }
2080
2081 status.messages += not_deleted;
2082 status.recent += not_deleted;
2083 status.unseen += not_deleted;
2084 status.uidnext += not_deleted;
2085 }
2086
2087 if(uidvalidity && cache.flags & SA_UIDVALIDITY)
2088 *uidvalidity = cache.uidvalidity;
2089
2090 if(uidnext && cache.flags & SA_UIDNEXT)
2091 *uidnext = cache.uidnext;
2092 }
2093 }
2094 }
2095
2096 /*
2097 * Do the regular mm_status callback which we've been intercepting
2098 * into different locations above.
2099 */
2100 pine_cached_status = NIL;
2101 if(ret)
2102 mm_status(NULL, mailbox, &status);
2103 }
2104 else{
2105 if(!stream){
2106 DRIVER *d;
2107
2108 if((d = mail_valid (NIL, mailbox, (char *) NIL))
2109 && !strcmp(d->name, "imap")){
2110 long openflags = OP_HALFOPEN|OP_SILENT|SP_USEPOOL|SP_TEMPUSE;
2111
2112 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
2113 openflags |= OP_TRYALT;
2114
2115 /*
2116 * We just use this to find the answer.
2117 * We're asking for trouble if we do a STATUS on a
2118 * selected mailbox. I don't believe this happens in pine.
2119 * It does now (2004-06-02) in next_folder if the
2120 * F_TAB_USES_UNSEEN option is set and the folder was
2121 * already opened.
2122 */
2123 stream = sp_stream_get(mailbox, SP_MATCH);
2124 if(stream){
2125 memset(&status, 0, sizeof(status));
2126 if(flags & SA_MESSAGES){
2127 status.flags |= SA_MESSAGES;
2128 status.messages = stream->nmsgs;
2129 }
2130
2131 if(flags & SA_RECENT){
2132 status.flags |= SA_RECENT;
2133 status.recent = stream->recent;
2134 }
2135
2136 if(flags & SA_UNSEEN){
2137 long i;
2138 SEARCHPGM *srchpgm;
2139 MESSAGECACHE *mc;
2140
2141 srchpgm = mail_newsearchpgm();
2142 srchpgm->unseen = 1;
2143
2144 pine_mail_search_full(stream, NULL, srchpgm,
2145 SE_NOPREFETCH | SE_FREE);
2146 status.flags |= SA_UNSEEN;
2147 status.unseen = 0L;
2148 for(i = 1L; i <= stream->nmsgs; i++)
2149 if((mc = mail_elt(stream, i)) && mc->searched)
2150 status.unseen++;
2151 }
2152
2153 if(flags & SA_UIDVALIDITY){
2154 status.flags |= SA_UIDVALIDITY;
2155 status.uidvalidity = stream->uid_validity;
2156 }
2157
2158 if(flags & SA_UIDNEXT){
2159 status.flags |= SA_UIDNEXT;
2160 status.uidnext = stream->uid_last + 1L;
2161 }
2162
2163 mm_status(NULL, mailbox, &status);
2164 return T; /* that's what c-client returns when success */
2165 }
2166
2167 if(!stream)
2168 stream = sp_stream_get(mailbox, SP_SAME);
2169
2170 if(!stream){
2171 stream = pine_mail_open(NULL, mailbox, openflags, NULL);
2172 ourstream = stream;
2173 }
2174 }
2175 else if(F_ON(F_ENABLE_MULNEWSRCS, ps_global)
2176 && d && (!strucmp(d->name, "news")
2177 || !strucmp(d->name, "nntp")))
2178 flags |= SA_MULNEWSRC;
2179 }
2180
2181 if(!ps_global->user_says_cancel)
2182 ret = mail_status(stream, mailbox, flags); /* non #move case */
2183 }
2184
2185 if(ourstream)
2186 pine_mail_close(ourstream);
2187
2188 return ret;
2189 }
2190
2191
2192 /*
2193 * Check for a mailbox name that is a legitimate #move mailbox.
2194 *
2195 * Args mbox -- The mailbox name to check
2196 * sourcebuf -- Copy the source mailbox name into this buffer
2197 * sbuflen -- Length of sourcebuf
2198 * targetptr -- Set the pointer this points to to point to the
2199 * target mailbox name in the original string
2200 *
2201 * Returns 1 - is a #move mailbox
2202 * 0 - not
2203 */
2204 int
2205 check_for_move_mbox(char *mbox, char *sourcebuf, size_t sbuflen, char **targetptr)
2206 {
2207 char delim, *s;
2208 int i;
2209
2210 if(mbox && (mbox[0] == '#')
2211 && ((mbox[1] == 'M') || (mbox[1] == 'm'))
2212 && ((mbox[2] == 'O') || (mbox[2] == 'o'))
2213 && ((mbox[3] == 'V') || (mbox[3] == 'v'))
2214 && ((mbox[4] == 'E') || (mbox[4] == 'e'))
2215 && (delim = mbox[5])
2216 && (s = strchr(mbox+6, delim))
2217 && (i = s++ - (mbox + 6))
2218 && (!sourcebuf || i < sbuflen)){
2219
2220 if(sourcebuf){
2221 strncpy(sourcebuf, mbox+6, i); /* copy source mailbox name */
2222 sourcebuf[i] = '\0';
2223 }
2224
2225 if(targetptr)
2226 *targetptr = s;
2227
2228 return 1;
2229 }
2230
2231 return 0;
2232 }
2233
2234
2235 /*
2236 * Checks through stream cache for a stream pointer already open to
2237 * this mailbox, read/write. Very similar to sp_stream_get, but we want
2238 * to look at all streams, not just imap streams.
2239 * Right now it is very specialized. If we want to use it more generally,
2240 * generalize it or combine it with sp_stream_get somehow.
2241 */
2242 MAILSTREAM *
2243 already_open_stream(char *mailbox, int flags)
2244 {
2245 int i;
2246 MAILSTREAM *m;
2247
2248 if(!mailbox)
2249 return(NULL);
2250
2251 if(*mailbox == '{'){
2252 for(i = 0; i < ps_global->s_pool.nstream; i++){
2253 m = ps_global->s_pool.streams[i];
2254 if(m && !(flags & AOS_RW_ONLY && m->rdonly)
2255 && (*m->mailbox == '{') && !sp_dead_stream(m)
2256 && same_stream_and_mailbox(mailbox, m))
2257 return(m);
2258 }
2259 }
2260 else{
2261 char *cn, tmp[MAILTMPLEN];
2262
2263 cn = mailboxfile(tmp, mailbox);
2264 for(i = 0; i < ps_global->s_pool.nstream; i++){
2265 m = ps_global->s_pool.streams[i];
2266 if(m && !(flags & AOS_RW_ONLY && m->rdonly)
2267 && (*m->mailbox != '{') && !sp_dead_stream(m)
2268 && ((cn && *cn && !strcmp(cn, m->mailbox))
2269 || !strcmp(mailbox, m->original_mailbox)
2270 || !strcmp(mailbox, m->mailbox)))
2271 return(m);
2272 }
2273 }
2274
2275 return(NULL);
2276 }
2277
2278
2279 void
2280 pine_imap_cmd_happened(MAILSTREAM *stream, char *cmd, long int flags)
2281 {
2282 dprint((9, "imap_cmd(%s, %s, 0x%lx)\n",
2283 STREAMNAME(stream), cmd ? cmd : "?", flags));
2284
2285 if(cmd && !strucmp(cmd, "CHECK"))
2286 reset_check_point(stream);
2287
2288 ps_global->can_interrupt = 0; /* never interrupt anything */
2289
2290 if(is_imap_stream(stream)){
2291 time_t now;
2292
2293 now = time(0);
2294 sp_set_last_ping(stream, now);
2295 sp_set_last_activity(stream, now);
2296 if(!(flags & SC_EXPUNGEDEFERRED))
2297 sp_set_last_expunged_reaper(stream, now);
2298
2299 if(cmd && !strucmp(cmd, "NOOP")) /* but can interrupt this one */
2300 ps_global->can_interrupt = 1;
2301 }
2302 }
2303
2304
2305 /*
2306 * Tells us whether we ought to check for a dead stream or not.
2307 * We assume that we ought to check if it is not IMAP and if it is IMAP we
2308 * don't need to check if the last activity was within the last 5 minutes.
2309 */
2310 int
2311 recent_activity(MAILSTREAM *stream)
2312 {
2313 if(is_imap_stream(stream) && !sp_dead_stream(stream)
2314 && (time(0) - sp_last_activity(stream) < 5L * 60L))
2315 return 1;
2316 else
2317 return 0;
2318 }
2319
2320 void
2321 sp_cleanup_dead_streams(void)
2322 {
2323 int i;
2324 MAILSTREAM *m;
2325
2326 (void) streams_died(); /* tell user in case they don't know yet */
2327
2328 for(i = 0; i < ps_global->s_pool.nstream; i++){
2329 m = ps_global->s_pool.streams[i];
2330 if(m && sp_dead_stream(m))
2331 pine_mail_close(m);
2332 }
2333 }
2334
2335
2336 /*
2337 * Returns 0 if stream flags not set, non-zero if they are.
2338 */
2339 int
2340 sp_flagged(MAILSTREAM *stream, long unsigned int flags)
2341 {
2342 return(sp_flags(stream) & flags);
2343 }
2344
2345
2346 void
2347 sp_set_fldr(MAILSTREAM *stream, char *folder)
2348 {
2349 PER_STREAM_S **pss;
2350
2351 pss = sp_data(stream);
2352 if(pss && *pss){
2353 if((*pss)->fldr)
2354 fs_give((void **) &(*pss)->fldr);
2355
2356 if(folder)
2357 (*pss)->fldr = cpystr(folder);
2358 }
2359 }
2360
2361
2362 void
2363 sp_set_saved_cur_msg_id(MAILSTREAM *stream, char *id)
2364 {
2365 PER_STREAM_S **pss;
2366
2367 pss = sp_data(stream);
2368 if(pss && *pss){
2369 if((*pss)->saved_cur_msg_id)
2370 fs_give((void **) &(*pss)->saved_cur_msg_id);
2371
2372 if(id)
2373 (*pss)->saved_cur_msg_id = cpystr(id);
2374 }
2375 }
2376
2377
2378 /*
2379 * Sets flags absolutely, erasing old flags.
2380 */
2381 void
2382 sp_flag(MAILSTREAM *stream, long unsigned int flags)
2383 {
2384 if(!stream)
2385 return;
2386
2387 dprint((9, "sp_flag(%s, 0x%x): %s%s%s%s%s%s%s%s\n",
2388 (stream && stream->mailbox) ? stream->mailbox : "?",
2389 flags,
2390 flags ? "set" : "clear",
2391 (flags & SP_LOCKED) ? " SP_LOCKED" : "",
2392 (flags & SP_PERMLOCKED) ? " SP_PERMLOCKED" : "",
2393 (flags & SP_INBOX) ? " SP_INBOX" : "",
2394 (flags & SP_USERFLDR) ? " SP_USERFLDR" : "",
2395 (flags & SP_USEPOOL) ? " SP_USEPOOL" : "",
2396 (flags & SP_TEMPUSE) ? " SP_TEMPUSE" : "",
2397 !flags ? " ALL" : ""));
2398
2399 sp_set_flags(stream, flags);
2400 }
2401
2402
2403 /*
2404 * Clear individual stream flags.
2405 */
2406 void
2407 sp_unflag(MAILSTREAM *stream, long unsigned int flags)
2408 {
2409 if(!stream || !flags)
2410 return;
2411
2412 dprint((9, "sp_unflag(%s, 0x%x): unset%s%s%s%s%s%s\n",
2413 (stream && stream->mailbox) ? stream->mailbox : "?",
2414 flags,
2415 (flags & SP_LOCKED) ? " SP_LOCKED" : "",
2416 (flags & SP_PERMLOCKED) ? " SP_PERMLOCKED" : "",
2417 (flags & SP_INBOX) ? " SP_INBOX" : "",
2418 (flags & SP_USERFLDR) ? " SP_USERFLDR" : "",
2419 (flags & SP_USEPOOL) ? " SP_USEPOOL" : "",
2420 (flags & SP_TEMPUSE) ? " SP_TEMPUSE" : ""));
2421
2422 sp_set_flags(stream, sp_flags(stream) & ~flags);
2423
2424 flags = sp_flags(stream);
2425 dprint((9, "sp_unflag(%s, 0x%x): result:%s%s%s%s%s%s\n",
2426 (stream && stream->mailbox) ? stream->mailbox : "?",
2427 flags,
2428 (flags & SP_LOCKED) ? " SP_LOCKED" : "",
2429 (flags & SP_PERMLOCKED) ? " SP_PERMLOCKED" : "",
2430 (flags & SP_INBOX) ? " SP_INBOX" : "",
2431 (flags & SP_USERFLDR) ? " SP_USERFLDR" : "",
2432 (flags & SP_USEPOOL) ? " SP_USEPOOL" : "",
2433 (flags & SP_TEMPUSE) ? " SP_TEMPUSE" : ""));
2434 }
2435
2436
2437 /*
2438 * Set dead stream indicator and close if not locked.
2439 */
2440 void
2441 sp_mark_stream_dead(MAILSTREAM *stream)
2442 {
2443 if(!stream)
2444 return;
2445
2446 dprint((9, "sp_mark_stream_dead(%s)\n",
2447 (stream && stream->mailbox) ? stream->mailbox : "?"));
2448
2449 /*
2450 * If the stream isn't locked, it is no longer useful. Get rid of it.
2451 */
2452 if(!sp_flagged(stream, SP_LOCKED))
2453 pine_mail_actually_close(stream);
2454 else{
2455 /*
2456 * If it is locked, then we have to worry about references to it
2457 * that still exist. For example, it might be a permlocked stream
2458 * or it might be the current stream. We need to let it be discovered
2459 * by those referencers instead of just eliminating it, so that they
2460 * can clean up the mess they need to clean up.
2461 */
2462 sp_set_dead_stream(stream, 1);
2463 }
2464 }
2465
2466
2467 /*
2468 * Returns the number of streams in the stream pool which are
2469 * SP_USEPOOL but not SP_PERMLOCKED.
2470 */
2471 int
2472 sp_nusepool_notperm(void)
2473 {
2474 int i, cnt = 0;
2475 MAILSTREAM *m;
2476
2477 for(i = 0; i < ps_global->s_pool.nstream; i++){
2478 m = ps_global->s_pool.streams[i];
2479 if(sp_flagged(m, SP_USEPOOL) && !sp_flagged(m, SP_PERMLOCKED))
2480 cnt++;
2481 }
2482
2483 return(cnt);
2484 }
2485
2486
2487 /*
2488 * Returns the number of folders that the user has marked to be PERMLOCKED
2489 * folders (plus INBOX) that are remote IMAP folders.
2490 *
2491 * This routine depends on the fact that VAR_INBOX_PATH, VAR_PERMLOCKED,
2492 * and the ps_global->context_list are correctly set.
2493 */
2494 int
2495 sp_nremote_permlocked(void)
2496 {
2497 int cnt = 0;
2498 char **lock_these, *p = NULL, *dummy = NULL, *lt;
2499 DRIVER *d;
2500
2501 /* first check if INBOX is remote */
2502 lt = ps_global->VAR_INBOX_PATH;
2503 if(lt && (d=mail_valid(NIL, lt, (char *) NIL))
2504 && !strcmp(d->name, "imap"))
2505 cnt++;
2506
2507 /* then count the user-specified permlocked folders */
2508 for(lock_these = ps_global->VAR_PERMLOCKED; lock_these && *lock_these;
2509 lock_these++){
2510
2511 /*
2512 * Skip inbox, already done above. Should do this better so that we
2513 * catch the case where the user puts the technical spec of the inbox
2514 * in the list, or where the user lists one folder twice.
2515 */
2516 if(*lock_these && !strucmp(*lock_these, ps_global->inbox_name))
2517 continue;
2518
2519 /* there isn't really a pair, it just dequotes for us */
2520 get_pair(*lock_these, &dummy, &p, 0, 0);
2521
2522 /*
2523 * Check to see if this is an incoming nickname and replace it
2524 * with the full name.
2525 */
2526 if(!(p && ps_global->context_list
2527 && ps_global->context_list->use & CNTXT_INCMNG
2528 && (lt=folder_is_nick(p, FOLDERS(ps_global->context_list),
2529 FN_WHOLE_NAME))))
2530 lt = p;
2531
2532 if(dummy)
2533 fs_give((void **) &dummy);
2534
2535 if(lt && (d=mail_valid(NIL, lt, (char *) NIL))
2536 && !strcmp(d->name, "imap"))
2537 cnt++;
2538
2539 if(p)
2540 fs_give((void **) &p);
2541 }
2542
2543 return(cnt);
2544 }
2545
2546
2547 /*
2548 * Look for an already open stream that can be used for a new purpose.
2549 * (Note that we only look through streams flagged SP_USEPOOL.)
2550 *
2551 * Args: mailbox
2552 * flags
2553 *
2554 * Flags is a set of values or'd together which tells us what the request
2555 * is looking for.
2556 *
2557 * Returns: a live stream from the stream pool or NULL.
2558 */
2559 MAILSTREAM *
2560 sp_stream_get(char *mailbox, long unsigned int flags)
2561 {
2562 int i;
2563 MAILSTREAM *m;
2564
2565 dprint((7, "sp_stream_get(%s):%s%s%s%s%s\n",
2566 mailbox ? mailbox : "?",
2567 (flags & SP_MATCH) ? " SP_MATCH" : "",
2568 (flags & SP_RO_OK) ? " SP_RO_OK" : "",
2569 (flags & SP_SAME) ? " SP_SAME" : "",
2570 (flags & SP_UNLOCKED) ? " SP_UNLOCKED" : "",
2571 (flags & SP_TEMPUSE) ? " SP_TEMPUSE" : ""));
2572
2573 /* look for stream already open to this mailbox */
2574 if(flags & SP_MATCH){
2575 for(i = 0; i < ps_global->s_pool.nstream; i++){
2576 m = ps_global->s_pool.streams[i];
2577 if(m && sp_flagged(m, SP_USEPOOL)
2578 && (!m->rdonly || (flags & SP_RO_OK)) && !sp_dead_stream(m)
2579 && same_stream_and_mailbox(mailbox, m)){
2580 if((sp_flagged(m, SP_LOCKED) && recent_activity(m))
2581 || pine_mail_ping(m)){
2582 dprint((7,
2583 "sp_stream_get: found exact match, slot %d\n", i));
2584 if(!sp_flagged(m, SP_LOCKED)){
2585 dprint((7,
2586 "reset idle timer1: next TAG %08lx (%s)\n",
2587 m->gensym,
2588 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
2589 sp_set_last_use_time(m, time(0));
2590 }
2591
2592 return(m);
2593 }
2594
2595 sp_mark_stream_dead(m);
2596 }
2597 }
2598 }
2599
2600 /*
2601 * SP_SAME will not match if an SP_MATCH match would have worked.
2602 * If the caller is interested in SP_MATCH streams as well as SP_SAME
2603 * streams then the caller should make two separate calls to this
2604 * routine.
2605 */
2606 if(flags & SP_SAME){
2607 /*
2608 * If the flags arg does not have either SP_TEMPUSE or SP_UNLOCKED
2609 * set, then we'll accept any stream, even if locked.
2610 * We want to prefer the LOCKED case so that we don't have to ping.
2611 */
2612 if(!(flags & SP_UNLOCKED) && !(flags & SP_TEMPUSE)){
2613 for(i = 0; i < ps_global->s_pool.nstream; i++){
2614 m = ps_global->s_pool.streams[i];
2615 if(m && sp_flagged(m, SP_USEPOOL)
2616 && sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
2617 && same_stream(mailbox, m)
2618 && !same_stream_and_mailbox(mailbox, m)){
2619 if(recent_activity(m) || pine_mail_ping(m)){
2620 dprint((7,
2621 "sp_stream_get: found SAME match, slot %d\n", i));
2622 return(m);
2623 }
2624
2625 sp_mark_stream_dead(m);
2626 }
2627 }
2628
2629 /* consider the unlocked streams */
2630 for(i = 0; i < ps_global->s_pool.nstream; i++){
2631 m = ps_global->s_pool.streams[i];
2632 if(m && sp_flagged(m, SP_USEPOOL)
2633 && !sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
2634 && same_stream(mailbox, m)
2635 && !same_stream_and_mailbox(mailbox, m)){
2636 /* always ping unlocked streams */
2637 if(pine_mail_ping(m)){
2638 dprint((7,
2639 "sp_stream_get: found SAME match, slot %d\n", i));
2640 dprint((7,
2641 "reset idle timer4: next TAG %08lx (%s)\n",
2642 m->gensym,
2643 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
2644 sp_set_last_use_time(m, time(0));
2645
2646 return(m);
2647 }
2648
2649 sp_mark_stream_dead(m);
2650 }
2651 }
2652 }
2653
2654 /*
2655 * Prefer streams marked SP_TEMPUSE and not LOCKED.
2656 * If SP_TEMPUSE is set in the flags arg then this is the
2657 * only loop we try.
2658 */
2659 for(i = 0; i < ps_global->s_pool.nstream; i++){
2660 m = ps_global->s_pool.streams[i];
2661 if(m && sp_flagged(m, SP_USEPOOL) && sp_flagged(m, SP_TEMPUSE)
2662 && !sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
2663 && same_stream(mailbox, m)
2664 && !same_stream_and_mailbox(mailbox, m)){
2665 if(pine_mail_ping(m)){
2666 dprint((7,
2667 "sp_stream_get: found SAME/TEMPUSE match, slot %d\n", i));
2668 dprint((7,
2669 "reset idle timer2: next TAG %08lx (%s)\n",
2670 m->gensym,
2671 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
2672 sp_set_last_use_time(m, time(0));
2673 return(m);
2674 }
2675
2676 sp_mark_stream_dead(m);
2677 }
2678 }
2679
2680 /*
2681 * If SP_TEMPUSE is not set in the flags arg but SP_UNLOCKED is,
2682 * then we will consider
2683 * streams which are not marked SP_TEMPUSE (but are still not
2684 * locked). We go through these in reverse order so that we'll get
2685 * the last one added instead of the first one. It's not clear if
2686 * that is a good idea or if a more complex search would somehow
2687 * be better. Maybe we should use a round-robin sort of search
2688 * here so that we don't leave behind unused streams. Or maybe
2689 * we should keep track of when we used it and look for the LRU stream.
2690 */
2691 if(!(flags & SP_TEMPUSE)){
2692 for(i = ps_global->s_pool.nstream - 1; i >= 0; i--){
2693 m = ps_global->s_pool.streams[i];
2694 if(m && sp_flagged(m, SP_USEPOOL)
2695 && !sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
2696 && same_stream(mailbox, m)
2697 && !same_stream_and_mailbox(mailbox, m)){
2698 if(pine_mail_ping(m)){
2699 dprint((7,
2700 "sp_stream_get: found SAME/UNLOCKED match, slot %d\n", i));
2701 dprint((7,
2702 "reset idle timer3: next TAG %08lx (%s)\n",
2703 m->gensym,
2704 debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress)));
2705 sp_set_last_use_time(m, time(0));
2706 return(m);
2707 }
2708
2709 sp_mark_stream_dead(m);
2710 }
2711 }
2712 }
2713 }
2714
2715 /*
2716 * If we can't find a useful stream to use in pine_mail_open, we may
2717 * want to re-use one that is not actively being used even though it
2718 * is not on the same server. We'll have to close it and then re-use
2719 * the slot.
2720 */
2721 if(!(flags & (SP_SAME | SP_MATCH))){
2722 /*
2723 * Prefer streams marked SP_TEMPUSE and not LOCKED.
2724 * If SP_TEMPUSE is set in the flags arg then this is the
2725 * only loop we try.
2726 */
2727 for(i = 0; i < ps_global->s_pool.nstream; i++){
2728 m = ps_global->s_pool.streams[i];
2729 if(m && sp_flagged(m, SP_USEPOOL) && sp_flagged(m, SP_TEMPUSE)
2730 && !sp_flagged(m, SP_LOCKED)){
2731 dprint((7,
2732 "sp_stream_get: found Not-SAME/TEMPUSE match, slot %d\n", i));
2733 /*
2734 * We ping it in case there is new mail that we should
2735 * pass through our filters. Pine_mail_actually_close will
2736 * do that.
2737 */
2738 (void) pine_mail_ping(m);
2739 return(m);
2740 }
2741 }
2742
2743 /*
2744 * If SP_TEMPUSE is not set in the flags arg, then we will consider
2745 * streams which are not marked SP_TEMPUSE (but are still not
2746 * locked). Maybe we should use a round-robin sort of search
2747 * here so that we don't leave behind unused streams. Or maybe
2748 * we should keep track of when we used it and look for the LRU stream.
2749 */
2750 if(!(flags & SP_TEMPUSE)){
2751 for(i = ps_global->s_pool.nstream - 1; i >= 0; i--){
2752 m = ps_global->s_pool.streams[i];
2753 if(m && sp_flagged(m, SP_USEPOOL) && !sp_flagged(m, SP_LOCKED)){
2754 dprint((7,
2755 "sp_stream_get: found Not-SAME/UNLOCKED match, slot %d\n", i));
2756 /*
2757 * We ping it in case there is new mail that we should
2758 * pass through our filters. Pine_mail_actually_close will
2759 * do that.
2760 */
2761 (void) pine_mail_ping(m);
2762 return(m);
2763 }
2764 }
2765 }
2766 }
2767
2768 dprint((7, "sp_stream_get: no match found\n"));
2769
2770 return(NULL);
2771 }
2772
2773
2774 void
2775 sp_end(void)
2776 {
2777 int i;
2778 MAILSTREAM *m;
2779
2780 dprint((7, "sp_end\n"));
2781
2782 for(i = 0; i < ps_global->s_pool.nstream; i++){
2783 m = ps_global->s_pool.streams[i];
2784 if(m)
2785 pine_mail_actually_close(m);
2786 }
2787
2788 if(ps_global->s_pool.streams)
2789 fs_give((void **) &ps_global->s_pool.streams);
2790
2791 ps_global->s_pool.nstream = 0;
2792 }
2793
2794
2795 /*
2796 * Find a vacant slot to put this new stream in.
2797 * We are willing to close and kick out another stream as long as it isn't
2798 * LOCKED. However, we may find that there is no place to put this one
2799 * because all the slots are used and locked. For now, we'll return -1
2800 * in that case and leave the new stream out of the pool.
2801 */
2802 int
2803 sp_add(MAILSTREAM *stream, int usepool)
2804 {
2805 int i, slot = -1;
2806 MAILSTREAM *m;
2807
2808 dprint((7, "sp_add(%s, %d)\n",
2809 (stream && stream->mailbox) ? stream->mailbox : "?", usepool));
2810
2811 if(!stream){
2812 dprint((7, "sp_add: NULL stream\n"));
2813 return -1;
2814 }
2815
2816 /* If this stream is already there, don't add it again */
2817 for(i = 0; i < ps_global->s_pool.nstream; i++){
2818 m = ps_global->s_pool.streams[i];
2819 if(m == stream){
2820 slot = i;
2821 dprint((7,
2822 "sp_add: stream was already in slot %d\n", slot));
2823 return 0;
2824 }
2825 }
2826
2827 if(usepool && !sp_flagged(stream, SP_PERMLOCKED)
2828 && sp_nusepool_notperm() >= ps_global->s_pool.max_remstream){
2829 dprint((7,
2830 "sp_add: reached max implicit SP_USEPOOL of %d\n",
2831 ps_global->s_pool.max_remstream));
2832 return -1;
2833 }
2834
2835 /* Look for an unused slot */
2836 for(i = 0; i < ps_global->s_pool.nstream; i++){
2837 m = ps_global->s_pool.streams[i];
2838 if(!m){
2839 slot = i;
2840 dprint((7,
2841 "sp_add: using empty slot %d\n", slot));
2842 break;
2843 }
2844 }
2845
2846 /* else, allocate more space */
2847 if(slot < 0){
2848 ps_global->s_pool.nstream++;
2849 slot = ps_global->s_pool.nstream - 1;
2850 if(ps_global->s_pool.streams){
2851 fs_resize((void **) &ps_global->s_pool.streams,
2852 ps_global->s_pool.nstream *
2853 sizeof(*ps_global->s_pool.streams));
2854 ps_global->s_pool.streams[slot] = NULL;
2855 }
2856 else{
2857 ps_global->s_pool.streams =
2858 (MAILSTREAM **) fs_get(ps_global->s_pool.nstream *
2859 sizeof(*ps_global->s_pool.streams));
2860 memset(ps_global->s_pool.streams, 0,
2861 ps_global->s_pool.nstream *
2862 sizeof(*ps_global->s_pool.streams));
2863 }
2864
2865 dprint((7,
2866 "sp_add: allocate more space, using new slot %d\n", slot));
2867 }
2868
2869 if(slot >= 0 && slot < ps_global->s_pool.nstream){
2870 ps_global->s_pool.streams[slot] = stream;
2871 return 0;
2872 }
2873 else{
2874 dprint((7, "sp_add: failed to find a slot!\n"));
2875 return -1;
2876 }
2877 }
2878
2879
2880 /*
2881 * Simply remove this stream from the stream pool.
2882 */
2883 void
2884 sp_delete(MAILSTREAM *stream)
2885 {
2886 int i;
2887 MAILSTREAM *m;
2888
2889 if(!stream)
2890 return;
2891
2892 dprint((7, "sp_delete(%s)\n",
2893 (stream && stream->mailbox) ? stream->mailbox : "?"));
2894
2895 /*
2896 * There are some global stream pointers that we have to worry
2897 * about before deleting the stream.
2898 */
2899
2900 /* first, mail_stream is the global currently open folder */
2901 if(ps_global->mail_stream == stream)
2902 ps_global->mail_stream = NULL;
2903
2904 /* remote address books may have open stream pointers */
2905 note_closed_adrbk_stream(stream);
2906
2907 if(pith_opt_closing_stream)
2908 (*pith_opt_closing_stream)(stream);
2909
2910 for(i = 0; i < ps_global->s_pool.nstream; i++){
2911 m = ps_global->s_pool.streams[i];
2912 if(m == stream){
2913 ps_global->s_pool.streams[i] = NULL;
2914 dprint((7,
2915 "sp_delete: stream removed from slot %d\n", i));
2916 return;
2917 }
2918 }
2919 }
2920
2921
2922 /*
2923 * Returns 1 if any locked userfldr is dead, 0 if all alive.
2924 */
2925 int
2926 sp_a_locked_stream_is_dead(void)
2927 {
2928 int i, ret = 0;
2929 MAILSTREAM *m;
2930
2931 for(i = 0; !ret && i < ps_global->s_pool.nstream; i++){
2932 m = ps_global->s_pool.streams[i];
2933 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
2934 && sp_dead_stream(m))
2935 ret++;
2936 }
2937
2938 return(ret);
2939 }
2940
2941
2942 /*
2943 * Returns 1 if any locked stream is changed, 0 otherwise
2944 */
2945 int
2946 sp_a_locked_stream_changed(void)
2947 {
2948 int i, ret = 0;
2949 MAILSTREAM *m;
2950
2951 for(i = 0; !ret && i < ps_global->s_pool.nstream; i++){
2952 m = ps_global->s_pool.streams[i];
2953 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
2954 && sp_mail_box_changed(m))
2955 ret++;
2956 }
2957
2958 return(ret);
2959 }
2960
2961
2962 /*
2963 * Returns the inbox stream or NULL.
2964 */
2965 MAILSTREAM *
2966 sp_inbox_stream(void)
2967 {
2968 int i;
2969 MAILSTREAM *m, *ret = NULL;
2970
2971 for(i = 0; !ret && i < ps_global->s_pool.nstream; i++){
2972 m = ps_global->s_pool.streams[i];
2973 if(m && sp_flagged(m, SP_INBOX))
2974 ret = m;
2975 }
2976
2977 return(ret);
2978 }
2979
2980
2981 /*
2982 * Make sure that the sp_data per-stream data storage area exists.
2983 *
2984 * Returns a handle to the sp_data data unless stream is NULL,
2985 * in which case NULL is returned
2986 */
2987 PER_STREAM_S **
2988 sp_data(MAILSTREAM *stream)
2989 {
2990 PER_STREAM_S **pss = NULL;
2991
2992 if(stream){
2993 if(*(pss = (PER_STREAM_S **) &stream->sparep) == NULL){
2994 *pss = (PER_STREAM_S *) fs_get(sizeof(PER_STREAM_S));
2995 memset(*pss, 0, sizeof(PER_STREAM_S));
2996 reset_check_point(stream);
2997 }
2998 }
2999
3000 return(pss);
3001 }
3002
3003
3004 /*
3005 * Returns a pointer to the msgmap associated with the argument stream.
3006 *
3007 * If the PER_STREAM_S data or the msgmap does not already exist, it will be
3008 * created.
3009 */
3010 MSGNO_S *
3011 sp_msgmap(MAILSTREAM *stream)
3012 {
3013 MSGNO_S **msgmap = NULL;
3014 PER_STREAM_S **pss = NULL;
3015
3016 pss = sp_data(stream);
3017
3018 if(pss && *pss
3019 && (*(msgmap = (MSGNO_S **) &(*pss)->msgmap) == NULL))
3020 mn_init(msgmap, stream->nmsgs);
3021
3022 return(msgmap ? *msgmap : NULL);
3023 }
3024
3025
3026 void
3027 sp_free_callback(void **sparep)
3028 {
3029 PER_STREAM_S **pss;
3030 MAILSTREAM *stream = NULL, *m;
3031 int i;
3032
3033 pss = (PER_STREAM_S **) sparep;
3034
3035 if(pss && *pss){
3036 /*
3037 * It is possible that this has been called from c-client when
3038 * we weren't expecting it. We need to clean up the stream pool
3039 * entries if the stream that goes with this pointer is in the
3040 * stream pool somewhere.
3041 */
3042 for(i = 0; !stream && i < ps_global->s_pool.nstream; i++){
3043 m = ps_global->s_pool.streams[i];
3044 if(sparep && *sparep && m && m->sparep == *sparep)
3045 stream = m;
3046 }
3047
3048 if(stream){
3049 if(ps_global->mail_stream == stream)
3050 ps_global->mail_stream = NULL;
3051
3052 sp_delete(stream);
3053 }
3054
3055 sp_free(pss);
3056 }
3057 }
3058
3059
3060 /*
3061 * Free the data but don't mess with the stream pool.
3062 */
3063 void
3064 sp_free(PER_STREAM_S **pss)
3065 {
3066 if(pss && *pss){
3067 if((*pss)->msgmap){
3068 if(ps_global->msgmap == (*pss)->msgmap)
3069 ps_global->msgmap = NULL;
3070
3071 mn_give(&(*pss)->msgmap);
3072 }
3073
3074 if((*pss)->fldr)
3075 fs_give((void **) &(*pss)->fldr);
3076
3077 fs_give((void **) pss);
3078 }
3079 }
3080
3081
3082
3083 /*----------------------------------------------------------------------
3084 See if stream can be used for a mailbox name
3085
3086 Accepts: mailbox name
3087 candidate stream
3088 Returns: stream if it can be used, else NIL
3089
3090 This is called to weed out unnecessary use of c-client streams. In other
3091 words, to help facilitate re-use of streams.
3092
3093 This code is very similar to the same_remote_mailboxes code below, which
3094 is used in pine_mail_open. That code compares two mailbox names. One is
3095 usually from the config file and the other is either from the config file
3096 or is typed in. Here and in same_stream_and_mailbox below, we're comparing
3097 an open stream to a name instead of two names. We could conceivably use
3098 same_remote_mailboxes to compare stream->mailbox to name, but it isn't
3099 exactly the same and the differences may be important. Some stuff that
3100 happens here seems wrong, but it isn't easy to fix.
3101 Having !mb_n.port count as a match to any mb_s.port isn't right. It should
3102 only match if mb_s.port is equal to the default, but the default isn't
3103 something that is available to us. The same thing is done in c-client in
3104 the mail_usable_network_stream() routine, and it isn't right there, either.
3105 The semantics of a missing user are also suspect, because just like with
3106 port, a default is used.
3107 ----*/
3108 MAILSTREAM *
3109 same_stream(char *name, MAILSTREAM *stream)
3110 {
3111 NETMBX mb_s, mb_n, mb_o;
3112
3113 if(stream && stream->mailbox && *stream->mailbox && name && *name
3114 && !(sp_dead_stream(stream))
3115 && mail_valid_net_parse(stream->mailbox, &mb_s)
3116 && mail_valid_net_parse(stream->original_mailbox, &mb_o)
3117 && mail_valid_net_parse(name, &mb_n)
3118 && !strucmp(mb_n.service, mb_s.service)
3119 && (!strucmp(mb_n.host, mb_o.host) /* s is already canonical */
3120 || !strucmp(canonical_name(mb_n.host), mb_s.host))
3121 && (!mb_n.port || mb_n.port == mb_s.port)
3122 && mb_n.anoflag == stream->anonymous
3123 && ((mb_n.user && *mb_n.user &&
3124 mb_s.user && !strcmp(mb_n.user, mb_s.user))
3125 ||
3126 ((!mb_n.user || !*mb_n.user)
3127 && mb_s.user
3128 && ((ps_global->VAR_USER_ID
3129 && !strcmp(ps_global->VAR_USER_ID, mb_s.user))
3130 ||
3131 (!ps_global->VAR_USER_ID
3132 && ps_global->ui.login[0]
3133 && !strcmp(ps_global->ui.login, mb_s.user))))
3134 ||
3135 (!((mb_n.user && *mb_n.user) || (mb_s.user && *mb_s.user))
3136 && stream->anonymous))
3137 && (struncmp(mb_n.service, "imap", 4) ? 1 : strcmp(imap_host(stream), ".NO-IMAP-CONNECTION."))){
3138 dprint((7, "same_stream: name->%s == stream->%s: yes\n",
3139 name ? name : "?",
3140 (stream && stream->mailbox) ? stream->mailbox : "NULL"));
3141 return(stream);
3142 }
3143
3144 dprint((7, "same_stream: name->%s == stream->%s: no dice\n",
3145 name ? name : "?",
3146 (stream && stream->mailbox) ? stream->mailbox : "NULL"));
3147 return(NULL);
3148 }
3149
3150
3151
3152 /*----------------------------------------------------------------------
3153 See if this stream has the named mailbox selected.
3154
3155 Accepts: mailbox name
3156 candidate stream
3157 Returns: stream if it can be used, else NIL
3158 ----*/
3159 MAILSTREAM *
3160 same_stream_and_mailbox(char *name, MAILSTREAM *stream)
3161 {
3162 NETMBX mb_s, mb_n;
3163
3164 if(same_stream(name, stream)
3165 && mail_valid_net_parse(stream->mailbox, &mb_s)
3166 && mail_valid_net_parse(name, &mb_n)
3167 && (mb_n.mailbox && mb_s.mailbox
3168 && (!strcmp(mb_n.mailbox,mb_s.mailbox) /* case depend except INBOX */
3169 || (!strucmp(mb_n.mailbox,"INBOX")
3170 && !strucmp(mb_s.mailbox,"INBOX"))))){
3171 dprint((7,
3172 "same_stream_and_mailbox: name->%s == stream->%s: yes\n",
3173 name ? name : "?",
3174 (stream && stream->mailbox) ? stream->mailbox : "NULL"));
3175 return(stream);
3176 }
3177
3178 dprint((7,
3179 "same_stream_and_mailbox: name->%s == stream->%s: no dice\n",
3180 name ? name : "?",
3181 (stream && stream->mailbox) ? stream->mailbox : "NULL"));
3182 return(NULL);
3183 }
3184
3185 /*
3186 * Args -- name1 and name2 are remote mailbox names.
3187 *
3188 * Returns -- True if names refer to same mailbox accessed in same way
3189 * False if not
3190 *
3191 * This has some very similar code to same_stream_and_mailbox but we're not
3192 * quite ready to discard the differences.
3193 * The treatment of the port and the user is not quite the same.
3194 */
3195 int
3196 same_remote_mailboxes(char *name1, char *name2)
3197 {
3198 NETMBX mb1, mb2;
3199 char *cn1;
3200
3201 /*
3202 * Probably we should allow !port equal to default port, but we don't
3203 * know how to get the default port. To match what c-client does we
3204 * allow !port to be equal to anything.
3205 */
3206 return(name1 && IS_REMOTE(name1)
3207 && name2 && IS_REMOTE(name2)
3208 && mail_valid_net_parse(name1, &mb1)
3209 && mail_valid_net_parse(name2, &mb2)
3210 && !strucmp(mb1.service, mb2.service)
3211 && (!strucmp(mb1.host, mb2.host) /* just to save DNS lookups */
3212 || !strucmp(cn1=canonical_name(mb1.host), mb2.host)
3213 || !strucmp(cn1, canonical_name(mb2.host)))
3214 && (!mb1.port || !mb2.port || mb1.port == mb2.port)
3215 && mb1.anoflag == mb2.anoflag
3216 && mb1.mailbox && mb2.mailbox
3217 && (!strcmp(mb1.mailbox, mb2.mailbox)
3218 || (!strucmp(mb1.mailbox,"INBOX")
3219 && !strucmp(mb2.mailbox,"INBOX")))
3220 && ((mb1.user && *mb1.user && mb2.user && *mb2.user
3221 && !strcmp(mb1.user, mb2.user))
3222 ||
3223 (!(mb1.user && *mb1.user) && !(mb2.user && *mb2.user))
3224 ||
3225 (!(mb1.user && *mb1.user)
3226 && ((ps_global->VAR_USER_ID
3227 && !strcmp(ps_global->VAR_USER_ID, mb2.user))
3228 ||
3229 (!ps_global->VAR_USER_ID
3230 && ps_global->ui.login[0]
3231 && !strcmp(ps_global->ui.login, mb2.user))))
3232 ||
3233 (!(mb2.user && *mb2.user)
3234 && ((ps_global->VAR_USER_ID
3235 && !strcmp(ps_global->VAR_USER_ID, mb1.user))
3236 ||
3237 (!ps_global->VAR_USER_ID
3238 && ps_global->ui.login[0]
3239 && !strcmp(ps_global->ui.login, mb1.user))))));
3240 }
3241
3242
3243 int
3244 is_imap_stream(MAILSTREAM *stream)
3245 {
3246 return(stream && stream->dtb && stream->dtb->name
3247 && !strcmp(stream->dtb->name, "imap"));
3248 }
3249
3250
3251 int
3252 modern_imap_stream(MAILSTREAM *stream)
3253 {
3254 return(is_imap_stream(stream) && LEVELIMAP4rev1(stream));
3255 }
3256
3257
3258 /*----------------------------------------------------------------------
3259 Check and see if all the stream are alive
3260
3261 Returns: 0 if there was no change
3262 >0 if streams have died since last call
3263
3264 Also outputs a message that the streams have died
3265 ----*/
3266 int
3267 streams_died(void)
3268 {
3269 int rv = 0;
3270 int i;
3271 MAILSTREAM *m;
3272 unsigned char *folder;
3273
3274 for(i = 0; i < ps_global->s_pool.nstream; i++){
3275 m = ps_global->s_pool.streams[i];
3276 if(m && sp_dead_stream(m)){
3277 if(sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)){
3278 if(!sp_noticed_dead_stream(m)){
3279 rv++;
3280 sp_set_noticed_dead_stream(m, 1);
3281 folder = folder_name_decoded((unsigned char *)STREAMNAME(m));
3282 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3283 _("MAIL FOLDER \"%s\" CLOSED DUE TO ACCESS ERROR"),
3284 short_str(pretty_fn((char *) folder) ? pretty_fn((char *) folder) : "?",
3285 tmp_20k_buf+1000, SIZEOF_20KBUF-1000, 35, FrontDots));
3286 dprint((6, "streams_died: locked: \"%s\"\n",
3287 folder));
3288 if(rv == 1){
3289 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Folder \"%s\" is Closed"),
3290 short_str(pretty_fn((char *)folder) ? pretty_fn((char *)folder) : "?",
3291 tmp_20k_buf+1000, SIZEOF_20KBUF-1000, 35, FrontDots));
3292 if(pith_opt_icon_text)
3293 (*pith_opt_icon_text)(tmp_20k_buf, IT_MCLOSED);
3294 }
3295 if(folder) fs_give((void **)&folder);
3296 }
3297 }
3298 else{
3299 if(!sp_noticed_dead_stream(m)){
3300 sp_set_noticed_dead_stream(m, 1);
3301 folder = (unsigned char *) STREAMNAME(m);
3302 /*
3303 * If a cached stream died and then we tried to use it
3304 * it could cause problems. We could warn about it here
3305 * but it may be confusing because it might be
3306 * unrelated to what the user is doing and not cause
3307 * any problem at all.
3308 */
3309 #if 0
3310 if(sp_flagged(m, SP_USEPOOL))
3311 q_status_message(SM_ORDER, 3, 3,
3312 "Warning: Possible problem accessing remote data, connection died.");
3313 #endif
3314
3315 dprint((6, "streams_died: not locked: \"%s\"\n",
3316 folder));
3317 }
3318
3319 pine_mail_actually_close(m);
3320 }
3321 }
3322 }
3323
3324 return(rv);
3325 }
3326
3327
3328 /* Some stream is locked checks to see if there is any stream for which we
3329 * are in a callback from c-client
3330 */
3331
3332 int
3333 some_stream_is_locked(void)
3334 {
3335 int rv = 0, i;
3336 MAILSTREAM *m;
3337
3338 for(i = 0; rv == 0 && i < ps_global->s_pool.nstream; i++){
3339 m = ps_global->s_pool.streams[i];
3340 if(m && m->lock)
3341 rv++;
3342 }
3343
3344 return(rv);
3345 }
3346
3347 /*
3348 * Very simple version of appenduid_cb until we need something
3349 * more complex.
3350 */
3351
3352 static imapuid_t last_append_uid;
3353
3354 void
3355 appenduid_cb(char *mailbox,unsigned long uidvalidity, SEARCHSET *set)
3356 {
3357 last_append_uid = set ? set->first : 0L;
3358 }
3359
3360
3361 imapuid_t
3362 get_last_append_uid(void)
3363 {
3364 return last_append_uid;
3365 }
3366
3367
3368 /*
3369 * mail_cmd_stream - return a stream suitable for mail_lsub,
3370 * mail_subscribe, and mail_unsubscribe
3371 *
3372 */
3373 MAILSTREAM *
3374 mail_cmd_stream(CONTEXT_S *context, int *closeit)
3375 {
3376 char tmp[MAILTMPLEN];
3377
3378 *closeit = 1;
3379 (void) context_apply(tmp, context, "x", sizeof(tmp));
3380
3381 return(pine_mail_open(NULL, tmp,
3382 OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE,
3383 NULL));
3384 }
3385
3386
3387 /*
3388 * This is so we can replace the old rfc822_ routines like rfc822_header_line
3389 * with the new version that checks bounds, like rfc822_output_header_line.
3390 * This routine is called when would be a bounds overflow, which we simply log
3391 * and go on with the truncated data.
3392 */
3393 long
3394 dummy_soutr(void *stream, char *string)
3395 {
3396 dprint((2, "dummy_soutr unexpected call, caught overflow\n"));
3397 return LONGT;
3398 }
3399