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

« back to all changes in this revision

Viewing changes to pith/remote.c

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#if !defined(lint) && !defined(DOS)
 
2
static char rcsid[] = "$Id: remote.c 394 2007-01-25 20:29:45Z hubert@u.washington.edu $";
 
3
#endif
 
4
 
 
5
/*
 
6
 * ========================================================================
 
7
 * Copyright 2006-2007 University of Washington
 
8
 *
 
9
 * Licensed under the Apache License, Version 2.0 (the "License");
 
10
 * you may not use this file except in compliance with the License.
 
11
 * You may obtain a copy of the License at
 
12
 *
 
13
 *     http://www.apache.org/licenses/LICENSE-2.0
 
14
 *
 
15
 * ========================================================================
 
16
 */
 
17
 
 
18
/*======================================================================
 
19
     remote.c
 
20
     Implements remote IMAP config files (remote config, remote abook).
 
21
  ====*/
 
22
 
 
23
 
 
24
#include "../pith/headers.h"
 
25
#include "../pith/remote.h"
 
26
#include "../pith/conf.h"
 
27
#include "../pith/imap.h"
 
28
#include "../pith/msgno.h"
 
29
#include "../pith/mailview.h"
 
30
#include "../pith/status.h"
 
31
#include "../pith/flag.h"
 
32
#include "../pith/tempfile.h"
 
33
#include "../pith/adrbklib.h"
 
34
#include "../pith/detach.h"
 
35
#include "../pith/filter.h"
 
36
#include "../pith/stream.h"
 
37
#include "../pith/options.h"
 
38
#include "../pith/busy.h"
 
39
#include "../pith/readfile.h"
 
40
 
 
41
 
 
42
/*
 
43
 * Internal prototypes
 
44
 */
 
45
REMDATA_META_S *rd_find_our_metadata(char *, unsigned long *);
 
46
int             rd_meta_is_broken(FILE *);
 
47
int             rd_add_hdr_msg(REMDATA_S *, char *);
 
48
int             rd_store_fake_hdrs(REMDATA_S *, char *, char *, char *);
 
49
int             rd_upgrade_cookies(REMDATA_S *, long, int);
 
50
int             rd_check_for_suspect_data(REMDATA_S *);
 
51
 
 
52
 
 
53
char meta_prefix[] = ".ab";
 
54
 
 
55
 
 
56
char *(*pith_opt_rd_metadata_name)(void);
 
57
 
 
58
 
 
59
char *
 
60
read_remote_pinerc(PINERC_S *prc, ParsePinerc which_vars)
 
61
{
 
62
    int        try_cache, no_perm_create_pass = 0;
 
63
    char      *file = NULL;
 
64
    unsigned   flags;
 
65
 
 
66
 
 
67
    dprint((7, "read_remote_pinerc \"%s\"\n",
 
68
           prc->name ? prc->name : "?"));
 
69
 
 
70
    /*
 
71
     * We don't cache the pinerc, we always copy it.
 
72
     *
 
73
     * Don't store the config in a temporary file, just leave it
 
74
     * in memory while using it.
 
75
     * It is currently required that NO_PERM_CACHE be set if NO_FILE is set.
 
76
     */
 
77
    flags = (NO_PERM_CACHE | NO_FILE);
 
78
 
 
79
create_the_remote_folder:
 
80
 
 
81
    if(no_perm_create_pass){
 
82
        if(prc->rd){
 
83
            prc->rd->flags &= ~DO_REMTRIM;
 
84
            rd_close_remdata(&prc->rd);
 
85
        }
 
86
 
 
87
        /* this will cause the remote folder to be created */
 
88
        flags = 0;
 
89
    }
 
90
 
 
91
    /*
 
92
     * We could parse the name here to find what type it is. So far we
 
93
     * only have type RemImap.
 
94
     */
 
95
    prc->rd = rd_create_remote(RemImap, prc->name,
 
96
                               (void *)REMOTE_PINERC_SUBTYPE,
 
97
                               &flags, _("Error: "),
 
98
                               _(" Can't fetch remote configuration."));
 
99
    if(!prc->rd)
 
100
      goto bail_out;
 
101
    
 
102
    /*
 
103
     * On first use we just use a temp file instead of memory (NO_FILE).
 
104
     * In other words, for our convenience, we don't turn NO_FILE back on
 
105
     * here. Why is that convenient? Because of the stuff that happened in
 
106
     * rd_create_remote when flags was set to zero.
 
107
     */
 
108
    if(no_perm_create_pass)
 
109
      prc->rd->flags |= NO_PERM_CACHE;
 
110
 
 
111
    try_cache = rd_read_metadata(prc->rd);
 
112
 
 
113
    if(prc->rd->access == MaybeRorW){
 
114
        if(prc->rd->read_status == 'R' ||
 
115
           !(which_vars == ParsePers || which_vars == ParsePersPost)){
 
116
            prc->rd->access = ReadOnly;
 
117
            prc->rd->read_status = 'R';
 
118
        }
 
119
        else
 
120
          prc->rd->access = ReadWrite;
 
121
    }
 
122
 
 
123
    if(prc->rd->access != NoExists){
 
124
 
 
125
        rd_check_remvalid(prc->rd, 1L);
 
126
 
 
127
        /*
 
128
         * If the cached info says it is readonly but
 
129
         * it looks like it's been fixed now, change it to readwrite.
 
130
         */
 
131
        if((which_vars == ParsePers || which_vars == ParsePersPost) &&
 
132
           prc->rd->read_status == 'R'){
 
133
            /*
 
134
             * We go to this trouble since readonly pinercs
 
135
             * are likely a mistake. They are usually supposed to be
 
136
             * readwrite so we open it and check if it's been fixed.
 
137
             */
 
138
            rd_check_readonly_access(prc->rd);
 
139
            if(prc->rd->read_status == 'W'){
 
140
                prc->rd->access = ReadWrite;
 
141
                prc->rd->flags |= REM_OUTOFDATE;
 
142
            }
 
143
            else
 
144
              prc->rd->access = ReadOnly;
 
145
        }
 
146
 
 
147
        if(prc->rd->flags & REM_OUTOFDATE){
 
148
            if(rd_update_local(prc->rd) != 0){
 
149
                if(!no_perm_create_pass && prc->rd->flags & NO_PERM_CACHE
 
150
                   && !(prc->rd->flags & USER_SAID_NO)){
 
151
                    /*
 
152
                     * We don't check for the existence of the remote
 
153
                     * folder when this flag is turned on, so we could
 
154
                     * fail here because the remote folder doesn't exist.
 
155
                     * We try to create it.
 
156
                     */
 
157
                    no_perm_create_pass++;
 
158
                    goto create_the_remote_folder;
 
159
                }
 
160
 
 
161
                dprint((1,
 
162
                       "read_pinerc_remote: rd_update_local failed\n"));
 
163
                /*
 
164
                 * Don't give up altogether. We still may be
 
165
                 * able to use a cached copy.
 
166
                 */
 
167
            }
 
168
            else{
 
169
                dprint((7,
 
170
                       "%s: copied remote to local (%ld)\n",
 
171
                       prc->rd->rn ? prc->rd->rn : "?",
 
172
                       (long)prc->rd->last_use));
 
173
            }
 
174
        }
 
175
 
 
176
        if(prc->rd->access == ReadWrite)
 
177
          prc->rd->flags |= DO_REMTRIM;
 
178
    }
 
179
 
 
180
    /* If we couldn't get to remote folder, try using the cached copy */
 
181
    if(prc->rd->access == NoExists || prc->rd->flags & REM_OUTOFDATE){
 
182
        if(try_cache){
 
183
            prc->rd->access = ReadOnly;
 
184
            prc->rd->flags |= USE_OLD_CACHE;
 
185
            q_status_message(SM_ORDER, 3, 4,
 
186
             "Can't contact remote config server, using cached copy");
 
187
            dprint((2,
 
188
    "Can't open remote pinerc %s, using local cached copy %s readonly\n",
 
189
                   prc->rd->rn ? prc->rd->rn : "?",
 
190
                   prc->rd->lf ? prc->rd->lf : "?"));
 
191
        }
 
192
        else{
 
193
            prc->rd->flags &= ~DO_REMTRIM;
 
194
            goto bail_out;
 
195
        }
 
196
    }
 
197
 
 
198
    if(prc->rd->flags & NO_FILE)
 
199
      /* copy text, leave sonofile for later use */
 
200
      file = cpystr((char *)so_text(prc->rd->sonofile));
 
201
    else
 
202
      file = read_file(prc->rd->lf, 0);
 
203
 
 
204
bail_out:
 
205
    if((which_vars == ParsePers || which_vars == ParsePersPost) &&
 
206
       (!file || !prc->rd || prc->rd->access != ReadWrite)){
 
207
        prc->readonly = 1;
 
208
        if(prc == ps_global->prc)
 
209
          ps_global->readonly_pinerc = 1;
 
210
    }
 
211
    
 
212
   return(file);
 
213
}
 
214
 
 
215
 
 
216
/*
 
217
 * Check if the remote data folder exists and create an empty folder
 
218
 * if it doesn't exist.
 
219
 *
 
220
 * Args -        type -- The type of remote storage.
 
221
 *        remote_name -- 
 
222
 *          type_spec -- Type-specific data.
 
223
 *              flags -- 
 
224
 *         err_prefix -- Should usually end with a SPACE
 
225
 *         err_suffix -- Should usually begin with a SPACE
 
226
 *
 
227
 * Returns a pointer to a REMDATA_S with access set to either
 
228
 * NoExists or MaybeRorW. On success, "so" will point to a storage object.
 
229
 */
 
230
REMDATA_S *
 
231
rd_create_remote(RemType type, char *remote_name, void *type_spec,
 
232
                 unsigned int *flags, char *err_prefix, char *err_suffix)
 
233
{
 
234
    REMDATA_S *rd = NULL;
 
235
    CONTEXT_S *dummy_cntxt = NULL;
 
236
 
 
237
    dprint((7, "rd_create_remote \"%s\"\n",
 
238
           remote_name ? remote_name : "?"));
 
239
 
 
240
    rd = rd_new_remdata(type, remote_name, type_spec);
 
241
    if(flags)
 
242
      rd->flags = *flags;
 
243
    
 
244
    switch(rd->type){
 
245
      case RemImap:
 
246
        if(rd->flags & NO_PERM_CACHE){
 
247
            if(rd->rn && (rd->so = so_get(CharStar, NULL, WRITE_ACCESS))){
 
248
                if(rd->flags & NO_FILE){
 
249
                    rd->sonofile = so_get(CharStar, NULL, WRITE_ACCESS);
 
250
                    if(!rd->sonofile)
 
251
                      rd->flags &= ~NO_FILE;
 
252
                }
 
253
 
 
254
                /*
 
255
                 * We're not going to check if it is there in this case,
 
256
                 * in order to save ourselves some round trips and
 
257
                 * connections. We'll just try to select it and then
 
258
                 * recover at that point if it isn't already there.
 
259
                 */
 
260
                rd->flags |= REM_OUTOFDATE;
 
261
                rd->access = MaybeRorW;
 
262
            }
 
263
 
 
264
        }
 
265
        else{
 
266
            /*
 
267
             * Open_fcc creates the folder if it didn't already exist.
 
268
             */
 
269
            if(rd->rn && (rd->so = open_fcc(rd->rn, &dummy_cntxt, 1,
 
270
                                            err_prefix, err_suffix)) != NULL){
 
271
                /*
 
272
                 * We know the folder is there but don't know what access
 
273
                 * rights we have until we try to select it, which we don't
 
274
                 * want to do unless we have to. So delay evaluating.
 
275
                 */
 
276
                rd->access = MaybeRorW;
 
277
            }
 
278
        }
 
279
 
 
280
        break;
 
281
 
 
282
      default:
 
283
        q_status_message(SM_ORDER, 3,5, "rd_create_remote: type not supported");
 
284
        break;
 
285
    }
 
286
 
 
287
    return(rd);
 
288
}
 
289
 
 
290
 
 
291
REMDATA_S *
 
292
rd_new_remdata(RemType type, char *remote_name, void *type_spec)
 
293
{
 
294
    REMDATA_S *rd = NULL;
 
295
 
 
296
    rd = (REMDATA_S *)fs_get(sizeof(*rd));
 
297
    memset((void *)rd, 0, sizeof(*rd));
 
298
 
 
299
    rd->type   = type;
 
300
    rd->access = NoExists;
 
301
 
 
302
    if(remote_name)
 
303
      rd->rn = cpystr(remote_name);
 
304
 
 
305
    switch(rd->type){
 
306
      case RemImap:
 
307
        if(type_spec)
 
308
          rd->t.i.special_hdr = cpystr((char *)type_spec);
 
309
 
 
310
        break;
 
311
 
 
312
      default:
 
313
        q_status_message(SM_ORDER, 3,5, "rd_new_remdata: type not supported");
 
314
        break;
 
315
    }
 
316
 
 
317
    return(rd);
 
318
}
 
319
 
 
320
 
 
321
/*
 
322
 * Closes the remote stream and frees.
 
323
 */
 
324
void
 
325
rd_free_remdata(REMDATA_S **rd)
 
326
{
 
327
    if(rd && *rd){
 
328
        rd_close_remote(*rd);
 
329
 
 
330
        if((*rd)->rn)
 
331
          fs_give((void **)&(*rd)->rn);
 
332
 
 
333
        if((*rd)->lf)
 
334
          fs_give((void **)&(*rd)->lf);
 
335
 
 
336
        if((*rd)->so){
 
337
            so_give(&(*rd)->so);
 
338
            (*rd)->so = NULL;
 
339
        }
 
340
 
 
341
        if((*rd)->sonofile){
 
342
            so_give(&(*rd)->sonofile);
 
343
            (*rd)->sonofile = NULL;
 
344
        }
 
345
 
 
346
        switch((*rd)->type){
 
347
          case RemImap:
 
348
            if((*rd)->t.i.special_hdr)
 
349
              fs_give((void **)&(*rd)->t.i.special_hdr);
 
350
 
 
351
            if((*rd)->t.i.chk_date)
 
352
              fs_give((void **)&(*rd)->t.i.chk_date);
 
353
            
 
354
            break;
 
355
 
 
356
          default:
 
357
            q_status_message(SM_ORDER, 3, 5,
 
358
                             "rd_free_remdata: type not supported");
 
359
            break;
 
360
        }
 
361
 
 
362
        fs_give((void **)rd);
 
363
    }
 
364
}
 
365
 
 
366
 
 
367
/*
 
368
 * Call this when finished with the remdata. This does the REMTRIM if the
 
369
 * flag is set, the DEL_FILE if the flag is set. It also closes the stream
 
370
 * and frees the rd.
 
371
 */
 
372
void
 
373
rd_trim_remdata(REMDATA_S **rd)
 
374
{
 
375
    if(!(rd && *rd))
 
376
      return;
 
377
 
 
378
    switch((*rd)->type){
 
379
      case RemImap:
 
380
        /*
 
381
         * Trim the number of saved copies of remote data history.
 
382
         * The first message is a fake message that always
 
383
         * stays there, then come ps_global->remote_abook_history messages
 
384
         * which are each a revision of the data, then comes the active
 
385
         * data.
 
386
         */
 
387
        if((*rd)->flags & DO_REMTRIM &&
 
388
           !((*rd)->flags & REM_OUTOFDATE) &&
 
389
           (*rd)->t.i.chk_nmsgs > ps_global->remote_abook_history + 2){
 
390
 
 
391
            /* make sure stream is open */
 
392
            rd_ping_stream(*rd);
 
393
            rd_open_remote(*rd);
 
394
 
 
395
            if(!rd_remote_is_readonly(*rd)){
 
396
                if((*rd)->t.i.stream &&
 
397
                   (*rd)->t.i.stream->nmsgs >
 
398
                                        ps_global->remote_abook_history + 2){
 
399
                    char sequence[20];
 
400
                    int  user_deleted = 0;
 
401
 
 
402
                    /*
 
403
                     * If user manually deleted some, we'd better not delete
 
404
                     * any more.
 
405
                     */
 
406
                    if(count_flagged((*rd)->t.i.stream, F_DEL) == 0L){
 
407
 
 
408
                        dprint((4, "  rd_trim: trimming remote: mark msgs 2-%ld deleted (%s)\n", (*rd)->t.i.stream->nmsgs - 1 - ps_global->remote_abook_history, (*rd)->rn ? (*rd)->rn : "?"));
 
409
                        snprintf(sequence, sizeof(sequence), "2:%ld",
 
410
                (*rd)->t.i.stream->nmsgs - 1 - ps_global->remote_abook_history);
 
411
                        mail_flag((*rd)->t.i.stream, sequence,
 
412
                                  "\\DELETED", ST_SET);
 
413
                    }
 
414
                    else
 
415
                      user_deleted++;
 
416
 
 
417
                    mail_expunge((*rd)->t.i.stream);
 
418
 
 
419
                    if(!user_deleted)
 
420
                      rd_update_metadata(*rd, NULL);
 
421
                    /* else
 
422
                     *  don't update metafile because user is messing with
 
423
                     *  the remote folder manually. We'd better re-read it next
 
424
                     *  time.  */
 
425
                }
 
426
            }
 
427
 
 
428
            ps_global->noshow_error = 0;
 
429
        }
 
430
 
 
431
        break;
 
432
 
 
433
      default:
 
434
        q_status_message(SM_ORDER, 3,5, "rd_trim_remdata: type not supported");
 
435
        break;
 
436
    }
 
437
}
 
438
 
 
439
 
 
440
/*
 
441
 * All done with this remote data. Trim the folder, close the
 
442
 * stream, and free.
 
443
 */
 
444
void
 
445
rd_close_remdata(REMDATA_S **rd)
 
446
{
 
447
    if(!(rd && *rd))
 
448
      return;
 
449
 
 
450
    rd_trim_remdata(rd);
 
451
 
 
452
    if((*rd)->lf && (*rd)->flags & DEL_FILE)
 
453
      our_unlink((*rd)->lf);
 
454
 
 
455
    /* this closes the stream and frees memory */
 
456
    rd_free_remdata(rd);
 
457
}
 
458
 
 
459
 
 
460
/*
 
461
 * Looks in the metadata file for the cache line corresponding to rd and
 
462
 * fills in data in rd.
 
463
 *
 
464
 * Return value -- 1 if it is likely that the filename we're returning
 
465
 *   is the permanent name of the local cache file and it may already have
 
466
 *   a cached copy of the data. This is to tell us if it makes sense to use
 
467
 *   the cached copy when we are unable to contact the remote server.
 
468
 *   0 otherwise.
 
469
 */
 
470
int
 
471
rd_read_metadata(REMDATA_S *rd)
 
472
{
 
473
    REMDATA_META_S *rab = NULL;
 
474
    int             try_cache = 0;
 
475
    struct variable *vars = ps_global->vars;
 
476
 
 
477
    dprint((7, "rd_read_metadata \"%s\"\n",
 
478
           (rd && rd->rn) ? rd->rn : "?"));
 
479
 
 
480
    if(!rd)
 
481
      return try_cache;
 
482
 
 
483
    if(rd->flags & NO_PERM_CACHE)
 
484
      rab = NULL;
 
485
    else
 
486
      rab = rd_find_our_metadata(rd->rn, &rd->flags);
 
487
 
 
488
    if(!rab){
 
489
        if(!rd->lf){
 
490
            rd->flags |= (NO_META_UPDATE | REM_OUTOFDATE);
 
491
            if(!(rd->flags & NO_FILE)){
 
492
                rd->lf = temp_nam(NULL, "a6", 0);
 
493
                rd->flags |= DEL_FILE;
 
494
            }
 
495
 
 
496
            /* display error */
 
497
            if(!(rd->flags & NO_PERM_CACHE))
 
498
              display_message('x');
 
499
 
 
500
            dprint((2, "using temp cache file %s\n",
 
501
                   rd->lf ? rd->lf : "<none>"));
 
502
        }
 
503
    }
 
504
    else if(rab->local_cache_file){     /* A-OK, it was in the file already */
 
505
        if(!is_absolute_path(rab->local_cache_file)){
 
506
            char  dir[MAXPATH+1], path[MAXPATH+1];
 
507
            char *lc;
 
508
 
 
509
            /*
 
510
             * This should be the normal case. The file is stored as a
 
511
             * filename in the pinerc dir, so that it can be
 
512
             * accessed from the PC or from unix where the pathnames to
 
513
             * get there will be different.
 
514
             */
 
515
 
 
516
            if((lc = last_cmpnt(ps_global->pinerc)) != NULL){
 
517
                int to_copy;
 
518
 
 
519
                to_copy = (lc - ps_global->pinerc > 1)
 
520
                            ? (lc - ps_global->pinerc - 1) : 1;
 
521
                strncpy(dir, ps_global->pinerc, MIN(to_copy, sizeof(dir)-1));
 
522
                dir[MIN(to_copy, sizeof(dir)-1)] = '\0';
 
523
            }
 
524
            else{
 
525
                dir[0] = '.';
 
526
                dir[1] = '\0';
 
527
            }
 
528
 
 
529
            build_path(path, dir, rab->local_cache_file, sizeof(path));
 
530
            rd->lf = cpystr(path);
 
531
        }
 
532
        else{
 
533
            rd->lf = rab->local_cache_file;
 
534
            /* don't free this below, we're using it */
 
535
            rab->local_cache_file = NULL;
 
536
        }
 
537
 
 
538
        rd->read_status = rab->read_status;
 
539
 
 
540
        switch(rd->type){
 
541
          case RemImap:
 
542
            rd->t.i.chk_date    = rab->date;
 
543
            rab->date = NULL;   /* don't free this below, we're using it */
 
544
 
 
545
            dprint((7,
 
546
               "in read_metadata, setting chk_date from metadata to ->%s<-\n",
 
547
               rd->t.i.chk_date ? rd->t.i.chk_date : "?"));
 
548
            rd->t.i.chk_nmsgs   = rab->nmsgs;
 
549
            rd->t.i.uidvalidity = rab->uidvalidity;
 
550
            rd->t.i.uidnext     = rab->uidnext;
 
551
            rd->t.i.uid         = rab->uid;
 
552
            rd->t.i.chk_nmsgs   = rab->nmsgs;
 
553
            dprint((7,
 
554
              "setting uid=%lu uidnext=%lu uidval=%lu read_stat=%c nmsgs=%lu\n",
 
555
                 rd->t.i.uid, rd->t.i.uidnext, rd->t.i.uidvalidity,
 
556
                 rd->read_status ? rd->read_status : '0',
 
557
                 rd->t.i.chk_nmsgs));
 
558
            break;
 
559
 
 
560
          default:
 
561
            q_status_message(SM_ORDER, 3, 5,
 
562
                             "rd_read_metadata: type not supported");
 
563
            break;
 
564
        }
 
565
 
 
566
        if(rd->t.i.chk_nmsgs > 0)
 
567
          try_cache++;  /* cache should be valid if we can't contact server */
 
568
    }
 
569
    /*
 
570
     * The line for this data wasn't in the metadata file yet.
 
571
     * Figure out what should go there and put it in.
 
572
     */
 
573
    else{
 
574
        /*
 
575
         * The local_cache_file is where we will store the cached local
 
576
         * copy of the remote data.
 
577
         */
 
578
        rab->local_cache_file = tempfile_in_same_dir(ps_global->pinerc,
 
579
                                                     meta_prefix, NULL, 0);
 
580
        if(rab->local_cache_file){
 
581
            rd->lf = rab->local_cache_file;
 
582
            rd_write_metadata(rd, 0);
 
583
            rab->local_cache_file = NULL;
 
584
        }
 
585
        else
 
586
          rd->lf = temp_nam(NULL, "a7", 0);
 
587
    }
 
588
 
 
589
    if(rab){
 
590
        if(rab->local_cache_file)
 
591
          fs_give((void **)&rab->local_cache_file);
 
592
        if(rab->date)
 
593
          fs_give((void **)&rab->date);
 
594
        fs_give((void **)&rab);
 
595
    }
 
596
 
 
597
    return(try_cache);
 
598
}
 
599
 
 
600
 
 
601
/*
 
602
 * Write out the contents of the metadata file.
 
603
 *
 
604
 * Each line should be: folder_name cache_file uidvalidity uidnext uid nmsgs
 
605
 *                                                           read_status date
 
606
 *
 
607
 * If delete_it is set, then remove the line instead of updating it.
 
608
 *
 
609
 * We have to be careful with the metadata, because it exists in user's
 
610
 * existing metadata files now, and it is oriented towards only RemImap.
 
611
 * We would have to change the version number and the format of the lines
 
612
 * in the file if we add another type.
 
613
 */
 
614
void
 
615
rd_write_metadata(REMDATA_S *rd, int delete_it)
 
616
{
 
617
    char *tempfile;
 
618
    FILE *fp_old = NULL, *fp_new = NULL;
 
619
    char *p, *pinerc_dir = NULL, *metafile = NULL;
 
620
    char *rel_filename, *key;
 
621
    char  line[MAILTMPLEN];
 
622
    int   fd;
 
623
    struct variable *vars = ps_global->vars;
 
624
 
 
625
    dprint((7, "rd_write_metadata \"%s\"\n",
 
626
           (rd && rd->rn) ? rd->rn : "?"));
 
627
 
 
628
    if(!rd || rd->flags & NO_META_UPDATE)
 
629
      return;
 
630
 
 
631
    if(rd->type != RemImap){
 
632
        q_status_message(SM_ORDER, 3, 5,
 
633
                         "rd_write_metadata: type not supported");
 
634
        return;
 
635
    }
 
636
 
 
637
    dprint((9, " - rd_write_metadata: rn=%s lf=%s\n",
 
638
           rd->rn ? rd->rn : "?", rd->lf ? rd->lf : "?"));
 
639
 
 
640
    key = rd->rn;
 
641
 
 
642
    if(!(pith_opt_rd_metadata_name && (metafile = (*pith_opt_rd_metadata_name)())))
 
643
      goto io_err;
 
644
 
 
645
    if(!(tempfile = tempfile_in_same_dir(metafile, "a9", &pinerc_dir, 0)))
 
646
      goto io_err;
 
647
 
 
648
    if((fd = our_open(tempfile, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0600)) >= 0)
 
649
      fp_new = fdopen(fd, "w");
 
650
    
 
651
    if(pinerc_dir && rd->lf && strlen(rd->lf) > strlen(pinerc_dir))
 
652
      rel_filename = rd->lf + strlen(pinerc_dir) + 1;
 
653
    else
 
654
      rel_filename = rd->lf;
 
655
 
 
656
    if(pinerc_dir)
 
657
      fs_give((void **)&pinerc_dir);
 
658
 
 
659
    fp_old = our_fopen(metafile, "rb");
 
660
 
 
661
    if(fp_new && fp_old){
 
662
        /*
 
663
         * Write the header line.
 
664
         */
 
665
        if(fprintf(fp_new, "%s %s Pine Metadata\n",
 
666
                   PMAGIC, METAFILE_VERSION_NUM) == EOF)
 
667
          goto io_err;
 
668
 
 
669
        while((p = fgets(line, sizeof(line), fp_old)) != NULL){
 
670
            /*
 
671
             * Skip the header line and any lines that don't begin
 
672
             * with a "{".
 
673
             */
 
674
            if(line[0] != '{')
 
675
              continue;
 
676
 
 
677
            /* skip the old cache line for this key */
 
678
            if(strncmp(line, key, strlen(key)) == 0 && line[strlen(key)] == TAB)
 
679
              continue;
 
680
            
 
681
            /* add this line to new version of file */
 
682
            if(fputs(p, fp_new) == EOF)
 
683
              goto io_err;
 
684
        }
 
685
    }
 
686
 
 
687
    /* add the cache line for this key */
 
688
    /* Warning: this is type RemImap specific right now! */
 
689
    if(!delete_it){
 
690
        if(!tempfile ||
 
691
           !fp_old ||
 
692
           !fp_new ||
 
693
           p ||
 
694
           fprintf(fp_new, "%s\t%s\t%lu\t%lu\t%lu\t%lu\t%c\t%s\n",
 
695
                            key ? key : "",
 
696
                            rel_filename ? rel_filename : "",
 
697
                            rd->t.i.uidvalidity, rd->t.i.uidnext, rd->t.i.uid,
 
698
                            rd->t.i.chk_nmsgs,
 
699
                            rd->read_status ? rd->read_status : '?',
 
700
                            rd->t.i.chk_date ? rd->t.i.chk_date : "no-match")
 
701
                                == EOF)
 
702
          goto io_err;
 
703
    }
 
704
 
 
705
    if(fclose(fp_new) == EOF){
 
706
        fp_new = NULL;
 
707
        goto io_err;
 
708
    }
 
709
 
 
710
    if(fclose(fp_old) == EOF){
 
711
        fp_old = NULL;
 
712
        goto io_err;
 
713
    }
 
714
 
 
715
    if(rename_file(tempfile, metafile) < 0)
 
716
      goto io_err;
 
717
 
 
718
    if(tempfile)
 
719
      fs_give((void **)&tempfile);
 
720
    
 
721
    if(metafile)
 
722
      fs_give((void **)&metafile);
 
723
    
 
724
    return;
 
725
 
 
726
io_err:
 
727
    dprint((2, "io_err in rd_write_metadata(%s), tempfile=%s: %s\n",
 
728
            metafile ? metafile : "<NULL>", tempfile ? tempfile : "<NULL>",
 
729
            error_description(errno)));
 
730
    q_status_message2(SM_ORDER, 3, 5,
 
731
                    "Trouble updating metafile %s, continuing (%s)",
 
732
                    metafile ? metafile : "<NULL>", error_description(errno));
 
733
    if(tempfile){
 
734
        our_unlink(tempfile);
 
735
        fs_give((void **)&tempfile);
 
736
    }
 
737
    if(metafile)
 
738
      fs_give((void **)&metafile);
 
739
    if(fp_old)
 
740
      (void)fclose(fp_old);
 
741
    if(fp_new)
 
742
      (void)fclose(fp_new);
 
743
}
 
744
 
 
745
 
 
746
void
 
747
rd_update_metadata(REMDATA_S *rd, char *date)
 
748
{
 
749
    if(!rd)
 
750
      return;
 
751
 
 
752
    dprint((9, " - rd_update_metadata: rn=%s lf=%s\n",
 
753
           rd->rn ? rd->rn : "?", rd->lf ? rd->lf : "<none>"));
 
754
 
 
755
    switch(rd->type){
 
756
      case RemImap:
 
757
        if(rd->t.i.stream){
 
758
            ps_global->noshow_error = 1;
 
759
            rd_ping_stream(rd);
 
760
            if(rd->t.i.stream){
 
761
                /*
 
762
                 * If nmsgs < 2 then something is wrong. Maybe it is just
 
763
                 * that we haven't been told about the messages we've
 
764
                 * appended ourselves. Try closing and re-opening the stream
 
765
                 * to see those.
 
766
                 */
 
767
                if(rd->t.i.stream->nmsgs < 2 ||
 
768
                   (rd->t.i.shouldbe_nmsgs &&
 
769
                    (rd->t.i.shouldbe_nmsgs != rd->t.i.stream->nmsgs))){
 
770
                    pine_mail_check(rd->t.i.stream);
 
771
                    if(rd->t.i.stream->nmsgs < 2 ||
 
772
                       (rd->t.i.shouldbe_nmsgs &&
 
773
                        (rd->t.i.shouldbe_nmsgs != rd->t.i.stream->nmsgs))){
 
774
                        rd_close_remote(rd);
 
775
                        rd_open_remote(rd);
 
776
                    }
 
777
                }
 
778
 
 
779
                rd->t.i.chk_nmsgs = rd->t.i.stream ? rd->t.i.stream->nmsgs : 0L;
 
780
 
 
781
                /*
 
782
                 * If nmsgs < 2 something is wrong.
 
783
                 */
 
784
                if(rd->t.i.chk_nmsgs < 2){
 
785
                    rd->t.i.uidvalidity = 0L;
 
786
                    rd->t.i.uid = 0L;
 
787
                    rd->t.i.uidnext = 0L;
 
788
                }
 
789
                else{
 
790
                    rd->t.i.uidvalidity = rd->t.i.stream->uid_validity;
 
791
                    rd->t.i.uid = mail_uid(rd->t.i.stream,
 
792
                                           rd->t.i.stream->nmsgs);
 
793
                    /*
 
794
                     * Uid_last is not always valid. If the last known uid is
 
795
                     * greater than uid_last, go with it instead (uid+1).
 
796
                     * If our guess is wrong (too low), the penalty is not
 
797
                     * harsh. When the uidnexts don't match we open the
 
798
                     * mailbox to check the uid of the actual last message.
 
799
                     * If it was a false hit then we adjust uidnext so it
 
800
                     * will be correct the next time through.
 
801
                     */
 
802
                    rd->t.i.uidnext = MAX(rd->t.i.stream->uid_last,rd->t.i.uid)
 
803
                                        + 1;
 
804
                }
 
805
            }
 
806
 
 
807
            ps_global->noshow_error = 0;
 
808
 
 
809
            /*
 
810
             * Save the date so that we can check if it changed next time
 
811
             * we go to write.
 
812
             */
 
813
            if(date){
 
814
                if(rd->t.i.chk_date)
 
815
                  fs_give((void **)&rd->t.i.chk_date);
 
816
                
 
817
                rd->t.i.chk_date = cpystr(date);
 
818
            }
 
819
 
 
820
            rd_write_metadata(rd, 0);
 
821
        }
 
822
 
 
823
        rd->t.i.shouldbe_nmsgs = 0;
 
824
 
 
825
        break;
 
826
 
 
827
      default:
 
828
        q_status_message(SM_ORDER, 3, 5,
 
829
                         "rd_update_metadata: type not supported");
 
830
        break;
 
831
    }
 
832
}
 
833
 
 
834
 
 
835
 
 
836
 
 
837
REMDATA_META_S *
 
838
rd_find_our_metadata(char *key, long unsigned int *flags)
 
839
{
 
840
    char        *p, *q, *metafile = NULL;
 
841
    char         line[MAILTMPLEN];
 
842
    REMDATA_META_S *rab = NULL;
 
843
    FILE        *fp;
 
844
 
 
845
    dprint((9, "rd_find_our_metadata \"%s\"\n", key ? key : "?"));
 
846
 
 
847
    if(!(key && *key))
 
848
      return rab;
 
849
 
 
850
try_once_more:
 
851
 
 
852
    if(!(pith_opt_rd_metadata_name && (metafile = (*pith_opt_rd_metadata_name)()) != NULL))
 
853
      return rab;
 
854
 
 
855
    /*
 
856
     * Open the metadata file and get some information out.
 
857
     */
 
858
    fp = our_fopen(metafile, "rb");
 
859
    if(fp == NULL){
 
860
        q_status_message2(SM_ORDER, 3, 5,
 
861
                   _("can't open metadata file %s, continuing (%s)"),
 
862
                   metafile, error_description(errno));
 
863
        dprint((2,
 
864
                   "can't open existing metadata file %s: %s\n",
 
865
                   metafile ? metafile : "?", error_description(errno)));
 
866
        if(flags)
 
867
          (*flags) |= NO_META_UPDATE;
 
868
 
 
869
        fs_give((void **)&metafile);
 
870
 
 
871
        return rab;
 
872
    }
 
873
 
 
874
    /*
 
875
     * If we make it to this point where we have opened the metadata file
 
876
     * we return a structure (possibly empty) instead of just a NULL pointer.
 
877
     */
 
878
    rab = (REMDATA_META_S *)fs_get(sizeof(*rab));
 
879
    memset(rab, 0, sizeof(*rab));
 
880
 
 
881
    /*
 
882
     * Check for header line. If it isn't there or is incorrect,
 
883
     * return with the empty rab. This call also positions the file pointer
 
884
     * past the header line.
 
885
     */
 
886
    if(rd_meta_is_broken(fp)){
 
887
 
 
888
        dprint((2,
 
889
                   "metadata file is broken, creating new one: %s\n",
 
890
                   metafile ? metafile : "?"));
 
891
 
 
892
        /* Make it size zero so we won't copy any bad stuff */
 
893
        if(fp_file_size(fp) != 0){
 
894
            int fd;
 
895
 
 
896
            (void)fclose(fp);
 
897
            our_unlink(metafile);
 
898
 
 
899
            if((fd = our_open(metafile, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0600)) < 0){
 
900
                q_status_message2(SM_ORDER, 3, 5,
 
901
                       _("can't create metadata file %s, continuing (%s)"),
 
902
                         metafile, error_description(errno));
 
903
                dprint((2,
 
904
                           "can't create metadata file %s: %s\n",
 
905
                           metafile ? metafile : "?",
 
906
                           error_description(errno)));
 
907
                fs_give((void **)&rab);
 
908
                fs_give((void **)&metafile);
 
909
                return NULL;
 
910
            }
 
911
 
 
912
            (void)close(fd);
 
913
        }
 
914
        else
 
915
          (void)fclose(fp);
 
916
 
 
917
        fs_give((void **)&metafile);
 
918
        return(rab);
 
919
    }
 
920
 
 
921
    fs_give((void **)&metafile);
 
922
 
 
923
    /* Look for our line */
 
924
    while((p = fgets(line, sizeof(line), fp)) != NULL)
 
925
      if(strncmp(line, key, strlen(key)) == 0 && line[strlen(key)] == TAB)
 
926
        break;
 
927
    
 
928
#define SKIP_TO_TAB(p) do{while(*p && *p != TAB)p++;}while(0)
 
929
 
 
930
    /*
 
931
     * The line should be TAB-separated with fields:
 
932
     * folder_name cache_file uidvalidity uidnext uid nmsgs read_status date
 
933
     * This part is highly RemImap-specific right now.
 
934
     */
 
935
    if(p){                              /* Found the line, parse it. */
 
936
      SKIP_TO_TAB(p);                   /* skip to TAB following folder_name */
 
937
      if(*p == TAB){
 
938
        q = ++p;                        /* q points to cache_file */
 
939
        SKIP_TO_TAB(p);                 /* skip to TAB following cache_file */
 
940
        if(*p == TAB){
 
941
          *p = '\0';
 
942
          rab->local_cache_file = cpystr(q);
 
943
          q = ++p;                      /* q points to uidvalidity */
 
944
          SKIP_TO_TAB(p);               /* skip to TAB following uidvalidity */
 
945
          if(*p == TAB){
 
946
            *p = '\0';
 
947
            rab->uidvalidity = strtoul(q,(char **)NULL,10);
 
948
            q = ++p;                    /* q points to uidnext */
 
949
            SKIP_TO_TAB(p);             /* skip to TAB following uidnext */
 
950
            if(*p == TAB){
 
951
              *p = '\0';
 
952
              rab->uidnext = strtoul(q,(char **)NULL,10);
 
953
              q = ++p;                  /* q points to uid */
 
954
              SKIP_TO_TAB(p);           /* skip to TAB following uid */
 
955
              if(*p == TAB){
 
956
                *p = '\0';
 
957
                rab->uid = strtoul(q,(char **)NULL,10);
 
958
                q = ++p;                /* q points to nmsgs */
 
959
                SKIP_TO_TAB(p);         /* skip to TAB following nmsgs */
 
960
                if(*p == TAB){
 
961
                  *p = '\0';
 
962
                  rab->nmsgs = strtoul(q,(char **)NULL,10);
 
963
                  q = ++p;              /* q points to read_status */
 
964
                  SKIP_TO_TAB(p);       /* skip to TAB following read_status */
 
965
                  if(*p == TAB){
 
966
                    *p = '\0';
 
967
                    rab->read_status = *q;  /* just a char, not whole string */
 
968
                    q = ++p;            /* q points to date */
 
969
                    while(*p && *p != '\n' && *p != '\r')  /* skip to newline */
 
970
                      p++;
 
971
                
 
972
                    *p = '\0';
 
973
                    rab->date = cpystr(q);
 
974
                  }
 
975
                }
 
976
              }
 
977
            }
 
978
          }
 
979
        }
 
980
      }
 
981
    }
 
982
 
 
983
    (void)fclose(fp);
 
984
    return(rab);
 
985
}
 
986
 
 
987
 
 
988
/*
 
989
 * Returns: -1 if this doesn't look like a metafile or some error,
 
990
 *               or if it looks like a non-current metafile.
 
991
 *           0 if it looks like a current metafile.
 
992
 *
 
993
 * A side effect is that the file pointer will be pointing to the second
 
994
 * line if 0 is returned.
 
995
 */
 
996
int
 
997
rd_meta_is_broken(FILE *fp)
 
998
{
 
999
    char buf[MAILTMPLEN];
 
1000
 
 
1001
    if(fp == (FILE *)NULL)
 
1002
      return -1;
 
1003
 
 
1004
    if(fp_file_size(fp) <
 
1005
                    (long)(SIZEOF_PMAGIC + SIZEOF_SPACE + SIZEOF_VERSION_NUM))
 
1006
      return -1;
 
1007
    
 
1008
    if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0))
 
1009
      return -1;
 
1010
 
 
1011
    /* check for magic */
 
1012
    if(fread(buf, sizeof(char), (unsigned)SIZEOF_PMAGIC, fp) != SIZEOF_PMAGIC)
 
1013
      return -1;
 
1014
 
 
1015
    buf[SIZEOF_PMAGIC] = '\0';
 
1016
    if(strcmp(buf, PMAGIC) != 0)
 
1017
      return -1;
 
1018
 
 
1019
    /*
 
1020
     * If we change to a new version, we may want to add code here to convert
 
1021
     * or to cleanup after the old version. For example, it might just
 
1022
     * remove the old cache files here.
 
1023
     */
 
1024
 
 
1025
    /* check for matching version number */
 
1026
    if(fseek(fp, (long)TO_FIND_VERSION_NUM, 0))
 
1027
      return -1;
 
1028
    
 
1029
    if(fread(buf, sizeof(char), (unsigned)SIZEOF_VERSION_NUM, fp) !=
 
1030
       SIZEOF_VERSION_NUM)
 
1031
      return -1;
 
1032
    
 
1033
    buf[SIZEOF_VERSION_NUM] = '\0';
 
1034
    if(strcmp(buf, METAFILE_VERSION_NUM) != 0)
 
1035
      return -1;
 
1036
 
 
1037
    /* Position file pointer to second line */
 
1038
    if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0))
 
1039
      return -1;
 
1040
    
 
1041
    if(fgets(buf, sizeof(buf), fp) == NULL)
 
1042
      return -1;
 
1043
 
 
1044
    return 0;
 
1045
}
 
1046
 
 
1047
 
 
1048
/*
 
1049
 * Open a data stream to the remote data.
 
1050
 */
 
1051
void
 
1052
rd_open_remote(REMDATA_S *rd)
 
1053
{
 
1054
    long openmode = SP_USEPOOL | SP_TEMPUSE;
 
1055
 
 
1056
    dprint((7, "rd_open_remote \"%s\"\n",
 
1057
           (rd && rd->rn) ? rd->rn : "?"));
 
1058
 
 
1059
    if(!rd)
 
1060
      return;
 
1061
    
 
1062
    if(rd_stream_exists(rd)){
 
1063
        rd->last_use = get_adj_time();
 
1064
        return;
 
1065
    }
 
1066
 
 
1067
    switch(rd->type){
 
1068
      case RemImap:
 
1069
 
 
1070
        if(rd->access == ReadOnly)
 
1071
          openmode |= OP_READONLY;
 
1072
 
 
1073
        ps_global->noshow_error = 1;
 
1074
        rd->t.i.stream = context_open(NULL, NULL, rd->rn, openmode, NULL);
 
1075
        ps_global->noshow_error = 0;
 
1076
 
 
1077
        /* Don't try to reopen if there was a problem (auth failure, etc.) */
 
1078
        if(!rd->t.i.stream)  
 
1079
          rd->flags |= USER_SAID_NO; /* Caution: overloading USER_SAID_NO */
 
1080
        
 
1081
        if(rd->t.i.stream && rd->t.i.stream->halfopen){
 
1082
            /* this is a failure */
 
1083
            rd_close_remote(rd);
 
1084
        }
 
1085
 
 
1086
        if(rd->t.i.stream)
 
1087
          rd->last_use = get_adj_time();
 
1088
 
 
1089
        break;
 
1090
 
 
1091
      default:
 
1092
        q_status_message(SM_ORDER, 3, 5, "rd_open_remote: type not supported");
 
1093
        break;
 
1094
    }
 
1095
}
 
1096
 
 
1097
 
 
1098
/*
 
1099
 * Close a data stream to the remote data.
 
1100
 */
 
1101
void
 
1102
rd_close_remote(REMDATA_S *rd)
 
1103
{
 
1104
    if(!rd || !rd_stream_exists(rd))
 
1105
      return;
 
1106
 
 
1107
    switch(rd->type){
 
1108
      case RemImap:
 
1109
        ps_global->noshow_error = 1;
 
1110
        pine_mail_close(rd->t.i.stream);
 
1111
        rd->t.i.stream = NULL;
 
1112
        ps_global->noshow_error = 0;
 
1113
        break;
 
1114
 
 
1115
      default:
 
1116
        q_status_message(SM_ORDER, 3, 5, "rd_close_remote: type not supported");
 
1117
        break;
 
1118
    }
 
1119
}
 
1120
 
 
1121
 
 
1122
int
 
1123
rd_stream_exists(REMDATA_S *rd)
 
1124
{
 
1125
    if(!rd)
 
1126
      return(0);
 
1127
 
 
1128
    switch(rd->type){
 
1129
      case RemImap:
 
1130
        return(rd->t.i.stream ? 1 : 0);
 
1131
 
 
1132
      default:
 
1133
        q_status_message(SM_ORDER, 3,5, "rd_stream_exists: type not supported");
 
1134
        break;
 
1135
    }
 
1136
    
 
1137
    return(0);
 
1138
}
 
1139
 
 
1140
 
 
1141
/*
 
1142
 * Ping the stream and return 1 if it is still alive.
 
1143
 */
 
1144
int
 
1145
rd_ping_stream(REMDATA_S *rd)
 
1146
{
 
1147
    int ret = 0;
 
1148
 
 
1149
    dprint((7, "rd_ping_stream \"%s\"\n",
 
1150
           (rd && rd->rn) ? rd->rn : "?"));
 
1151
 
 
1152
    if(!rd)
 
1153
      return(ret);
 
1154
 
 
1155
    if(!rd_stream_exists(rd)){
 
1156
        switch(rd->type){
 
1157
          case RemImap:
 
1158
            /*
 
1159
             * If this stream is already actually open, officially open
 
1160
             * it and use it.
 
1161
             */
 
1162
            if(sp_stream_get(rd->rn, SP_MATCH)){
 
1163
                long openflags = SP_USEPOOL | SP_TEMPUSE;
 
1164
 
 
1165
                if(rd->access == ReadOnly)
 
1166
                  openflags |= OP_READONLY;
 
1167
 
 
1168
                rd->t.i.stream = pine_mail_open(NULL, rd->rn, openflags, NULL);
 
1169
            }
 
1170
 
 
1171
            break;
 
1172
 
 
1173
          default:
 
1174
            break;
 
1175
        }
 
1176
    }
 
1177
 
 
1178
    if(!rd_stream_exists(rd))
 
1179
      return(ret);
 
1180
 
 
1181
    switch(rd->type){
 
1182
      case RemImap:
 
1183
        ps_global->noshow_error = 1;
 
1184
        if(pine_mail_ping(rd->t.i.stream))
 
1185
          ret = 1;
 
1186
        else
 
1187
          rd->t.i.stream = NULL;
 
1188
 
 
1189
        ps_global->noshow_error = 0;
 
1190
        break;
 
1191
 
 
1192
      default:
 
1193
        q_status_message(SM_ORDER, 3, 5, "rd_ping_stream: type not supported");
 
1194
        break;
 
1195
    }
 
1196
    
 
1197
    return(ret);
 
1198
}
 
1199
 
 
1200
 
 
1201
/*
 
1202
 * Change readonly access to readwrite if appropriate. Call this if
 
1203
 * the remote data ought to be readwrite but it is marked readonly in
 
1204
 * the metadata.
 
1205
 */
 
1206
void
 
1207
rd_check_readonly_access(REMDATA_S *rd)
 
1208
{
 
1209
    if(rd && rd->read_status == 'R'){
 
1210
        switch(rd->type){
 
1211
          case RemImap:
 
1212
            rd_open_remote(rd);
 
1213
            if(rd->t.i.stream && !rd->t.i.stream->rdonly){
 
1214
                rd->read_status = 'W';
 
1215
                rd->access = ReadWrite;
 
1216
            }
 
1217
 
 
1218
            break;
 
1219
 
 
1220
          default:
 
1221
            q_status_message(SM_ORDER, 3, 5,
 
1222
                             "rd_check_readonly_access: type not supported");
 
1223
            break;
 
1224
        }
 
1225
    }
 
1226
}
 
1227
 
 
1228
 
 
1229
/*
 
1230
 * Initialize remote data. The remote data is initialized
 
1231
 * with no data. On success, the local file will be empty and the remote
 
1232
 * folder (if type RemImap) will contain a header message and an empty
 
1233
 * data message.
 
1234
 * The remote folder already exists before we get here, though it may be empty.
 
1235
 *
 
1236
 * Returns -1 on failure
 
1237
 *          0 on success
 
1238
 */
 
1239
int
 
1240
rd_init_remote(REMDATA_S *rd, int add_only_first_msg)
 
1241
{
 
1242
    int  err = 0;
 
1243
    char date[200];
 
1244
 
 
1245
    dprint((7, "rd_init_remote \"%s\"\n",
 
1246
           (rd && rd->rn) ? rd->rn : "?"));
 
1247
 
 
1248
    if(rd && rd->type != RemImap){
 
1249
        dprint((1, "rd_init_remote: type not supported\n"));
 
1250
        return -1;
 
1251
    }
 
1252
 
 
1253
    /*
 
1254
     * The rest is currently type RemImap-specific. 
 
1255
     */
 
1256
 
 
1257
    if(!(rd->flags & NO_FILE || rd->lf) ||
 
1258
       !rd->rn || !rd->so || !rd->t.i.stream ||
 
1259
       !rd->t.i.special_hdr){
 
1260
        dprint((1,
 
1261
               "rd_init_remote: Unexpected error: %s is NULL\n",
 
1262
               !(rd->flags & NO_FILE || rd->lf) ? "localfile" :
 
1263
                !rd->rn ? "remotename" :
 
1264
                 !rd->so ? "so" :
 
1265
                  !rd->t.i.stream ? "stream" :
 
1266
                   !rd->t.i.special_hdr ? "special_hdr" : "?"));
 
1267
        return -1;
 
1268
    }
 
1269
 
 
1270
    /* already init'd */
 
1271
    if(rd->t.i.stream->nmsgs >= 2 ||
 
1272
       (rd->t.i.stream->nmsgs >= 1 && add_only_first_msg))
 
1273
      return err;
 
1274
 
 
1275
    dprint((7, " - rd_init_remote(%s): %s\n",
 
1276
           rd->t.i.special_hdr ? rd->t.i.special_hdr : "?",
 
1277
           rd->rn ? rd->rn : "?"));
 
1278
    
 
1279
    /*
 
1280
     * We want to protect the user who specifies an actual address book
 
1281
     * file on the remote system as a remote address book. For example,
 
1282
     * they might say address-book={host}.addressbook where .addressbook
 
1283
     * is just a file on host. Remote address books are folders, not
 
1284
     * files.  We also want to protect the user who specifies an existing
 
1285
     * mail folder as a remote address book. We do that by requiring the
 
1286
     * first message in the folder to be our header message.
 
1287
     */
 
1288
 
 
1289
    if(rd->t.i.stream->rdonly){
 
1290
        q_status_message1(SM_ORDER | SM_DING, 7, 10,
 
1291
                 _("Can't initialize folder \"%s\" (write permission)"), rd->rn);
 
1292
        if(rd->t.i.stream->nmsgs > 0)
 
1293
          q_status_message(SM_ORDER | SM_DING, 7, 10,
 
1294
           _("Choose a new, unused folder for the remote data"));
 
1295
 
 
1296
        dprint((1,
 
1297
               "Can't initialize remote folder \"%s\"\n",
 
1298
               rd->rn ? rd->rn : "?"));
 
1299
        dprint((1,
 
1300
               "   No write permission for that remote folder.\n"));
 
1301
        dprint((1,
 
1302
                   "   Choose a new unused folder for the remote data.\n"));
 
1303
        err = -1;
 
1304
    }
 
1305
 
 
1306
    if(!err){
 
1307
        if(rd->t.i.stream->nmsgs == 0){
 
1308
            int we_cancel;
 
1309
 
 
1310
            we_cancel = busy_cue(_("Initializing remote data"), NULL, 1);
 
1311
            rfc822_date(date);
 
1312
            /*
 
1313
             * The first message in a remote data folder is a special
 
1314
             * header message. It is there as a way to explain what the
 
1315
             * folder is to users who open it trying to read it. It is also
 
1316
             * used as a consistency check so that we don't use a folder
 
1317
             * that was being used for something else as a remote
 
1318
             * data folder.
 
1319
             */
 
1320
            err = rd_add_hdr_msg(rd, date);
 
1321
            if(rd->t.i.stream->nmsgs == 0)
 
1322
              rd_ping_stream(rd);
 
1323
            if(rd->t.i.stream && rd->t.i.stream->nmsgs == 0)
 
1324
              pine_mail_check(rd->t.i.stream);
 
1325
 
 
1326
            if(we_cancel)
 
1327
              cancel_busy_cue(-1);
 
1328
        }
 
1329
        else{
 
1330
            char *eptr = NULL;
 
1331
 
 
1332
            err = rd_chk_for_hdr_msg(&(rd->t.i.stream), rd, &eptr);
 
1333
            if(err){
 
1334
                q_status_message1(SM_ORDER | SM_DING, 5, 5,
 
1335
                     _("\"%s\" has invalid format, can't initialize"), rd->rn);
 
1336
 
 
1337
                dprint((1,
 
1338
                       "Can't initialize remote data \"%s\"\n",
 
1339
                       rd->rn ? rd->rn : "?"));
 
1340
 
 
1341
                if(eptr){
 
1342
                    q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
 
1343
                    dprint((1, "%s\n", eptr ? eptr : "?"));
 
1344
                    fs_give((void **)&eptr);
 
1345
                }
 
1346
            }
 
1347
        }
 
1348
    }
 
1349
 
 
1350
    /*
 
1351
     * Add the second (empty) message.
 
1352
     */
 
1353
    if(!err && !add_only_first_msg){
 
1354
        char *tempfile = NULL;
 
1355
        int   fd;
 
1356
 
 
1357
        if(rd->flags & NO_FILE){
 
1358
            if(so_truncate(rd->sonofile, 0L) == 0)
 
1359
              err = -1;
 
1360
        }
 
1361
        else{
 
1362
            if(!(tempfile = tempfile_in_same_dir(rd->lf, "a8", NULL, 0))){
 
1363
                q_status_message1(SM_ORDER | SM_DING, 3, 5,
 
1364
                                  _("Error opening temporary file: %s"),
 
1365
                                  error_description(errno));
 
1366
                dprint((2, "init_remote: Error opening file: %s\n",
 
1367
                       error_description(errno)));
 
1368
                err = -1;
 
1369
            }
 
1370
 
 
1371
            if(!err &&
 
1372
               (fd = our_open(tempfile, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0600)) < 0){
 
1373
                q_status_message2(SM_ORDER | SM_DING, 3, 5,
 
1374
                                  "Error opening temporary file %.200s: %.200s",
 
1375
                                  tempfile, error_description(errno));
 
1376
                dprint((2,
 
1377
                       "init_remote: Error opening temporary file: %s: %s\n",
 
1378
                       tempfile ? tempfile : "?", error_description(errno)));
 
1379
                our_unlink(tempfile);
 
1380
                err = -1;
 
1381
            }
 
1382
            else
 
1383
              (void)close(fd);
 
1384
 
 
1385
            if(!err && rename_file(tempfile, rd->lf) < 0){
 
1386
                q_status_message2(SM_ORDER | SM_DING, 3, 5,
 
1387
                              "Error creating cache file %.200s: %.200s",
 
1388
                              rd->lf, error_description(errno));
 
1389
                our_unlink(tempfile);
 
1390
                err = -1;
 
1391
            }
 
1392
 
 
1393
            if(tempfile)
 
1394
              fs_give((void **)&tempfile);
 
1395
        }
 
1396
 
 
1397
        if(!err){
 
1398
            err = rd_update_remote(rd, date);
 
1399
            if(err){
 
1400
                q_status_message1(SM_ORDER | SM_DING, 3, 5,
 
1401
                                  _("Error copying to remote folder: %s"),
 
1402
                                  error_description(errno));
 
1403
                
 
1404
                q_status_message(SM_ORDER | SM_DING, 5, 5,
 
1405
                                 "Creation of remote data folder failed");
 
1406
            }
 
1407
        }
 
1408
    }
 
1409
 
 
1410
    if(!err){
 
1411
        rd_update_metadata(rd, date);
 
1412
        /* turn off out of date flag */
 
1413
        rd->flags &= ~REM_OUTOFDATE;
 
1414
    }
 
1415
 
 
1416
    return(err ? -1 : 0);
 
1417
}
 
1418
 
 
1419
 
 
1420
/*
 
1421
 * IMAP stream should already be open to a remote folder.
 
1422
 * Check the first message in the folder to be sure it is the right
 
1423
 * kind of message, not some message from some other folder.
 
1424
 *
 
1425
 * Returns 0 if ok, < 0 if invalid format.
 
1426
 *
 
1427
 */
 
1428
int
 
1429
rd_chk_for_hdr_msg(MAILSTREAM **streamp, REMDATA_S *rd, char **errmsg)
 
1430
{
 
1431
    char *fields[3], *values[3];
 
1432
    char *h;
 
1433
    int   tried_again = 0;
 
1434
    int   ret;
 
1435
    MAILSTREAM *st = NULL;
 
1436
 
 
1437
    fields[0] = rd->t.i.special_hdr;
 
1438
    fields[1] = "received";
 
1439
    fields[2] = NULL;
 
1440
 
 
1441
try_again:
 
1442
    ret = -1;
 
1443
 
 
1444
    if(!streamp || !*streamp){
 
1445
        dprint((1, "rd_chk_for_hdr_msg: stream is null\n"));
 
1446
    }
 
1447
    else if((*streamp)->nmsgs == 0){
 
1448
        ret = -2;
 
1449
        dprint((1,
 
1450
                   "rd_chk_for_hdr_msg: stream has nmsgs=0, try a ping\n"));
 
1451
        if(!pine_mail_ping(*streamp))
 
1452
          *streamp = NULL;
 
1453
 
 
1454
        if(*streamp && (*streamp)->nmsgs == 0){
 
1455
            dprint((1,
 
1456
               "rd_chk_for_hdr_msg: still looks like nmsgs=0, try a check\n"));
 
1457
            pine_mail_check(*streamp);
 
1458
        }
 
1459
 
 
1460
        if(*streamp && (*streamp)->nmsgs == 0){
 
1461
            dprint((1,
 
1462
               "rd_chk_for_hdr_msg: still nmsgs=0, try re-opening stream\n"));
 
1463
 
 
1464
            if(rd_stream_exists(rd))
 
1465
              rd_close_remote(rd);
 
1466
 
 
1467
            rd_open_remote(rd);
 
1468
            if(rd_stream_exists(rd))
 
1469
              st = rd->t.i.stream;
 
1470
        }
 
1471
 
 
1472
        if(!st)
 
1473
          st = *streamp;
 
1474
 
 
1475
        if(st && st->nmsgs == 0){
 
1476
            dprint((1,
 
1477
                       "rd_chk_for_hdr_msg: can't see header message\n"));
 
1478
        }
 
1479
    }
 
1480
    else
 
1481
      st = *streamp;
 
1482
 
 
1483
    if(st && st->nmsgs != 0
 
1484
       && (h=pine_fetchheader_lines(st, 1L, NULL, fields))){
 
1485
        simple_header_parse(h, fields, values);
 
1486
        ret = -3;
 
1487
        if(values[1])
 
1488
          ret = -4;
 
1489
        else if(values[0]){
 
1490
            rd->cookie = strtoul(values[0], (char **)NULL, 10);
 
1491
            if(rd->cookie == 0)
 
1492
              ret = -5;
 
1493
            else if(rd->cookie == 1){
 
1494
                if(rd->flags & COOKIE_ONE_OK || tried_again)
 
1495
                  ret = 0;
 
1496
                else
 
1497
                  ret = -6;
 
1498
            }
 
1499
            else if(rd->cookie > 1)
 
1500
              ret = 0;
 
1501
        }
 
1502
 
 
1503
        if(values[0])
 
1504
          fs_give((void **)&values[0]);
 
1505
 
 
1506
        if(values[1])
 
1507
          fs_give((void **)&values[1]);
 
1508
 
 
1509
        fs_give((void **)&h);
 
1510
    }
 
1511
    
 
1512
    
 
1513
    if(ret && ret != -6 && errmsg){
 
1514
        *errmsg = (char *)fs_get(500 * sizeof(char));
 
1515
        (*errmsg)[0] = '\0';
 
1516
    }
 
1517
 
 
1518
    if(ret == -1){
 
1519
        /* null stream */
 
1520
        if(errmsg)
 
1521
          snprintf(*errmsg, 500, _("Can't open remote address book \"%s\""), rd->rn);
 
1522
    }
 
1523
    else if(ret == -2){
 
1524
        /* no messages in folder */
 
1525
        if(errmsg)
 
1526
          snprintf(*errmsg, 500,
 
1527
                  _("Error: no messages in remote address book \"%s\"!"),
 
1528
                  rd->rn);
 
1529
    }
 
1530
    else if(ret == -3){
 
1531
        /* no cookie */
 
1532
        if(errmsg)
 
1533
          snprintf(*errmsg, 500,
 
1534
                  "First msg in \"%s\" should have \"%s\" header",
 
1535
                  rd->rn, rd->t.i.special_hdr);
 
1536
    }
 
1537
    else if(ret == -4){
 
1538
        /* Received header */
 
1539
        if(errmsg)
 
1540
          snprintf(*errmsg, 500,
 
1541
                  _("Suspicious Received headers in first msg in \"%s\""),
 
1542
                  rd->rn);
 
1543
    }
 
1544
    else if(ret == -5){
 
1545
 
 
1546
        /* cookie is 0 */
 
1547
 
 
1548
        /*
 
1549
         * This is a failure and should not happen, but we're not going to
 
1550
         * fail on this condition.
 
1551
         */
 
1552
        dprint((1, "Unexpected value in \"%s\" header of \"%s\"\n",
 
1553
               rd->t.i.special_hdr ? rd->t.i.special_hdr : "?",
 
1554
               rd->rn ? rd->rn : "?"));
 
1555
        ret = 0;
 
1556
    }
 
1557
    else if(ret == -6){
 
1558
        dprint((1,
 
1559
                   "rd_chk_for_hdr_msg: cookie is 1, try to upgrade it\n"));
 
1560
 
 
1561
        if(rd_remote_is_readonly(rd)){
 
1562
            dprint((1,
 
1563
                   "rd_chk_for_hdr_msg:  can't upgrade, readonly\n"));
 
1564
            ret = 0;                    /* stick with 1 */
 
1565
        }
 
1566
        else{
 
1567
            /* cookie is 1, upgrade it */
 
1568
            if(rd_upgrade_cookies(rd, st->nmsgs, 0) == 0){
 
1569
                /* now check again */
 
1570
                if(!tried_again){
 
1571
                    tried_again++;
 
1572
                    goto try_again;
 
1573
                }
 
1574
            }
 
1575
 
 
1576
            /*
 
1577
             * This is actually a failure but we've decided that this
 
1578
             * failure is ok.
 
1579
             */
 
1580
            ret = 0;
 
1581
        }
 
1582
    }
 
1583
 
 
1584
    if(errmsg && *errmsg)
 
1585
      dprint((1, "rd_chk_for_hdr_msg: %s\n", *errmsg));
 
1586
 
 
1587
    return ret;
 
1588
}
 
1589
 
 
1590
 
 
1591
/*
 
1592
 * For remote data, this adds the explanatory header
 
1593
 * message to the remote IMAP folder.
 
1594
 *
 
1595
 * Args:    rd -- Remote data handle
 
1596
 *        date -- The date string to use
 
1597
 *
 
1598
 * Result: 0 success
 
1599
 *        -1 failure
 
1600
 */
 
1601
int
 
1602
rd_add_hdr_msg(REMDATA_S *rd, char *date)
 
1603
{
 
1604
    int           err = 0;
 
1605
 
 
1606
    if(!rd|| rd->type != RemImap || !rd->rn || !rd->so || !rd->t.i.special_hdr){
 
1607
        dprint((1,
 
1608
               "rd_add_hdr_msg: Unexpected error: %s is NULL\n",
 
1609
               !rd ? "rd" :
 
1610
                !rd->rn ? "remotename" :
 
1611
                 !rd->so ? "so" :
 
1612
                  !rd->t.i.special_hdr ? "special_hdr" : "?"));
 
1613
        return -1;
 
1614
    }
 
1615
 
 
1616
    err = rd_store_fake_hdrs(rd, "Header Message for Remote Data",
 
1617
                             "plain", date);
 
1618
 
 
1619
    /* Write the dummy message */
 
1620
    if(!strucmp(rd->t.i.special_hdr, REMOTE_ABOOK_SUBTYPE)){
 
1621
        if(!err && so_puts(rd->so,
 
1622
            "This folder contains a single Alpine addressbook.\015\012") == 0)
 
1623
          err = -1;
 
1624
        if(!err && so_puts(rd->so,
 
1625
            "This message is just an explanatory message.\015\012") == 0)
 
1626
          err = -1;
 
1627
        if(!err && so_puts(rd->so,
 
1628
            "The last message in the folder is the live addressbook data.\015\012") == 0)
 
1629
          err = -1;
 
1630
        if(!err && so_puts(rd->so,
 
1631
            "The rest of the messages contain previous revisions of the addressbook data.\015\012") == 0)
 
1632
          err = -1;
 
1633
        if(!err && so_puts(rd->so,
 
1634
            "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
 
1635
          err = -1;
 
1636
        if(!err && so_puts(rd->so,
 
1637
            "which come after it.\015\012") == 0)
 
1638
          err = -1;
 
1639
    }
 
1640
    else if(!strucmp(rd->t.i.special_hdr, REMOTE_PINERC_SUBTYPE)){
 
1641
        if(!err && so_puts(rd->so,
 
1642
            "This folder contains a Alpine config file.\015\012") == 0)
 
1643
          err = -1;
 
1644
        if(!err && so_puts(rd->so,
 
1645
            "This message is just an explanatory message.\015\012") == 0)
 
1646
          err = -1;
 
1647
        if(!err && so_puts(rd->so,
 
1648
            "The last message in the folder is the live config data.\015\012") == 0)
 
1649
          err = -1;
 
1650
        if(!err && so_puts(rd->so,
 
1651
            "The rest of the messages contain previous revisions of the data.\015\012") == 0)
 
1652
          err = -1;
 
1653
        if(!err && so_puts(rd->so,
 
1654
            "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
 
1655
          err = -1;
 
1656
        if(!err && so_puts(rd->so,
 
1657
            "which come after it.\015\012") == 0)
 
1658
          err = -1;
 
1659
    }
 
1660
    else{
 
1661
        if(!err && so_puts(rd->so,
 
1662
            "This folder contains remote Alpine data.\015\012") == 0)
 
1663
          err = -1;
 
1664
        if(!err && so_puts(rd->so,
 
1665
            "This message is just an explanatory message.\015\012") == 0)
 
1666
          err = -1;
 
1667
        if(!err && so_puts(rd->so,
 
1668
            "The last message in the folder is the live data.\015\012") == 0)
 
1669
          err = -1;
 
1670
        if(!err && so_puts(rd->so,
 
1671
            "The rest of the messages contain previous revisions of the data.\015\012") == 0)
 
1672
          err = -1;
 
1673
        if(!err && so_puts(rd->so,
 
1674
            "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
 
1675
          err = -1;
 
1676
        if(!err && so_puts(rd->so,
 
1677
            "which come after it.\015\012") == 0)
 
1678
          err = -1;
 
1679
    }
 
1680
 
 
1681
    /* Take the message from "so" to the remote folder */
 
1682
    if(!err){
 
1683
        MAILSTREAM *st;
 
1684
 
 
1685
        if((st = rd->t.i.stream) != NULL)
 
1686
          rd->t.i.shouldbe_nmsgs = rd->t.i.stream->nmsgs + 1;
 
1687
        else
 
1688
          st = adrbk_handy_stream(rd->rn);
 
1689
 
 
1690
        err = write_fcc(rd->rn, NULL, rd->so, st, "remote data", NULL) ? 0 : -1;
 
1691
    }
 
1692
    
 
1693
    return err;
 
1694
}
 
1695
 
 
1696
 
 
1697
/*
 
1698
 * Write some fake header lines into storage object so.
 
1699
 *
 
1700
 * Args: so    -- storage object to write into
 
1701
 *     subject -- subject to put in header
 
1702
 *     subtype -- subtype to put in header
 
1703
 *     date    -- date to put in header
 
1704
 */
 
1705
int
 
1706
rd_store_fake_hdrs(REMDATA_S *rd, char *subject, char *subtype, char *date)
 
1707
{
 
1708
    ENVELOPE     *fake_env;
 
1709
    BODY         *fake_body;
 
1710
    ADDRESS      *fake_from;
 
1711
    int           err = 0;
 
1712
    char          vers[50], *p;
 
1713
    unsigned long r = 0L;
 
1714
    RFC822BUFFER rbuf;
 
1715
 
 
1716
    if(!rd|| rd->type != RemImap || !rd->so || !rd->t.i.special_hdr)
 
1717
      return -1;
 
1718
    
 
1719
    fake_env = (ENVELOPE *)fs_get(sizeof(ENVELOPE));
 
1720
    memset(fake_env, 0, sizeof(ENVELOPE));
 
1721
    fake_body = (BODY *)fs_get(sizeof(BODY));
 
1722
    memset(fake_body, 0, sizeof(BODY));
 
1723
    fake_from = (ADDRESS *)fs_get(sizeof(ADDRESS));
 
1724
    memset(fake_from, 0, sizeof(ADDRESS));
 
1725
 
 
1726
    fake_env->subject = cpystr(subject);
 
1727
    fake_env->date = (unsigned char *) cpystr(date);
 
1728
    fake_from->personal = cpystr("Pine Remote Data");
 
1729
    fake_from->mailbox = cpystr("nobody");
 
1730
    fake_from->host = cpystr("nowhere");
 
1731
    fake_env->from = fake_from;
 
1732
    fake_body->type = REMOTE_DATA_TYPE;
 
1733
    fake_body->subtype = cpystr(subtype);
 
1734
 
 
1735
    if(rd->cookie > 0)
 
1736
      r = rd->cookie;
 
1737
 
 
1738
    if(!r){
 
1739
        int i;
 
1740
 
 
1741
        for(i = 100; i > 0 && r < 1000000; i--)
 
1742
          r = random();
 
1743
        
 
1744
        if(r < 1000000)
 
1745
          r = 1712836L + getpid();
 
1746
        
 
1747
        rd->cookie = r;
 
1748
    }
 
1749
 
 
1750
    snprintf(vers, sizeof(vers), "%ld", r);
 
1751
 
 
1752
    p = tmp_20k_buf;
 
1753
    *p = '\0';
 
1754
    rbuf.f   = dummy_soutr;
 
1755
    rbuf.s   = NULL;
 
1756
    rbuf.beg = p;
 
1757
    rbuf.cur = p;
 
1758
    rbuf.end = p+SIZEOF_20KBUF-1;
 
1759
    rfc822_output_header_line(&rbuf, rd->t.i.special_hdr, 0L, vers);
 
1760
    rfc822_output_header(&rbuf, fake_env, fake_body, NULL, 0L);
 
1761
    *rbuf.cur = '\0';
 
1762
 
 
1763
    mail_free_envelope(&fake_env);
 
1764
    mail_free_body(&fake_body);
 
1765
 
 
1766
    /* Write the fake headers */
 
1767
    if(so_puts(rd->so, tmp_20k_buf) == 0)
 
1768
      err = -1;
 
1769
    
 
1770
    return err;
 
1771
}
 
1772
 
 
1773
 
 
1774
/*
 
1775
 * We have discovered that the data in the remote folder is suspicious.
 
1776
 * In some cases it is just because it is from an old version of pine.
 
1777
 * We have decided to update the data so that it won't look suspicious
 
1778
 * next time.
 
1779
 *
 
1780
 * Args -- only_update_last  If set, that means to just add a new last message
 
1781
 *                           by calling rd_update_remote. Don't create a new
 
1782
 *                           header message and delete the old header message.
 
1783
 *         nmsgs             Not used if only_update_last is set
 
1784
 */
 
1785
int
 
1786
rd_upgrade_cookies(REMDATA_S *rd, long int nmsgs, int only_update_last)
 
1787
{
 
1788
    char date[200];
 
1789
    int  err = 0;
 
1790
 
 
1791
    /*
 
1792
     * We need to copy the data from the last message, add a new header
 
1793
     * message with a random cookie, add the data back in with the
 
1794
     * new cookie, and delete the old messages.
 
1795
     */
 
1796
 
 
1797
    /* get data */
 
1798
    rd->flags |= COOKIE_ONE_OK;
 
1799
 
 
1800
    /*
 
1801
     * The local copy may be newer than the remote copy. We don't want to
 
1802
     * blast the local copy in that case. The BELIEVE_CACHE flag tells us
 
1803
     * to not do the blasting.
 
1804
     */
 
1805
    if(rd->flags & BELIEVE_CACHE)
 
1806
      rd->flags &= ~BELIEVE_CACHE;
 
1807
    else{
 
1808
        dprint((1, "rd_upgrade_cookies:  copy abook data\n"));
 
1809
        err = rd_update_local(rd);
 
1810
    }
 
1811
 
 
1812
    if(!err && !only_update_last){
 
1813
        rd->cookie = 0;         /* causes new cookie to be generated */
 
1814
        rfc822_date(date);
 
1815
        dprint((1, "rd_upgrade_cookies:  add new hdr msg to end\n"));
 
1816
        err = rd_add_hdr_msg(rd, date);
 
1817
    }
 
1818
 
 
1819
    if(!err){
 
1820
        dprint((1, "rd_upgrade_cookies:  copy back data\n"));
 
1821
        err = rd_update_remote(rd, NULL);
 
1822
    }
 
1823
 
 
1824
    rd->flags &= ~COOKIE_ONE_OK;
 
1825
 
 
1826
    if(!err && !only_update_last){
 
1827
        char sequence[20];
 
1828
 
 
1829
        /*
 
1830
         * We've created a new header message and added a new copy of the
 
1831
         * data after it. Only problem is that the new copy will have used
 
1832
         * the original header message to get its cookie (== 1) from. We
 
1833
         * could have deleted the original messages before the last step
 
1834
         * to get it right but that would delete all copies of the data
 
1835
         * temporarily. Delete now and then re-update.
 
1836
         */
 
1837
        rd_ping_stream(rd);
 
1838
        rd_open_remote(rd);
 
1839
        if(rd->t.i.stream && rd->t.i.stream->nmsgs >= nmsgs+2){
 
1840
            snprintf(sequence, sizeof(sequence), "1:%ld", nmsgs);
 
1841
            mail_flag(rd->t.i.stream, sequence, "\\DELETED", ST_SET);
 
1842
            mail_expunge(rd->t.i.stream);
 
1843
            err = rd_update_remote(rd, NULL);
 
1844
        }
 
1845
    }
 
1846
 
 
1847
    return(err);
 
1848
}
 
1849
 
 
1850
 
 
1851
/*
 
1852
 * Copy remote data to local file. If needed, the remote data is initialized
 
1853
 * with no data. On success, the local file contains the remote data (which
 
1854
 * means it will be empty if we initialized).
 
1855
 *
 
1856
 * Returns != 0 on failure
 
1857
 *            0 on success
 
1858
 */
 
1859
int
 
1860
rd_update_local(REMDATA_S *rd)
 
1861
{
 
1862
    char     *error;
 
1863
    STORE_S  *store;
 
1864
    gf_io_t   pc;
 
1865
    int       i, we_cancel = 0;
 
1866
    BODY     *body = NULL;
 
1867
    ENVELOPE *env;
 
1868
    char     *tempfile = NULL;
 
1869
 
 
1870
 
 
1871
    if(!rd || !(rd->flags & NO_FILE || rd->lf) || !rd->rn){
 
1872
        dprint((1,
 
1873
               "rd_update_local: Unexpected error: %s is NULL\n",
 
1874
               !rd ? "rd" :
 
1875
                !(rd->flags & NO_FILE || rd->lf) ? "localfile" :
 
1876
                 !rd->rn ? "remotename" : "?"));
 
1877
 
 
1878
        return -1;
 
1879
    }
 
1880
 
 
1881
    dprint((3, " - rd_update_local(%s): %s => %s\n",
 
1882
           rd->type == RemImap ? "Imap" : "?", rd->rn ? rd->rn : "?",
 
1883
           (rd->flags & NO_FILE) ? "<mem>" : (rd->lf ? rd->lf : "?")));
 
1884
    
 
1885
    switch(rd->type){
 
1886
      case RemImap:
 
1887
        if(!rd->so || !rd->t.i.special_hdr){
 
1888
            dprint((1,
 
1889
                   "rd_update_local: Unexpected error: %s is NULL\n",
 
1890
                   !rd->so ? "so" :
 
1891
                    !rd->t.i.special_hdr ? "special_hdr" : "?"));
 
1892
            return -1;
 
1893
        }
 
1894
 
 
1895
        rd_open_remote(rd);
 
1896
        if(!rd_stream_exists(rd)){
 
1897
            if(rd->flags & NO_PERM_CACHE){
 
1898
                dprint((1,
 
1899
             "rd_update_local: backtrack, remote folder does not exist yet\n"));
 
1900
            }
 
1901
            else{
 
1902
                dprint((1,
 
1903
                       "rd_update_local: Unexpected error: stream is NULL\n"));
 
1904
            }
 
1905
 
 
1906
            return -1;
 
1907
        }
 
1908
else
 
1909
 
 
1910
        if(rd->t.i.stream){
 
1911
            char  ebuf[500];
 
1912
            char *eptr = NULL;
 
1913
            int   chk;
 
1914
 
 
1915
            /* force ReadOnly */
 
1916
            if(rd->t.i.stream->rdonly){
 
1917
                rd->read_status = 'R';
 
1918
                rd->access = ReadOnly;
 
1919
            }
 
1920
            else
 
1921
              rd->read_status = 'W';
 
1922
 
 
1923
            if(rd->t.i.stream->nmsgs < 2)
 
1924
              return(rd_init_remote(rd, 0));
 
1925
            else if(rd_chk_for_hdr_msg(&(rd->t.i.stream), rd, &eptr)){
 
1926
                q_status_message1(SM_ORDER | SM_DING, 5, 5,
 
1927
                     _("Can't initialize \"%s\" (invalid format)"), rd->rn);
 
1928
 
 
1929
                if(eptr){
 
1930
                    q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
 
1931
                    dprint((1, "%s\n", eptr));
 
1932
                    fs_give((void **)&eptr);
 
1933
                }
 
1934
 
 
1935
                dprint((1,
 
1936
                       "Can't initialize remote data \"%s\"\n",
 
1937
                       rd->rn ? rd->rn : "?"));
 
1938
                return -1;
 
1939
            }
 
1940
 
 
1941
            we_cancel = busy_cue(_("Copying remote data"), NULL, 1);
 
1942
 
 
1943
            if(rd->flags & NO_FILE){
 
1944
                store = rd->sonofile;
 
1945
                so_truncate(store, 0L);
 
1946
            }
 
1947
            else{
 
1948
                if(!(tempfile = tempfile_in_same_dir(rd->lf, "a8", NULL, 0))){
 
1949
                    q_status_message1(SM_ORDER | SM_DING, 3, 5,
 
1950
                                      _("Error opening temporary file: %s"),
 
1951
                                      error_description(errno));
 
1952
                    dprint((2,
 
1953
                          "rd_update_local: Error opening temporary file: %s\n",
 
1954
                          error_description(errno)));
 
1955
                    return -1;
 
1956
                }
 
1957
 
 
1958
                /* Copy the data into tempfile */
 
1959
                if((store = so_get(FileStar, tempfile, WRITE_ACCESS|OWNER_ONLY))
 
1960
                                                                    == NULL){
 
1961
                    q_status_message2(SM_ORDER | SM_DING, 3, 5,
 
1962
                                  _("Error opening temporary file %s: %s"),
 
1963
                                      tempfile, error_description(errno));
 
1964
                    dprint((2,
 
1965
                      "rd_update_local: Error opening temporary file: %s: %s\n",
 
1966
                      tempfile ? tempfile : "?",
 
1967
                      error_description(errno)));
 
1968
                    our_unlink(tempfile);
 
1969
                    fs_give((void **)&tempfile);
 
1970
                    return -1;
 
1971
                }
 
1972
            }
 
1973
 
 
1974
            /*
 
1975
             * Copy from the last message in the folder.
 
1976
             */
 
1977
            if(!pine_mail_fetchstructure(rd->t.i.stream, rd->t.i.stream->nmsgs,
 
1978
                                         &body)){
 
1979
                q_status_message(SM_ORDER | SM_DING, 3, 4,
 
1980
                                 _("Can't access remote IMAP data"));
 
1981
                dprint((2, "Can't access remote IMAP data\n"));
 
1982
                if(tempfile){
 
1983
                    our_unlink(tempfile);
 
1984
                    fs_give((void **)&tempfile);
 
1985
                }
 
1986
 
 
1987
                if(!(rd->flags & NO_FILE))
 
1988
                  so_give(&store);
 
1989
 
 
1990
                if(we_cancel)
 
1991
                  cancel_busy_cue(-1);
 
1992
 
 
1993
                return -1;
 
1994
            }
 
1995
 
 
1996
            if(!body ||
 
1997
               body->type != REMOTE_DATA_TYPE ||
 
1998
               !body->subtype ||
 
1999
               strucmp(body->subtype, rd->t.i.special_hdr)){
 
2000
                q_status_message(SM_ORDER | SM_DING, 3, 4,
 
2001
                                 _("Remote IMAP folder has wrong contents"));
 
2002
                dprint((2,
 
2003
                       "Remote IMAP folder has wrong contents\n"));
 
2004
                if(tempfile){
 
2005
                    our_unlink(tempfile);
 
2006
                    fs_give((void **)&tempfile);
 
2007
                }
 
2008
 
 
2009
                if(!(rd->flags & NO_FILE))
 
2010
                  so_give(&store);
 
2011
 
 
2012
                if(we_cancel)
 
2013
                  cancel_busy_cue(-1);
 
2014
 
 
2015
                return -1;
 
2016
            }
 
2017
            
 
2018
            if(!(env = pine_mail_fetchenvelope(rd->t.i.stream,
 
2019
                                               rd->t.i.stream->nmsgs))){
 
2020
                q_status_message(SM_ORDER | SM_DING, 3, 4,
 
2021
                                 _("Can't access check date in remote data"));
 
2022
                dprint((2,
 
2023
                       "Can't access check date in remote data\n"));
 
2024
                if(tempfile){
 
2025
                    our_unlink(tempfile);
 
2026
                    fs_give((void **)&tempfile);
 
2027
                }
 
2028
 
 
2029
                if(!(rd->flags & NO_FILE))
 
2030
                  so_give(&store);
 
2031
 
 
2032
                if(we_cancel)
 
2033
                  cancel_busy_cue(-1);
 
2034
 
 
2035
                return -1;
 
2036
            }
 
2037
 
 
2038
            if(rd && rd->flags & USER_SAID_YES)
 
2039
              chk = 0;
 
2040
            else
 
2041
              chk = rd_check_for_suspect_data(rd);
 
2042
 
 
2043
            switch(chk){
 
2044
              case -1:          /* suspicious data, user says abort */
 
2045
                if(tempfile){
 
2046
                    our_unlink(tempfile);
 
2047
                    fs_give((void **)&tempfile);
 
2048
                }
 
2049
 
 
2050
                if(!(rd->flags & NO_FILE))
 
2051
                  so_give(&store);
 
2052
 
 
2053
                if(we_cancel)
 
2054
                  cancel_busy_cue(-1);
 
2055
 
 
2056
                return -1;
 
2057
 
 
2058
              case 1:           /* suspicious data, user says go ahead */
 
2059
                if(rd_remote_is_readonly(rd)){
 
2060
                    dprint((1,
 
2061
                           "rd_update_local:  can't upgrade, readonly\n"));
 
2062
                }
 
2063
                else
 
2064
                  /* attempt to upgrade cookie in last message */
 
2065
                  (void)rd_upgrade_cookies(rd, 0, 1);
 
2066
 
 
2067
                break;
 
2068
 
 
2069
              case 0:           /* all is ok */
 
2070
              default:
 
2071
                break;
 
2072
            }
 
2073
 
 
2074
 
 
2075
            /* store may have been written to at this point, so we'll clear it out  */
 
2076
            so_truncate(store, 0L);
 
2077
 
 
2078
            gf_set_so_writec(&pc, store);
 
2079
 
 
2080
            error = detach(rd->t.i.stream, rd->t.i.stream->nmsgs, "1", 0L,
 
2081
                           NULL, pc, NULL, 0);
 
2082
 
 
2083
            gf_clear_so_writec(store);
 
2084
 
 
2085
 
 
2086
            if(we_cancel)
 
2087
              cancel_busy_cue(-1);
 
2088
 
 
2089
            if(!(rd->flags & NO_FILE)){
 
2090
                if(so_give(&store)){
 
2091
                    snprintf(ebuf, sizeof(ebuf), _("Error writing temp file: %s"),
 
2092
                            error_description(errno));
 
2093
                    error = ebuf;
 
2094
                }
 
2095
            }
 
2096
 
 
2097
            if(error){
 
2098
                q_status_message1(SM_ORDER | SM_DING, 3, 4,
 
2099
                              _("%s: Error copying remote IMAP data"), error);
 
2100
                dprint((2, "rd_update_local: Error copying: %s\n",
 
2101
                       error ? error : "?"));
 
2102
                if(tempfile){
 
2103
                    our_unlink(tempfile);
 
2104
                    fs_give((void **)&tempfile);
 
2105
                }
 
2106
 
 
2107
                return -1;
 
2108
            }
 
2109
 
 
2110
            if(tempfile && (i = rename_file(tempfile, rd->lf)) < 0){
 
2111
#ifdef  _WINDOWS
 
2112
                if(i == -5){
 
2113
                    q_status_message2(SM_ORDER | SM_DING, 3, 4,
 
2114
                                    _("Error updating local file: %s: %s"),
 
2115
                                      rd->lf, error_description(errno));
 
2116
                    q_status_message(SM_ORDER, 3, 4,
 
2117
                                 _("Perhaps another process has the file open?"));
 
2118
                    dprint((2,
 
2119
        "Rename_file(%s,%s): %s: returned -5, another process has file open?\n",
 
2120
                           tempfile ? tempfile : "?",
 
2121
                           rd->lf ? rd->lf : "?",
 
2122
                           error_description(errno)));
 
2123
                }
 
2124
                else
 
2125
#endif  /* _WINDOWS */
 
2126
                {
 
2127
                q_status_message2(SM_ORDER | SM_DING, 3, 5,
 
2128
                                  _("Error updating cache file %s: %s"),
 
2129
                                  rd->lf, error_description(errno));
 
2130
                dprint((2,
 
2131
                       "Error updating cache file %s: rename(%s,%s): %s\n",
 
2132
                       tempfile ? tempfile : "?",
 
2133
                       tempfile ? tempfile : "?",
 
2134
                       rd->lf ? rd->lf : "?",
 
2135
                       error_description(errno)));
 
2136
                }
 
2137
 
 
2138
                our_unlink(tempfile);
 
2139
                fs_give((void **)&tempfile);
 
2140
                return -1;
 
2141
            }
 
2142
 
 
2143
            dprint((5,
 
2144
                   "in rd_update_local, setting chk_date to ->%s<-\n",
 
2145
                   env->date ? (char *)env->date : "?"));
 
2146
            rd_update_metadata(rd, (char *) env->date);
 
2147
 
 
2148
            /* turn off out of date flag */
 
2149
            rd->flags &= ~REM_OUTOFDATE;
 
2150
 
 
2151
            if(tempfile)
 
2152
              fs_give((void **)&tempfile);
 
2153
 
 
2154
            return 0;
 
2155
        }
 
2156
        else{
 
2157
            q_status_message1(SM_ORDER | SM_DING, 5, 5,
 
2158
                 _("Can't open remote IMAP folder \"%s\""), rd->rn);
 
2159
            dprint((1,
 
2160
                   "Can't open remote IMAP folder \"%s\"\n",
 
2161
                   rd->rn ? rd->rn : "?"));
 
2162
            rd->access = ReadOnly;
 
2163
            return -1;
 
2164
        }
 
2165
 
 
2166
        break;
 
2167
 
 
2168
      default:
 
2169
        dprint((1, "rd_update_local: Unsupported type\n"));
 
2170
        return -1;
 
2171
    }
 
2172
}
 
2173
 
 
2174
 
 
2175
/*
 
2176
 * Copy local data to remote folder.
 
2177
 *
 
2178
 * Args         lf -- Local file name
 
2179
 *              rn -- Remote name
 
2180
 * header_to_check -- Name of indicator header in remote folder
 
2181
 *       imapstuff -- Structure holding info about connection
 
2182
 *      returndate -- Pointer to the date that was stored in the remote folder
 
2183
 *
 
2184
 * Returns !=0 on failure
 
2185
 *          0 on success
 
2186
 */
 
2187
int
 
2188
rd_update_remote(REMDATA_S *rd, char *returndate)
 
2189
{
 
2190
    STORE_S    *store;
 
2191
    int         err = 0;
 
2192
    long        openmode = SP_USEPOOL | SP_TEMPUSE;
 
2193
    MAILSTREAM *st;
 
2194
    char       *eptr = NULL;
 
2195
 
 
2196
    if(rd && rd->type != RemImap){
 
2197
        dprint((1, "rd_update_remote: type not supported\n"));
 
2198
        return -1;
 
2199
    }
 
2200
 
 
2201
    if(!rd || !(rd->flags & NO_FILE || rd->lf) || !rd->rn ||
 
2202
       !rd->so || !rd->t.i.special_hdr){
 
2203
        dprint((1,
 
2204
               "rd_update_remote: Unexpected error: %s is NULL\n",
 
2205
               !rd ? "rd" :
 
2206
                !(rd->flags & NO_FILE || rd->lf) ? "localfile" :
 
2207
                 !rd->rn ? "remotename" :
 
2208
                  !rd->so ? "so" :
 
2209
                    !rd->t.i.special_hdr ? "special_hdr" : "?"));
 
2210
        return -1;
 
2211
    }
 
2212
 
 
2213
    dprint((7, " - rd_update_remote(%s): %s => %s\n",
 
2214
           rd->t.i.special_hdr ? rd->t.i.special_hdr : "?",
 
2215
           rd->lf ? rd->lf : "<mem>",
 
2216
           rd->rn ? rd->rn : "?"));
 
2217
 
 
2218
    if(!(st = rd->t.i.stream))
 
2219
      st = adrbk_handy_stream(rd->rn);
 
2220
 
 
2221
    if(!st)
 
2222
      st = rd->t.i.stream = context_open(NULL, NULL, rd->rn, openmode, NULL);
 
2223
 
 
2224
    if(!st){
 
2225
        q_status_message1(SM_ORDER | SM_DING, 5, 5,
 
2226
             _("Can't open \"%s\" for copying"), rd->rn);
 
2227
        dprint((1,
 
2228
          "rd_update_remote: Can't open remote folder \"%s\" for copying\n",
 
2229
               rd->rn ? rd->rn : "?"));
 
2230
        return 1;
 
2231
    }
 
2232
 
 
2233
    rd->last_use = get_adj_time();
 
2234
    err = rd_chk_for_hdr_msg(&st, rd, &eptr);
 
2235
    if(err){
 
2236
        q_status_message1(SM_ORDER | SM_DING, 5, 5,
 
2237
                          _("\"%s\" has invalid format"), rd->rn);
 
2238
 
 
2239
        if(eptr){
 
2240
            q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
 
2241
            dprint((1, "%s\n", eptr));
 
2242
            fs_give((void **)&eptr);
 
2243
        }
 
2244
 
 
2245
        dprint((1,
 
2246
           "rd_update_remote: \"%s\" has invalid format\n",
 
2247
           rd->rn ? rd->rn : "?"));
 
2248
        return 1;
 
2249
    }
 
2250
 
 
2251
    errno = 0;
 
2252
 
 
2253
    /*
 
2254
     * The data that will be going to the remote folder rn is
 
2255
     * written into the following storage object and then copied to
 
2256
     * the remote folder from there.
 
2257
     */
 
2258
 
 
2259
    if(rd->flags & NO_FILE){
 
2260
        store = rd->sonofile;
 
2261
        if(store)
 
2262
          so_seek(store, 0L, 0);        /* rewind */
 
2263
    }
 
2264
    else
 
2265
      store = so_get(FileStar, rd->lf, READ_ACCESS);
 
2266
 
 
2267
    if(store != NULL){
 
2268
        char date[200];
 
2269
        unsigned char c;
 
2270
        unsigned char last_c = 0;
 
2271
 
 
2272
        /* Reset the storage object, since we may have already used it. */
 
2273
        if(so_truncate(rd->so, 0L) == 0)
 
2274
          err = 1;
 
2275
 
 
2276
        rfc822_date(date);
 
2277
        dprint((7,
 
2278
               "in rd_update_remote, storing date ->%s<-\n",
 
2279
               date ? date : "?"));
 
2280
        if(!err && rd_store_fake_hdrs(rd, "Pine Remote Data Container",
 
2281
                                      rd->t.i.special_hdr, date))
 
2282
          err = 1;
 
2283
        
 
2284
        /* save the date for later comparisons */
 
2285
        if(!err && returndate)
 
2286
          strncpy(returndate, date, 100);
 
2287
 
 
2288
        /* Write the data */
 
2289
        while(!err && so_readc(&c, store)){
 
2290
            /*
 
2291
             * C-client expects CRLF-terminated lines. Convert them
 
2292
             * as we copy into c-client. Read ahead isn't available.
 
2293
             * Leave CRLF as is, convert LF to CRLF, leave CR as is.
 
2294
             */
 
2295
            if(last_c != '\r' && c == '\n'){
 
2296
                /* Convert \n to CRFL */
 
2297
                if(so_writec('\r', rd->so) == 0 || so_writec('\n', rd->so) == 0)
 
2298
                  err = 1;
 
2299
                
 
2300
                last_c = 0;
 
2301
            }
 
2302
            else{
 
2303
                last_c = c;
 
2304
                if(so_writec((int) c, rd->so) == 0)
 
2305
                  err = 1;
 
2306
            }
 
2307
        }
 
2308
 
 
2309
        /*
 
2310
         * Take that message from so to the remote folder.
 
2311
         * We append to that folder and always
 
2312
         * use the final message as the active data.
 
2313
         */
 
2314
        if(!err){
 
2315
            MAILSTREAM *st;
 
2316
 
 
2317
            if((st = rd->t.i.stream) != NULL)
 
2318
              rd->t.i.shouldbe_nmsgs = rd->t.i.stream->nmsgs + 1;
 
2319
            else
 
2320
              st = adrbk_handy_stream(rd->rn);
 
2321
 
 
2322
            err = write_fcc(rd->rn, NULL, rd->so, st,
 
2323
                            "remote data", NULL) ? 0 : 1;
 
2324
        }
 
2325
 
 
2326
 
 
2327
        if(!(rd->flags & NO_FILE))
 
2328
          so_give(&store);
 
2329
    }
 
2330
    else
 
2331
      err = -1;
 
2332
 
 
2333
    if(err)
 
2334
        dprint((2, "error in rd_update_remote for %s => %s\n",
 
2335
               rd->lf ? rd->lf : "<mem>", rd->rn ? rd->rn : "?"));
 
2336
 
 
2337
    return(err);
 
2338
}
 
2339
 
 
2340
 
 
2341
/*
 
2342
 * Check to see if the remote data has changed since we cached it.
 
2343
 *
 
2344
 * Args    rd -- REMDATA handle
 
2345
 *  do_it_now -- If > 0, check now regardless
 
2346
 *               If = 0, check if time since last chk more than default
 
2347
 *               If < 0, check if time since last chk more than -do_it_now
 
2348
 */
 
2349
void
 
2350
rd_check_remvalid(REMDATA_S *rd, long int do_it_now)
 
2351
{
 
2352
    time_t      chk_interval;
 
2353
    long        openmode = SP_USEPOOL | SP_TEMPUSE;
 
2354
    MAILSTREAM *stat_stream = NULL;
 
2355
    int         we_cancel = 0, got_cmsgs = 0;
 
2356
    unsigned long current_nmsgs;
 
2357
 
 
2358
    dprint((7, "- rd_check_remvalid(%s) -\n",
 
2359
           (rd && rd->rn) ? rd->rn : ""));
 
2360
 
 
2361
    if(rd && rd->type != RemImap){
 
2362
        dprint((1, "rd_check_remvalid: type not supported\n"));
 
2363
        return;
 
2364
    }
 
2365
 
 
2366
    if(!rd || rd->flags & REM_OUTOFDATE || rd->flags & USE_OLD_CACHE)
 
2367
      return;
 
2368
 
 
2369
    if(!rd->t.i.chk_date){
 
2370
        dprint((2,
 
2371
               "rd_check_remvalid: remote data %s changed (chk_date)\n",
 
2372
               rd->rn));
 
2373
        rd->flags |= REM_OUTOFDATE;
 
2374
        return;
 
2375
    }
 
2376
 
 
2377
    if(rd->t.i.chk_nmsgs <= 1){
 
2378
        dprint((2,
 
2379
          "rd_check_remvalid: remote data %s changed (chk_nmsgs <= 1)\n",
 
2380
               rd->rn ? rd->rn : "?"));
 
2381
        rd->flags |= REM_OUTOFDATE;
 
2382
        return;
 
2383
    }
 
2384
 
 
2385
    if(do_it_now < 0L){
 
2386
        chk_interval = -1L * do_it_now;
 
2387
        do_it_now = 0L;
 
2388
    }
 
2389
    else
 
2390
      chk_interval = ps_global->remote_abook_validity * 60L;
 
2391
 
 
2392
    /* too soon to check again */
 
2393
    if(!do_it_now &&
 
2394
       (chk_interval == 0L ||
 
2395
        get_adj_time() <= rd->last_valid_chk + chk_interval))
 
2396
      return;
 
2397
    
 
2398
    if(rd->access == ReadOnly)
 
2399
      openmode |= OP_READONLY;
 
2400
 
 
2401
    rd->last_valid_chk = get_adj_time();
 
2402
    mm_status_result.flags  = 0L;
 
2403
 
 
2404
    /* make sure the cache file is still there */
 
2405
    if(rd->lf && can_access(rd->lf, READ_ACCESS) != 0){
 
2406
        dprint((2,
 
2407
               "rd_check_remvalid: %s: cache file %s disappeared\n",
 
2408
               rd->rn ? rd->rn : "?",
 
2409
               rd->lf ? rd->lf : "?"));
 
2410
        rd->flags |= REM_OUTOFDATE;
 
2411
        return;
 
2412
    }
 
2413
 
 
2414
    /*
 
2415
     * If the stream is open we should check there instead of using
 
2416
     * a STATUS command. Check to see if it is really still alive with the
 
2417
     * ping. It would be convenient if we could use a status command
 
2418
     * on the open stream but apparently that won't work everywhere.
 
2419
     */
 
2420
    rd_ping_stream(rd);
 
2421
 
 
2422
try_looking_in_stream:
 
2423
 
 
2424
    /*
 
2425
     * Get the current number of messages in the folder to
 
2426
     * compare with our saved number of messages.
 
2427
     */
 
2428
    current_nmsgs = 0;
 
2429
    if(rd->t.i.stream){
 
2430
        dprint((7, "using open remote data stream\n"));
 
2431
        rd->last_use = get_adj_time();
 
2432
        current_nmsgs = rd->t.i.stream->nmsgs;
 
2433
        got_cmsgs++;
 
2434
    }
 
2435
    else{
 
2436
 
 
2437
        /*
 
2438
         * Try to use the imap status command
 
2439
         * to get the information as cheaply as possible.
 
2440
         * If NO_STATUSCMD is set we just bypass all this stuff.
 
2441
         */
 
2442
 
 
2443
        if(!(rd->flags & NO_STATUSCMD))
 
2444
          stat_stream = adrbk_handy_stream(rd->rn);
 
2445
 
 
2446
        /*
 
2447
         * If we don't have a stream to use for the status command we
 
2448
         * skip it and open the folder instead. Then, next time we want to
 
2449
         * check we'll probably be able to use that open stream instead of
 
2450
         * opening another one each time for the status command.
 
2451
         */
 
2452
        if(stat_stream){
 
2453
            if(!LEVELSTATUS(stat_stream)){
 
2454
                rd->flags |= NO_STATUSCMD;
 
2455
                dprint((2,
 
2456
   "rd_check_remvalid: remote data %s: server doesn't support status\n",
 
2457
                       rd->rn ? rd->rn : "?"));
 
2458
            }
 
2459
            else{
 
2460
                /*
 
2461
                 * This sure seems like a crock. We have to check to
 
2462
                 * see if the stream is actually open to the folder
 
2463
                 * we want to do the status on because c-client can't
 
2464
                 * do a status on an open folder. In this case, we fake
 
2465
                 * the status command results ourselves.
 
2466
                 * If we're so unlucky as to get back a stream that will
 
2467
                 * work for the status command while we also have another
 
2468
                 * stream that is rd->rn and we don't pick up on that,
 
2469
                 * too bad.
 
2470
                 */
 
2471
                if(same_stream_and_mailbox(rd->rn, stat_stream)){
 
2472
                    dprint((7,
 
2473
                           "rd_check_remvalid: faking status\n"));
 
2474
                    mm_status_result.flags = SA_MESSAGES | SA_UIDVALIDITY
 
2475
                                             | SA_UIDNEXT;
 
2476
                    mm_status_result.messages = stat_stream->nmsgs;
 
2477
                    mm_status_result.uidvalidity = stat_stream->uid_validity;
 
2478
                    mm_status_result.uidnext = stat_stream->uid_last+1;
 
2479
                }
 
2480
                else{
 
2481
 
 
2482
                    dprint((7,
 
2483
                           "rd_check_remvalid: trying status\n"));
 
2484
                    ps_global->noshow_error = 1;
 
2485
                    if(!pine_mail_status(stat_stream, rd->rn,
 
2486
                                    SA_UIDVALIDITY | SA_UIDNEXT | SA_MESSAGES)){
 
2487
                        /* failed, mark it so we won't try again */
 
2488
                        rd->flags |= NO_STATUSCMD;
 
2489
                        dprint((2,
 
2490
                   "rd_check_remvalid: addrbook %s: status command failed\n",
 
2491
                   rd->rn ? rd->rn : "?"));
 
2492
                        mm_status_result.flags = 0L;
 
2493
                    }
 
2494
                }
 
2495
 
 
2496
                ps_global->noshow_error = 0;
 
2497
            }
 
2498
        }
 
2499
 
 
2500
        /* if we got back a result from the status command, use it */
 
2501
        if(mm_status_result.flags){
 
2502
            dprint((7,
 
2503
                   "rd_check_remvalid: got status_result 0x%x\n",
 
2504
                   mm_status_result.flags));
 
2505
            if(mm_status_result.flags & SA_MESSAGES){
 
2506
                current_nmsgs = mm_status_result.messages;
 
2507
                got_cmsgs++;
 
2508
            }
 
2509
        }
 
2510
    }
 
2511
 
 
2512
    /*
 
2513
     * Check current_nmsgs versus what we think it should be.
 
2514
     * If they're different we know things have changed and we can
 
2515
     * return now. If they're the same we don't know.
 
2516
     */
 
2517
    if(got_cmsgs && current_nmsgs != rd->t.i.chk_nmsgs){
 
2518
        rd->flags |= REM_OUTOFDATE;
 
2519
        dprint((2,
 
2520
               "rd_check_remvalid: remote data %s changed (current msgs (%ld) != chk_nmsgs (%ld))\n",
 
2521
               rd->rn ? rd->rn : "?", current_nmsgs, rd->t.i.chk_nmsgs));
 
2522
        return;
 
2523
    }
 
2524
    
 
2525
    /*
 
2526
     * Get the current uidvalidity and uidnext values from the
 
2527
     * folder to compare with our saved values.
 
2528
     */
 
2529
    if(rd->t.i.stream){
 
2530
        if(rd->t.i.stream->uid_validity == rd->t.i.uidvalidity){
 
2531
            /*
 
2532
             * Uid is valid so we just have to check whether or not the
 
2533
             * uid of the last message has changed or not and return.
 
2534
             */
 
2535
            if(mail_uid(rd->t.i.stream, rd->t.i.stream->nmsgs) != rd->t.i.uid){
 
2536
                /* uid has changed so we're out of date */
 
2537
                rd->flags |= REM_OUTOFDATE;
 
2538
                dprint((2,
 
2539
                     "rd_check_remvalid: remote data %s changed (uid)\n",
 
2540
                     rd->rn ? rd->rn : "?"));
 
2541
            }
 
2542
            else{
 
2543
                dprint((7,"rd_check_remvalid: uids match\n"));
 
2544
            }
 
2545
 
 
2546
            return;
 
2547
        }
 
2548
        else{
 
2549
            /*
 
2550
             * If the uidvalidity changed that probably means it can't
 
2551
             * be relied on to be meaningful, so don't use it in the future.
 
2552
             */
 
2553
            rd->flags |= NO_STATUSCMD;
 
2554
            dprint((7,
 
2555
       "rd_check_remvalid: remote data %s uidvalidity changed, don't use uid\n",
 
2556
                   rd->rn ? rd->rn : "?"));
 
2557
        }
 
2558
    }
 
2559
    else{                       /* stream not open, use status results */
 
2560
        if(mm_status_result.flags & SA_UIDVALIDITY &&
 
2561
           mm_status_result.flags & SA_UIDNEXT &&
 
2562
           mm_status_result.uidvalidity == rd->t.i.uidvalidity){
 
2563
 
 
2564
            /*
 
2565
             * Uids are valid.
 
2566
             */
 
2567
 
 
2568
            if(mm_status_result.uidnext == rd->t.i.uidnext){
 
2569
                /*
 
2570
                 * Uidnext valid and unchanged, so the folder is unchanged.
 
2571
                 */
 
2572
                dprint((7, "rd_check_remvalid: uidnexts match\n"));
 
2573
                return;
 
2574
            }
 
2575
            else{       /* uidnext changed, folder _may_ have changed */
 
2576
 
 
2577
                dprint((7,
 
2578
   "rd_check_remvalid: uidnexts don't match, ours=%lu status=%lu\n",
 
2579
                   rd->t.i.uidnext, mm_status_result.uidnext));
 
2580
 
 
2581
                /*
 
2582
                 * Since c-client can't handle a status cmd on the selected
 
2583
                 * mailbox, we may have had to guess at the value of uidnext,
 
2584
                 * and we don't know for sure that this is a real change.
 
2585
                 * We need to open the mailbox and find out for sure.
 
2586
                 */
 
2587
                we_cancel = busy_cue(NULL, NULL, 1);
 
2588
                ps_global->noshow_error = 1;
 
2589
                if(rd->t.i.stream = context_open(NULL, NULL, rd->rn, openmode,
 
2590
                                                 NULL)){
 
2591
                    imapuid_t last_msg_uid;
 
2592
 
 
2593
                    if(rd->t.i.stream->rdonly)
 
2594
                      rd->read_status = 'R';
 
2595
 
 
2596
                    last_msg_uid = mail_uid(rd->t.i.stream,
 
2597
                                            rd->t.i.stream->nmsgs);
 
2598
                    ps_global->noshow_error = 0;
 
2599
                    rd->last_use = get_adj_time();
 
2600
                    dprint((7,
 
2601
                           "%s: opened to check uid (%ld)\n",
 
2602
                           rd->rn ? rd->rn : "?", (long)rd->last_use));
 
2603
                    if(last_msg_uid != rd->t.i.uid){    /* really did change */
 
2604
                        rd->flags |= REM_OUTOFDATE;
 
2605
                        dprint((2,
 
2606
 "rd_check_remvalid: remote data %s changed, our uid=%lu real uid=%lu\n",
 
2607
                               rd->rn ? rd->rn : "?",
 
2608
                               rd->t.i.uid, last_msg_uid));
 
2609
                    }
 
2610
                    else{                               /* false hit */
 
2611
                        /*
 
2612
                         * The uid of the last message is the same as we
 
2613
                         * remembered, so the problem is that our guess
 
2614
                         * for the nextuid was wrong. It didn't actually
 
2615
                         * change. Since we know the correct uidnext now we
 
2616
                         * can reset that guess to the correct value for
 
2617
                         * next time, avoiding this extra mail_open.
 
2618
                         */
 
2619
                        dprint((2,
 
2620
                               "rd_check_remvalid: remote data %s false change: adjusting uidnext from %lu to %lu\n",
 
2621
                               rd->rn ? rd->rn : "?",
 
2622
                               rd->t.i.uidnext,
 
2623
                               mm_status_result.uidnext));
 
2624
                        rd->t.i.uidnext = mm_status_result.uidnext;
 
2625
                        rd_write_metadata(rd, 0);
 
2626
                    }
 
2627
 
 
2628
                    if(we_cancel)
 
2629
                      cancel_busy_cue(-1);
 
2630
 
 
2631
                    return;
 
2632
                }
 
2633
                else{
 
2634
                    ps_global->noshow_error = 0;
 
2635
                    dprint((2,
 
2636
                           "rd_check_remvalid: couldn't open %s\n",
 
2637
                           rd->rn ? rd->rn : "?"));
 
2638
                }
 
2639
            }
 
2640
        }
 
2641
        else{
 
2642
            /*
 
2643
             * If the status command doesn't return these or
 
2644
             * if the uidvalidity is changing that probably means it can't
 
2645
             * be relied on to be meaningful, so don't use it.
 
2646
             *
 
2647
             * We also come here if we don't have a stat_stream handy to
 
2648
             * look up the status. This happens, for example, if our
 
2649
             * address book is on a different server from the open mail
 
2650
             * folders. In that case, instead of opening a stream,
 
2651
             * doing a status command, and closing the stream, we open
 
2652
             * the stream and use it to check next time, too.
 
2653
             */
 
2654
            if(stat_stream){
 
2655
                rd->flags |= NO_STATUSCMD;
 
2656
                dprint((7,
 
2657
                  "rd_check_remvalid: remote data %s don't use status\n",
 
2658
                   rd->rn ? rd->rn : "?"));
 
2659
            }
 
2660
 
 
2661
            dprint((7,
 
2662
                   "opening remote data stream for validity check\n"));
 
2663
            we_cancel = busy_cue(NULL, NULL, 1);
 
2664
            ps_global->noshow_error = 1;
 
2665
            rd->t.i.stream = context_open(NULL, NULL, rd->rn, openmode,
 
2666
                                          NULL);
 
2667
            ps_global->noshow_error = 0;
 
2668
            if(we_cancel)
 
2669
              cancel_busy_cue(-1);
 
2670
 
 
2671
            we_cancel = 0;
 
2672
            if(rd->t.i.stream)
 
2673
              goto try_looking_in_stream;
 
2674
            else{
 
2675
                dprint((7,
 
2676
                  "rd_check_remvalid: cannot open remote mailbox\n"));
 
2677
                return;
 
2678
            }
 
2679
        }
 
2680
    }
 
2681
 
 
2682
    /*
 
2683
     * If we got here that means that we still don't know if the folder
 
2684
     * changed or not. If the stream is open then it must be the case that
 
2685
     * uidvalidity changed so we can't rely on using uids.  If the stream
 
2686
     * isn't open, then either the status command didn't work or the
 
2687
     * uidvalidity changed. In any case, we need to fall back to looking
 
2688
     * inside the folder at the last message and checking whether or not the
 
2689
     * Date of the last message is the one we remembered.
 
2690
     */
 
2691
 
 
2692
    dprint((7,
 
2693
           "rd_check_remvalid: falling back to Date check\n"));
 
2694
 
 
2695
    /*
 
2696
     * Fall back to looking in the folder at the Date header.
 
2697
     */
 
2698
 
 
2699
    if(!rd->t.i.stream)
 
2700
      we_cancel = busy_cue(NULL, NULL, 1);
 
2701
 
 
2702
    ps_global->noshow_error = 1;
 
2703
    if(rd->t.i.stream ||
 
2704
       (rd->t.i.stream = context_open(NULL,NULL,rd->rn,openmode,NULL))){
 
2705
        ENVELOPE *env = NULL;
 
2706
 
 
2707
        if(rd->t.i.stream->rdonly)
 
2708
          rd->read_status = 'R';
 
2709
 
 
2710
        if(rd->t.i.stream->nmsgs != rd->t.i.chk_nmsgs){
 
2711
            rd->flags |= REM_OUTOFDATE;
 
2712
            dprint((2,
 
2713
   "rd_check_remvalid: remote data %s changed (expected nmsgs %ld, got %ld)\n",
 
2714
                   rd->rn ? rd->rn : "?",
 
2715
                   rd->t.i.chk_nmsgs, rd->t.i.stream->nmsgs));
 
2716
        }
 
2717
        else if(rd->t.i.stream->nmsgs > 1){
 
2718
            env = pine_mail_fetchenvelope(rd->t.i.stream,rd->t.i.stream->nmsgs);
 
2719
        
 
2720
            if(!env || (env->date && strucmp((char *) env->date, rd->t.i.chk_date))){
 
2721
                rd->flags |= REM_OUTOFDATE;
 
2722
                dprint((2,
 
2723
                      "rd_check_remvalid: remote data %s changed (%s)\n",
 
2724
                      rd->rn ? rd->rn : "?", env ? "date" : "not enough msgs"));
 
2725
            }
 
2726
        }
 
2727
 
 
2728
        rd->last_use = get_adj_time();
 
2729
        if(env)
 
2730
          dprint((7,
 
2731
                 "%s: got envelope to check date (%ld)\n",
 
2732
                 rd->rn ? rd->rn : "?", (long)rd->last_use));
 
2733
    }
 
2734
    /* else, we give up and go with the cache copy */
 
2735
 
 
2736
    ps_global->noshow_error = 0;
 
2737
 
 
2738
    if(we_cancel)
 
2739
      cancel_busy_cue(-1);
 
2740
 
 
2741
    dprint((7, "rd_check_remvalid: falling off end\n"));
 
2742
}
 
2743
 
 
2744
 
 
2745
/*
 
2746
 * Returns nonzero if remote data is currently readonly.
 
2747
 *
 
2748
 * Returns 0 -- apparently writeable
 
2749
 *         1 -- stream not open
 
2750
 *         2 -- stream open but not writeable
 
2751
 */
 
2752
int
 
2753
rd_remote_is_readonly(REMDATA_S *rd)
 
2754
{
 
2755
    if(!rd || rd->access == ReadOnly || !rd_stream_exists(rd))
 
2756
      return(1);
 
2757
    
 
2758
    switch(rd->type){
 
2759
      case RemImap:
 
2760
        if(rd->t.i.stream->rdonly)
 
2761
          return(2);
 
2762
        
 
2763
        break;
 
2764
 
 
2765
      default:
 
2766
        q_status_message(SM_ORDER, 3, 5,
 
2767
                         "rd_remote_is_readonly: type not supported");
 
2768
        break;
 
2769
    }
 
2770
 
 
2771
    return(0);
 
2772
}
 
2773
 
 
2774
 
 
2775
/*
 
2776
 * Returns  0 if ok
 
2777
 *         -1 if not ok and user says No
 
2778
 *          1 if not ok but user says Yes, ok to use it
 
2779
 */
 
2780
int
 
2781
rd_check_for_suspect_data(REMDATA_S *rd)
 
2782
{
 
2783
    int           ans = -1;
 
2784
    char         *fields[3], *values[3], *h;
 
2785
    unsigned long cookie;
 
2786
 
 
2787
    if(!rd || rd->type != RemImap || !rd->so || !rd->rn || !rd->t.i.special_hdr)
 
2788
      return -1;
 
2789
 
 
2790
    fields[0] = rd->t.i.special_hdr;
 
2791
    fields[1] = "received";
 
2792
    fields[2] = NULL;
 
2793
    cookie = 0L;
 
2794
    if(h=pine_fetchheader_lines(rd->t.i.stream, rd->t.i.stream->nmsgs,
 
2795
                                NULL, fields)){
 
2796
        simple_header_parse(h, fields, values);
 
2797
        if(values[1])                           /* Received lines present! */
 
2798
          ans = rd_prompt_about_forged_remote_data(-1, rd, NULL);
 
2799
        else if(values[0]){
 
2800
            cookie = strtoul(values[0], (char **)NULL, 10);
 
2801
            if(cookie == rd->cookie)            /* all's well */
 
2802
              ans = 0;
 
2803
            else
 
2804
              ans = rd_prompt_about_forged_remote_data(cookie > 1L
 
2805
                                                          ? 100 : cookie,
 
2806
                                                       rd, values[0]);
 
2807
        }
 
2808
        else
 
2809
          ans = rd_prompt_about_forged_remote_data(-2, rd, NULL);
 
2810
 
 
2811
        if(values[0])
 
2812
          fs_give((void **)&values[0]);
 
2813
 
 
2814
        if(values[1])
 
2815
          fs_give((void **)&values[1]);
 
2816
 
 
2817
        fs_give((void **)&h);
 
2818
    }
 
2819
    else                                        /* missing magic header */
 
2820
      ans = rd_prompt_about_forged_remote_data(-2, rd, NULL);
 
2821
 
 
2822
    return ans;
 
2823
}