1
#if !defined(lint) && !defined(DOS)
2
static char rcsid[] = "$Id: remote.c 394 2007-01-25 20:29:45Z hubert@u.washington.edu $";
6
* ========================================================================
7
* Copyright 2006-2007 University of Washington
9
* Licensed under the Apache License, Version 2.0 (the "License");
10
* you may not use this file except in compliance with the License.
11
* You may obtain a copy of the License at
13
* http://www.apache.org/licenses/LICENSE-2.0
15
* ========================================================================
18
/*======================================================================
20
Implements remote IMAP config files (remote config, remote abook).
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"
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 *);
53
char meta_prefix[] = ".ab";
56
char *(*pith_opt_rd_metadata_name)(void);
60
read_remote_pinerc(PINERC_S *prc, ParsePinerc which_vars)
62
int try_cache, no_perm_create_pass = 0;
67
dprint((7, "read_remote_pinerc \"%s\"\n",
68
prc->name ? prc->name : "?"));
71
* We don't cache the pinerc, we always copy it.
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.
77
flags = (NO_PERM_CACHE | NO_FILE);
79
create_the_remote_folder:
81
if(no_perm_create_pass){
83
prc->rd->flags &= ~DO_REMTRIM;
84
rd_close_remdata(&prc->rd);
87
/* this will cause the remote folder to be created */
92
* We could parse the name here to find what type it is. So far we
93
* only have type RemImap.
95
prc->rd = rd_create_remote(RemImap, prc->name,
96
(void *)REMOTE_PINERC_SUBTYPE,
98
_(" Can't fetch remote configuration."));
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.
108
if(no_perm_create_pass)
109
prc->rd->flags |= NO_PERM_CACHE;
111
try_cache = rd_read_metadata(prc->rd);
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';
120
prc->rd->access = ReadWrite;
123
if(prc->rd->access != NoExists){
125
rd_check_remvalid(prc->rd, 1L);
128
* If the cached info says it is readonly but
129
* it looks like it's been fixed now, change it to readwrite.
131
if((which_vars == ParsePers || which_vars == ParsePersPost) &&
132
prc->rd->read_status == 'R'){
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.
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;
144
prc->rd->access = ReadOnly;
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)){
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.
157
no_perm_create_pass++;
158
goto create_the_remote_folder;
162
"read_pinerc_remote: rd_update_local failed\n"));
164
* Don't give up altogether. We still may be
165
* able to use a cached copy.
170
"%s: copied remote to local (%ld)\n",
171
prc->rd->rn ? prc->rd->rn : "?",
172
(long)prc->rd->last_use));
176
if(prc->rd->access == ReadWrite)
177
prc->rd->flags |= DO_REMTRIM;
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){
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");
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 : "?"));
193
prc->rd->flags &= ~DO_REMTRIM;
198
if(prc->rd->flags & NO_FILE)
199
/* copy text, leave sonofile for later use */
200
file = cpystr((char *)so_text(prc->rd->sonofile));
202
file = read_file(prc->rd->lf, 0);
205
if((which_vars == ParsePers || which_vars == ParsePersPost) &&
206
(!file || !prc->rd || prc->rd->access != ReadWrite)){
208
if(prc == ps_global->prc)
209
ps_global->readonly_pinerc = 1;
217
* Check if the remote data folder exists and create an empty folder
218
* if it doesn't exist.
220
* Args - type -- The type of remote storage.
222
* type_spec -- Type-specific data.
224
* err_prefix -- Should usually end with a SPACE
225
* err_suffix -- Should usually begin with a SPACE
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.
231
rd_create_remote(RemType type, char *remote_name, void *type_spec,
232
unsigned int *flags, char *err_prefix, char *err_suffix)
234
REMDATA_S *rd = NULL;
235
CONTEXT_S *dummy_cntxt = NULL;
237
dprint((7, "rd_create_remote \"%s\"\n",
238
remote_name ? remote_name : "?"));
240
rd = rd_new_remdata(type, remote_name, type_spec);
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);
251
rd->flags &= ~NO_FILE;
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.
260
rd->flags |= REM_OUTOFDATE;
261
rd->access = MaybeRorW;
267
* Open_fcc creates the folder if it didn't already exist.
269
if(rd->rn && (rd->so = open_fcc(rd->rn, &dummy_cntxt, 1,
270
err_prefix, err_suffix)) != NULL){
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.
276
rd->access = MaybeRorW;
283
q_status_message(SM_ORDER, 3,5, "rd_create_remote: type not supported");
292
rd_new_remdata(RemType type, char *remote_name, void *type_spec)
294
REMDATA_S *rd = NULL;
296
rd = (REMDATA_S *)fs_get(sizeof(*rd));
297
memset((void *)rd, 0, sizeof(*rd));
300
rd->access = NoExists;
303
rd->rn = cpystr(remote_name);
308
rd->t.i.special_hdr = cpystr((char *)type_spec);
313
q_status_message(SM_ORDER, 3,5, "rd_new_remdata: type not supported");
322
* Closes the remote stream and frees.
325
rd_free_remdata(REMDATA_S **rd)
328
rd_close_remote(*rd);
331
fs_give((void **)&(*rd)->rn);
334
fs_give((void **)&(*rd)->lf);
342
so_give(&(*rd)->sonofile);
343
(*rd)->sonofile = NULL;
348
if((*rd)->t.i.special_hdr)
349
fs_give((void **)&(*rd)->t.i.special_hdr);
351
if((*rd)->t.i.chk_date)
352
fs_give((void **)&(*rd)->t.i.chk_date);
357
q_status_message(SM_ORDER, 3, 5,
358
"rd_free_remdata: type not supported");
362
fs_give((void **)rd);
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
373
rd_trim_remdata(REMDATA_S **rd)
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
387
if((*rd)->flags & DO_REMTRIM &&
388
!((*rd)->flags & REM_OUTOFDATE) &&
389
(*rd)->t.i.chk_nmsgs > ps_global->remote_abook_history + 2){
391
/* make sure stream is open */
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){
400
int user_deleted = 0;
403
* If user manually deleted some, we'd better not delete
406
if(count_flagged((*rd)->t.i.stream, F_DEL) == 0L){
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);
417
mail_expunge((*rd)->t.i.stream);
420
rd_update_metadata(*rd, NULL);
422
* don't update metafile because user is messing with
423
* the remote folder manually. We'd better re-read it next
428
ps_global->noshow_error = 0;
434
q_status_message(SM_ORDER, 3,5, "rd_trim_remdata: type not supported");
441
* All done with this remote data. Trim the folder, close the
445
rd_close_remdata(REMDATA_S **rd)
452
if((*rd)->lf && (*rd)->flags & DEL_FILE)
453
our_unlink((*rd)->lf);
455
/* this closes the stream and frees memory */
461
* Looks in the metadata file for the cache line corresponding to rd and
462
* fills in data in rd.
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.
471
rd_read_metadata(REMDATA_S *rd)
473
REMDATA_META_S *rab = NULL;
475
struct variable *vars = ps_global->vars;
477
dprint((7, "rd_read_metadata \"%s\"\n",
478
(rd && rd->rn) ? rd->rn : "?"));
483
if(rd->flags & NO_PERM_CACHE)
486
rab = rd_find_our_metadata(rd->rn, &rd->flags);
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;
497
if(!(rd->flags & NO_PERM_CACHE))
498
display_message('x');
500
dprint((2, "using temp cache file %s\n",
501
rd->lf ? rd->lf : "<none>"));
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];
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.
516
if((lc = last_cmpnt(ps_global->pinerc)) != NULL){
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';
529
build_path(path, dir, rab->local_cache_file, sizeof(path));
530
rd->lf = cpystr(path);
533
rd->lf = rab->local_cache_file;
534
/* don't free this below, we're using it */
535
rab->local_cache_file = NULL;
538
rd->read_status = rab->read_status;
542
rd->t.i.chk_date = rab->date;
543
rab->date = NULL; /* don't free this below, we're using it */
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;
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',
561
q_status_message(SM_ORDER, 3, 5,
562
"rd_read_metadata: type not supported");
566
if(rd->t.i.chk_nmsgs > 0)
567
try_cache++; /* cache should be valid if we can't contact server */
570
* The line for this data wasn't in the metadata file yet.
571
* Figure out what should go there and put it in.
575
* The local_cache_file is where we will store the cached local
576
* copy of the remote data.
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;
586
rd->lf = temp_nam(NULL, "a7", 0);
590
if(rab->local_cache_file)
591
fs_give((void **)&rab->local_cache_file);
593
fs_give((void **)&rab->date);
594
fs_give((void **)&rab);
602
* Write out the contents of the metadata file.
604
* Each line should be: folder_name cache_file uidvalidity uidnext uid nmsgs
607
* If delete_it is set, then remove the line instead of updating it.
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.
615
rd_write_metadata(REMDATA_S *rd, int delete_it)
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];
623
struct variable *vars = ps_global->vars;
625
dprint((7, "rd_write_metadata \"%s\"\n",
626
(rd && rd->rn) ? rd->rn : "?"));
628
if(!rd || rd->flags & NO_META_UPDATE)
631
if(rd->type != RemImap){
632
q_status_message(SM_ORDER, 3, 5,
633
"rd_write_metadata: type not supported");
637
dprint((9, " - rd_write_metadata: rn=%s lf=%s\n",
638
rd->rn ? rd->rn : "?", rd->lf ? rd->lf : "?"));
642
if(!(pith_opt_rd_metadata_name && (metafile = (*pith_opt_rd_metadata_name)())))
645
if(!(tempfile = tempfile_in_same_dir(metafile, "a9", &pinerc_dir, 0)))
648
if((fd = our_open(tempfile, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0600)) >= 0)
649
fp_new = fdopen(fd, "w");
651
if(pinerc_dir && rd->lf && strlen(rd->lf) > strlen(pinerc_dir))
652
rel_filename = rd->lf + strlen(pinerc_dir) + 1;
654
rel_filename = rd->lf;
657
fs_give((void **)&pinerc_dir);
659
fp_old = our_fopen(metafile, "rb");
661
if(fp_new && fp_old){
663
* Write the header line.
665
if(fprintf(fp_new, "%s %s Pine Metadata\n",
666
PMAGIC, METAFILE_VERSION_NUM) == EOF)
669
while((p = fgets(line, sizeof(line), fp_old)) != NULL){
671
* Skip the header line and any lines that don't begin
677
/* skip the old cache line for this key */
678
if(strncmp(line, key, strlen(key)) == 0 && line[strlen(key)] == TAB)
681
/* add this line to new version of file */
682
if(fputs(p, fp_new) == EOF)
687
/* add the cache line for this key */
688
/* Warning: this is type RemImap specific right now! */
694
fprintf(fp_new, "%s\t%s\t%lu\t%lu\t%lu\t%lu\t%c\t%s\n",
696
rel_filename ? rel_filename : "",
697
rd->t.i.uidvalidity, rd->t.i.uidnext, rd->t.i.uid,
699
rd->read_status ? rd->read_status : '?',
700
rd->t.i.chk_date ? rd->t.i.chk_date : "no-match")
705
if(fclose(fp_new) == EOF){
710
if(fclose(fp_old) == EOF){
715
if(rename_file(tempfile, metafile) < 0)
719
fs_give((void **)&tempfile);
722
fs_give((void **)&metafile);
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));
734
our_unlink(tempfile);
735
fs_give((void **)&tempfile);
738
fs_give((void **)&metafile);
740
(void)fclose(fp_old);
742
(void)fclose(fp_new);
747
rd_update_metadata(REMDATA_S *rd, char *date)
752
dprint((9, " - rd_update_metadata: rn=%s lf=%s\n",
753
rd->rn ? rd->rn : "?", rd->lf ? rd->lf : "<none>"));
758
ps_global->noshow_error = 1;
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
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))){
779
rd->t.i.chk_nmsgs = rd->t.i.stream ? rd->t.i.stream->nmsgs : 0L;
782
* If nmsgs < 2 something is wrong.
784
if(rd->t.i.chk_nmsgs < 2){
785
rd->t.i.uidvalidity = 0L;
787
rd->t.i.uidnext = 0L;
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);
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.
802
rd->t.i.uidnext = MAX(rd->t.i.stream->uid_last,rd->t.i.uid)
807
ps_global->noshow_error = 0;
810
* Save the date so that we can check if it changed next time
815
fs_give((void **)&rd->t.i.chk_date);
817
rd->t.i.chk_date = cpystr(date);
820
rd_write_metadata(rd, 0);
823
rd->t.i.shouldbe_nmsgs = 0;
828
q_status_message(SM_ORDER, 3, 5,
829
"rd_update_metadata: type not supported");
838
rd_find_our_metadata(char *key, long unsigned int *flags)
840
char *p, *q, *metafile = NULL;
841
char line[MAILTMPLEN];
842
REMDATA_META_S *rab = NULL;
845
dprint((9, "rd_find_our_metadata \"%s\"\n", key ? key : "?"));
852
if(!(pith_opt_rd_metadata_name && (metafile = (*pith_opt_rd_metadata_name)()) != NULL))
856
* Open the metadata file and get some information out.
858
fp = our_fopen(metafile, "rb");
860
q_status_message2(SM_ORDER, 3, 5,
861
_("can't open metadata file %s, continuing (%s)"),
862
metafile, error_description(errno));
864
"can't open existing metadata file %s: %s\n",
865
metafile ? metafile : "?", error_description(errno)));
867
(*flags) |= NO_META_UPDATE;
869
fs_give((void **)&metafile);
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.
878
rab = (REMDATA_META_S *)fs_get(sizeof(*rab));
879
memset(rab, 0, sizeof(*rab));
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.
886
if(rd_meta_is_broken(fp)){
889
"metadata file is broken, creating new one: %s\n",
890
metafile ? metafile : "?"));
892
/* Make it size zero so we won't copy any bad stuff */
893
if(fp_file_size(fp) != 0){
897
our_unlink(metafile);
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));
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);
917
fs_give((void **)&metafile);
921
fs_give((void **)&metafile);
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)
928
#define SKIP_TO_TAB(p) do{while(*p && *p != TAB)p++;}while(0)
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.
935
if(p){ /* Found the line, parse it. */
936
SKIP_TO_TAB(p); /* skip to TAB following folder_name */
938
q = ++p; /* q points to cache_file */
939
SKIP_TO_TAB(p); /* skip to TAB following cache_file */
942
rab->local_cache_file = cpystr(q);
943
q = ++p; /* q points to uidvalidity */
944
SKIP_TO_TAB(p); /* skip to TAB following uidvalidity */
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 */
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 */
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 */
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 */
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 */
973
rab->date = cpystr(q);
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.
993
* A side effect is that the file pointer will be pointing to the second
994
* line if 0 is returned.
997
rd_meta_is_broken(FILE *fp)
999
char buf[MAILTMPLEN];
1001
if(fp == (FILE *)NULL)
1004
if(fp_file_size(fp) <
1005
(long)(SIZEOF_PMAGIC + SIZEOF_SPACE + SIZEOF_VERSION_NUM))
1008
if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0))
1011
/* check for magic */
1012
if(fread(buf, sizeof(char), (unsigned)SIZEOF_PMAGIC, fp) != SIZEOF_PMAGIC)
1015
buf[SIZEOF_PMAGIC] = '\0';
1016
if(strcmp(buf, PMAGIC) != 0)
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.
1025
/* check for matching version number */
1026
if(fseek(fp, (long)TO_FIND_VERSION_NUM, 0))
1029
if(fread(buf, sizeof(char), (unsigned)SIZEOF_VERSION_NUM, fp) !=
1033
buf[SIZEOF_VERSION_NUM] = '\0';
1034
if(strcmp(buf, METAFILE_VERSION_NUM) != 0)
1037
/* Position file pointer to second line */
1038
if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0))
1041
if(fgets(buf, sizeof(buf), fp) == NULL)
1049
* Open a data stream to the remote data.
1052
rd_open_remote(REMDATA_S *rd)
1054
long openmode = SP_USEPOOL | SP_TEMPUSE;
1056
dprint((7, "rd_open_remote \"%s\"\n",
1057
(rd && rd->rn) ? rd->rn : "?"));
1062
if(rd_stream_exists(rd)){
1063
rd->last_use = get_adj_time();
1070
if(rd->access == ReadOnly)
1071
openmode |= OP_READONLY;
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;
1077
/* Don't try to reopen if there was a problem (auth failure, etc.) */
1079
rd->flags |= USER_SAID_NO; /* Caution: overloading USER_SAID_NO */
1081
if(rd->t.i.stream && rd->t.i.stream->halfopen){
1082
/* this is a failure */
1083
rd_close_remote(rd);
1087
rd->last_use = get_adj_time();
1092
q_status_message(SM_ORDER, 3, 5, "rd_open_remote: type not supported");
1099
* Close a data stream to the remote data.
1102
rd_close_remote(REMDATA_S *rd)
1104
if(!rd || !rd_stream_exists(rd))
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;
1116
q_status_message(SM_ORDER, 3, 5, "rd_close_remote: type not supported");
1123
rd_stream_exists(REMDATA_S *rd)
1130
return(rd->t.i.stream ? 1 : 0);
1133
q_status_message(SM_ORDER, 3,5, "rd_stream_exists: type not supported");
1142
* Ping the stream and return 1 if it is still alive.
1145
rd_ping_stream(REMDATA_S *rd)
1149
dprint((7, "rd_ping_stream \"%s\"\n",
1150
(rd && rd->rn) ? rd->rn : "?"));
1155
if(!rd_stream_exists(rd)){
1159
* If this stream is already actually open, officially open
1162
if(sp_stream_get(rd->rn, SP_MATCH)){
1163
long openflags = SP_USEPOOL | SP_TEMPUSE;
1165
if(rd->access == ReadOnly)
1166
openflags |= OP_READONLY;
1168
rd->t.i.stream = pine_mail_open(NULL, rd->rn, openflags, NULL);
1178
if(!rd_stream_exists(rd))
1183
ps_global->noshow_error = 1;
1184
if(pine_mail_ping(rd->t.i.stream))
1187
rd->t.i.stream = NULL;
1189
ps_global->noshow_error = 0;
1193
q_status_message(SM_ORDER, 3, 5, "rd_ping_stream: type not supported");
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
1207
rd_check_readonly_access(REMDATA_S *rd)
1209
if(rd && rd->read_status == 'R'){
1213
if(rd->t.i.stream && !rd->t.i.stream->rdonly){
1214
rd->read_status = 'W';
1215
rd->access = ReadWrite;
1221
q_status_message(SM_ORDER, 3, 5,
1222
"rd_check_readonly_access: type not supported");
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
1234
* The remote folder already exists before we get here, though it may be empty.
1236
* Returns -1 on failure
1240
rd_init_remote(REMDATA_S *rd, int add_only_first_msg)
1245
dprint((7, "rd_init_remote \"%s\"\n",
1246
(rd && rd->rn) ? rd->rn : "?"));
1248
if(rd && rd->type != RemImap){
1249
dprint((1, "rd_init_remote: type not supported\n"));
1254
* The rest is currently type RemImap-specific.
1257
if(!(rd->flags & NO_FILE || rd->lf) ||
1258
!rd->rn || !rd->so || !rd->t.i.stream ||
1259
!rd->t.i.special_hdr){
1261
"rd_init_remote: Unexpected error: %s is NULL\n",
1262
!(rd->flags & NO_FILE || rd->lf) ? "localfile" :
1263
!rd->rn ? "remotename" :
1265
!rd->t.i.stream ? "stream" :
1266
!rd->t.i.special_hdr ? "special_hdr" : "?"));
1270
/* already init'd */
1271
if(rd->t.i.stream->nmsgs >= 2 ||
1272
(rd->t.i.stream->nmsgs >= 1 && add_only_first_msg))
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 : "?"));
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.
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"));
1297
"Can't initialize remote folder \"%s\"\n",
1298
rd->rn ? rd->rn : "?"));
1300
" No write permission for that remote folder.\n"));
1302
" Choose a new unused folder for the remote data.\n"));
1307
if(rd->t.i.stream->nmsgs == 0){
1310
we_cancel = busy_cue(_("Initializing remote data"), NULL, 1);
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
1320
err = rd_add_hdr_msg(rd, date);
1321
if(rd->t.i.stream->nmsgs == 0)
1323
if(rd->t.i.stream && rd->t.i.stream->nmsgs == 0)
1324
pine_mail_check(rd->t.i.stream);
1327
cancel_busy_cue(-1);
1332
err = rd_chk_for_hdr_msg(&(rd->t.i.stream), rd, &eptr);
1334
q_status_message1(SM_ORDER | SM_DING, 5, 5,
1335
_("\"%s\" has invalid format, can't initialize"), rd->rn);
1338
"Can't initialize remote data \"%s\"\n",
1339
rd->rn ? rd->rn : "?"));
1342
q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
1343
dprint((1, "%s\n", eptr ? eptr : "?"));
1344
fs_give((void **)&eptr);
1351
* Add the second (empty) message.
1353
if(!err && !add_only_first_msg){
1354
char *tempfile = NULL;
1357
if(rd->flags & NO_FILE){
1358
if(so_truncate(rd->sonofile, 0L) == 0)
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)));
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));
1377
"init_remote: Error opening temporary file: %s: %s\n",
1378
tempfile ? tempfile : "?", error_description(errno)));
1379
our_unlink(tempfile);
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);
1394
fs_give((void **)&tempfile);
1398
err = rd_update_remote(rd, date);
1400
q_status_message1(SM_ORDER | SM_DING, 3, 5,
1401
_("Error copying to remote folder: %s"),
1402
error_description(errno));
1404
q_status_message(SM_ORDER | SM_DING, 5, 5,
1405
"Creation of remote data folder failed");
1411
rd_update_metadata(rd, date);
1412
/* turn off out of date flag */
1413
rd->flags &= ~REM_OUTOFDATE;
1416
return(err ? -1 : 0);
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.
1425
* Returns 0 if ok, < 0 if invalid format.
1429
rd_chk_for_hdr_msg(MAILSTREAM **streamp, REMDATA_S *rd, char **errmsg)
1431
char *fields[3], *values[3];
1433
int tried_again = 0;
1435
MAILSTREAM *st = NULL;
1437
fields[0] = rd->t.i.special_hdr;
1438
fields[1] = "received";
1444
if(!streamp || !*streamp){
1445
dprint((1, "rd_chk_for_hdr_msg: stream is null\n"));
1447
else if((*streamp)->nmsgs == 0){
1450
"rd_chk_for_hdr_msg: stream has nmsgs=0, try a ping\n"));
1451
if(!pine_mail_ping(*streamp))
1454
if(*streamp && (*streamp)->nmsgs == 0){
1456
"rd_chk_for_hdr_msg: still looks like nmsgs=0, try a check\n"));
1457
pine_mail_check(*streamp);
1460
if(*streamp && (*streamp)->nmsgs == 0){
1462
"rd_chk_for_hdr_msg: still nmsgs=0, try re-opening stream\n"));
1464
if(rd_stream_exists(rd))
1465
rd_close_remote(rd);
1468
if(rd_stream_exists(rd))
1469
st = rd->t.i.stream;
1475
if(st && st->nmsgs == 0){
1477
"rd_chk_for_hdr_msg: can't see header message\n"));
1483
if(st && st->nmsgs != 0
1484
&& (h=pine_fetchheader_lines(st, 1L, NULL, fields))){
1485
simple_header_parse(h, fields, values);
1490
rd->cookie = strtoul(values[0], (char **)NULL, 10);
1493
else if(rd->cookie == 1){
1494
if(rd->flags & COOKIE_ONE_OK || tried_again)
1499
else if(rd->cookie > 1)
1504
fs_give((void **)&values[0]);
1507
fs_give((void **)&values[1]);
1509
fs_give((void **)&h);
1513
if(ret && ret != -6 && errmsg){
1514
*errmsg = (char *)fs_get(500 * sizeof(char));
1515
(*errmsg)[0] = '\0';
1521
snprintf(*errmsg, 500, _("Can't open remote address book \"%s\""), rd->rn);
1524
/* no messages in folder */
1526
snprintf(*errmsg, 500,
1527
_("Error: no messages in remote address book \"%s\"!"),
1533
snprintf(*errmsg, 500,
1534
"First msg in \"%s\" should have \"%s\" header",
1535
rd->rn, rd->t.i.special_hdr);
1538
/* Received header */
1540
snprintf(*errmsg, 500,
1541
_("Suspicious Received headers in first msg in \"%s\""),
1549
* This is a failure and should not happen, but we're not going to
1550
* fail on this condition.
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 : "?"));
1559
"rd_chk_for_hdr_msg: cookie is 1, try to upgrade it\n"));
1561
if(rd_remote_is_readonly(rd)){
1563
"rd_chk_for_hdr_msg: can't upgrade, readonly\n"));
1564
ret = 0; /* stick with 1 */
1567
/* cookie is 1, upgrade it */
1568
if(rd_upgrade_cookies(rd, st->nmsgs, 0) == 0){
1569
/* now check again */
1577
* This is actually a failure but we've decided that this
1584
if(errmsg && *errmsg)
1585
dprint((1, "rd_chk_for_hdr_msg: %s\n", *errmsg));
1592
* For remote data, this adds the explanatory header
1593
* message to the remote IMAP folder.
1595
* Args: rd -- Remote data handle
1596
* date -- The date string to use
1602
rd_add_hdr_msg(REMDATA_S *rd, char *date)
1606
if(!rd|| rd->type != RemImap || !rd->rn || !rd->so || !rd->t.i.special_hdr){
1608
"rd_add_hdr_msg: Unexpected error: %s is NULL\n",
1610
!rd->rn ? "remotename" :
1612
!rd->t.i.special_hdr ? "special_hdr" : "?"));
1616
err = rd_store_fake_hdrs(rd, "Header Message for Remote Data",
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)
1624
if(!err && so_puts(rd->so,
1625
"This message is just an explanatory message.\015\012") == 0)
1627
if(!err && so_puts(rd->so,
1628
"The last message in the folder is the live addressbook data.\015\012") == 0)
1630
if(!err && so_puts(rd->so,
1631
"The rest of the messages contain previous revisions of the addressbook data.\015\012") == 0)
1633
if(!err && so_puts(rd->so,
1634
"To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1636
if(!err && so_puts(rd->so,
1637
"which come after it.\015\012") == 0)
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)
1644
if(!err && so_puts(rd->so,
1645
"This message is just an explanatory message.\015\012") == 0)
1647
if(!err && so_puts(rd->so,
1648
"The last message in the folder is the live config data.\015\012") == 0)
1650
if(!err && so_puts(rd->so,
1651
"The rest of the messages contain previous revisions of the data.\015\012") == 0)
1653
if(!err && so_puts(rd->so,
1654
"To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1656
if(!err && so_puts(rd->so,
1657
"which come after it.\015\012") == 0)
1661
if(!err && so_puts(rd->so,
1662
"This folder contains remote Alpine data.\015\012") == 0)
1664
if(!err && so_puts(rd->so,
1665
"This message is just an explanatory message.\015\012") == 0)
1667
if(!err && so_puts(rd->so,
1668
"The last message in the folder is the live data.\015\012") == 0)
1670
if(!err && so_puts(rd->so,
1671
"The rest of the messages contain previous revisions of the data.\015\012") == 0)
1673
if(!err && so_puts(rd->so,
1674
"To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1676
if(!err && so_puts(rd->so,
1677
"which come after it.\015\012") == 0)
1681
/* Take the message from "so" to the remote folder */
1685
if((st = rd->t.i.stream) != NULL)
1686
rd->t.i.shouldbe_nmsgs = rd->t.i.stream->nmsgs + 1;
1688
st = adrbk_handy_stream(rd->rn);
1690
err = write_fcc(rd->rn, NULL, rd->so, st, "remote data", NULL) ? 0 : -1;
1698
* Write some fake header lines into storage object so.
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
1706
rd_store_fake_hdrs(REMDATA_S *rd, char *subject, char *subtype, char *date)
1713
unsigned long r = 0L;
1716
if(!rd|| rd->type != RemImap || !rd->so || !rd->t.i.special_hdr)
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));
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);
1741
for(i = 100; i > 0 && r < 1000000; i--)
1745
r = 1712836L + getpid();
1750
snprintf(vers, sizeof(vers), "%ld", r);
1754
rbuf.f = dummy_soutr;
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);
1763
mail_free_envelope(&fake_env);
1764
mail_free_body(&fake_body);
1766
/* Write the fake headers */
1767
if(so_puts(rd->so, tmp_20k_buf) == 0)
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
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
1786
rd_upgrade_cookies(REMDATA_S *rd, long int nmsgs, int only_update_last)
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.
1798
rd->flags |= COOKIE_ONE_OK;
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.
1805
if(rd->flags & BELIEVE_CACHE)
1806
rd->flags &= ~BELIEVE_CACHE;
1808
dprint((1, "rd_upgrade_cookies: copy abook data\n"));
1809
err = rd_update_local(rd);
1812
if(!err && !only_update_last){
1813
rd->cookie = 0; /* causes new cookie to be generated */
1815
dprint((1, "rd_upgrade_cookies: add new hdr msg to end\n"));
1816
err = rd_add_hdr_msg(rd, date);
1820
dprint((1, "rd_upgrade_cookies: copy back data\n"));
1821
err = rd_update_remote(rd, NULL);
1824
rd->flags &= ~COOKIE_ONE_OK;
1826
if(!err && !only_update_last){
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.
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);
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).
1856
* Returns != 0 on failure
1860
rd_update_local(REMDATA_S *rd)
1865
int i, we_cancel = 0;
1868
char *tempfile = NULL;
1871
if(!rd || !(rd->flags & NO_FILE || rd->lf) || !rd->rn){
1873
"rd_update_local: Unexpected error: %s is NULL\n",
1875
!(rd->flags & NO_FILE || rd->lf) ? "localfile" :
1876
!rd->rn ? "remotename" : "?"));
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 : "?")));
1887
if(!rd->so || !rd->t.i.special_hdr){
1889
"rd_update_local: Unexpected error: %s is NULL\n",
1891
!rd->t.i.special_hdr ? "special_hdr" : "?"));
1896
if(!rd_stream_exists(rd)){
1897
if(rd->flags & NO_PERM_CACHE){
1899
"rd_update_local: backtrack, remote folder does not exist yet\n"));
1903
"rd_update_local: Unexpected error: stream is NULL\n"));
1915
/* force ReadOnly */
1916
if(rd->t.i.stream->rdonly){
1917
rd->read_status = 'R';
1918
rd->access = ReadOnly;
1921
rd->read_status = 'W';
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);
1930
q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
1931
dprint((1, "%s\n", eptr));
1932
fs_give((void **)&eptr);
1936
"Can't initialize remote data \"%s\"\n",
1937
rd->rn ? rd->rn : "?"));
1941
we_cancel = busy_cue(_("Copying remote data"), NULL, 1);
1943
if(rd->flags & NO_FILE){
1944
store = rd->sonofile;
1945
so_truncate(store, 0L);
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));
1953
"rd_update_local: Error opening temporary file: %s\n",
1954
error_description(errno)));
1958
/* Copy the data into tempfile */
1959
if((store = so_get(FileStar, tempfile, WRITE_ACCESS|OWNER_ONLY))
1961
q_status_message2(SM_ORDER | SM_DING, 3, 5,
1962
_("Error opening temporary file %s: %s"),
1963
tempfile, error_description(errno));
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);
1975
* Copy from the last message in the folder.
1977
if(!pine_mail_fetchstructure(rd->t.i.stream, rd->t.i.stream->nmsgs,
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"));
1983
our_unlink(tempfile);
1984
fs_give((void **)&tempfile);
1987
if(!(rd->flags & NO_FILE))
1991
cancel_busy_cue(-1);
1997
body->type != REMOTE_DATA_TYPE ||
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"));
2003
"Remote IMAP folder has wrong contents\n"));
2005
our_unlink(tempfile);
2006
fs_give((void **)&tempfile);
2009
if(!(rd->flags & NO_FILE))
2013
cancel_busy_cue(-1);
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"));
2023
"Can't access check date in remote data\n"));
2025
our_unlink(tempfile);
2026
fs_give((void **)&tempfile);
2029
if(!(rd->flags & NO_FILE))
2033
cancel_busy_cue(-1);
2038
if(rd && rd->flags & USER_SAID_YES)
2041
chk = rd_check_for_suspect_data(rd);
2044
case -1: /* suspicious data, user says abort */
2046
our_unlink(tempfile);
2047
fs_give((void **)&tempfile);
2050
if(!(rd->flags & NO_FILE))
2054
cancel_busy_cue(-1);
2058
case 1: /* suspicious data, user says go ahead */
2059
if(rd_remote_is_readonly(rd)){
2061
"rd_update_local: can't upgrade, readonly\n"));
2064
/* attempt to upgrade cookie in last message */
2065
(void)rd_upgrade_cookies(rd, 0, 1);
2069
case 0: /* all is ok */
2075
/* store may have been written to at this point, so we'll clear it out */
2076
so_truncate(store, 0L);
2078
gf_set_so_writec(&pc, store);
2080
error = detach(rd->t.i.stream, rd->t.i.stream->nmsgs, "1", 0L,
2083
gf_clear_so_writec(store);
2087
cancel_busy_cue(-1);
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));
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 : "?"));
2103
our_unlink(tempfile);
2104
fs_give((void **)&tempfile);
2110
if(tempfile && (i = rename_file(tempfile, rd->lf)) < 0){
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?"));
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)));
2125
#endif /* _WINDOWS */
2127
q_status_message2(SM_ORDER | SM_DING, 3, 5,
2128
_("Error updating cache file %s: %s"),
2129
rd->lf, error_description(errno));
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)));
2138
our_unlink(tempfile);
2139
fs_give((void **)&tempfile);
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);
2148
/* turn off out of date flag */
2149
rd->flags &= ~REM_OUTOFDATE;
2152
fs_give((void **)&tempfile);
2157
q_status_message1(SM_ORDER | SM_DING, 5, 5,
2158
_("Can't open remote IMAP folder \"%s\""), rd->rn);
2160
"Can't open remote IMAP folder \"%s\"\n",
2161
rd->rn ? rd->rn : "?"));
2162
rd->access = ReadOnly;
2169
dprint((1, "rd_update_local: Unsupported type\n"));
2176
* Copy local data to remote folder.
2178
* Args lf -- Local file 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
2184
* Returns !=0 on failure
2188
rd_update_remote(REMDATA_S *rd, char *returndate)
2192
long openmode = SP_USEPOOL | SP_TEMPUSE;
2196
if(rd && rd->type != RemImap){
2197
dprint((1, "rd_update_remote: type not supported\n"));
2201
if(!rd || !(rd->flags & NO_FILE || rd->lf) || !rd->rn ||
2202
!rd->so || !rd->t.i.special_hdr){
2204
"rd_update_remote: Unexpected error: %s is NULL\n",
2206
!(rd->flags & NO_FILE || rd->lf) ? "localfile" :
2207
!rd->rn ? "remotename" :
2209
!rd->t.i.special_hdr ? "special_hdr" : "?"));
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 : "?"));
2218
if(!(st = rd->t.i.stream))
2219
st = adrbk_handy_stream(rd->rn);
2222
st = rd->t.i.stream = context_open(NULL, NULL, rd->rn, openmode, NULL);
2225
q_status_message1(SM_ORDER | SM_DING, 5, 5,
2226
_("Can't open \"%s\" for copying"), rd->rn);
2228
"rd_update_remote: Can't open remote folder \"%s\" for copying\n",
2229
rd->rn ? rd->rn : "?"));
2233
rd->last_use = get_adj_time();
2234
err = rd_chk_for_hdr_msg(&st, rd, &eptr);
2236
q_status_message1(SM_ORDER | SM_DING, 5, 5,
2237
_("\"%s\" has invalid format"), rd->rn);
2240
q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
2241
dprint((1, "%s\n", eptr));
2242
fs_give((void **)&eptr);
2246
"rd_update_remote: \"%s\" has invalid format\n",
2247
rd->rn ? rd->rn : "?"));
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.
2259
if(rd->flags & NO_FILE){
2260
store = rd->sonofile;
2262
so_seek(store, 0L, 0); /* rewind */
2265
store = so_get(FileStar, rd->lf, READ_ACCESS);
2270
unsigned char last_c = 0;
2272
/* Reset the storage object, since we may have already used it. */
2273
if(so_truncate(rd->so, 0L) == 0)
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))
2284
/* save the date for later comparisons */
2285
if(!err && returndate)
2286
strncpy(returndate, date, 100);
2288
/* Write the data */
2289
while(!err && so_readc(&c, store)){
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.
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)
2304
if(so_writec((int) c, rd->so) == 0)
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.
2317
if((st = rd->t.i.stream) != NULL)
2318
rd->t.i.shouldbe_nmsgs = rd->t.i.stream->nmsgs + 1;
2320
st = adrbk_handy_stream(rd->rn);
2322
err = write_fcc(rd->rn, NULL, rd->so, st,
2323
"remote data", NULL) ? 0 : 1;
2327
if(!(rd->flags & NO_FILE))
2334
dprint((2, "error in rd_update_remote for %s => %s\n",
2335
rd->lf ? rd->lf : "<mem>", rd->rn ? rd->rn : "?"));
2342
* Check to see if the remote data has changed since we cached it.
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
2350
rd_check_remvalid(REMDATA_S *rd, long int do_it_now)
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;
2358
dprint((7, "- rd_check_remvalid(%s) -\n",
2359
(rd && rd->rn) ? rd->rn : ""));
2361
if(rd && rd->type != RemImap){
2362
dprint((1, "rd_check_remvalid: type not supported\n"));
2366
if(!rd || rd->flags & REM_OUTOFDATE || rd->flags & USE_OLD_CACHE)
2369
if(!rd->t.i.chk_date){
2371
"rd_check_remvalid: remote data %s changed (chk_date)\n",
2373
rd->flags |= REM_OUTOFDATE;
2377
if(rd->t.i.chk_nmsgs <= 1){
2379
"rd_check_remvalid: remote data %s changed (chk_nmsgs <= 1)\n",
2380
rd->rn ? rd->rn : "?"));
2381
rd->flags |= REM_OUTOFDATE;
2386
chk_interval = -1L * do_it_now;
2390
chk_interval = ps_global->remote_abook_validity * 60L;
2392
/* too soon to check again */
2394
(chk_interval == 0L ||
2395
get_adj_time() <= rd->last_valid_chk + chk_interval))
2398
if(rd->access == ReadOnly)
2399
openmode |= OP_READONLY;
2401
rd->last_valid_chk = get_adj_time();
2402
mm_status_result.flags = 0L;
2404
/* make sure the cache file is still there */
2405
if(rd->lf && can_access(rd->lf, READ_ACCESS) != 0){
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;
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.
2422
try_looking_in_stream:
2425
* Get the current number of messages in the folder to
2426
* compare with our saved number of messages.
2430
dprint((7, "using open remote data stream\n"));
2431
rd->last_use = get_adj_time();
2432
current_nmsgs = rd->t.i.stream->nmsgs;
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.
2443
if(!(rd->flags & NO_STATUSCMD))
2444
stat_stream = adrbk_handy_stream(rd->rn);
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.
2453
if(!LEVELSTATUS(stat_stream)){
2454
rd->flags |= NO_STATUSCMD;
2456
"rd_check_remvalid: remote data %s: server doesn't support status\n",
2457
rd->rn ? rd->rn : "?"));
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,
2471
if(same_stream_and_mailbox(rd->rn, stat_stream)){
2473
"rd_check_remvalid: faking status\n"));
2474
mm_status_result.flags = SA_MESSAGES | SA_UIDVALIDITY
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;
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;
2490
"rd_check_remvalid: addrbook %s: status command failed\n",
2491
rd->rn ? rd->rn : "?"));
2492
mm_status_result.flags = 0L;
2496
ps_global->noshow_error = 0;
2500
/* if we got back a result from the status command, use it */
2501
if(mm_status_result.flags){
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;
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.
2517
if(got_cmsgs && current_nmsgs != rd->t.i.chk_nmsgs){
2518
rd->flags |= REM_OUTOFDATE;
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));
2526
* Get the current uidvalidity and uidnext values from the
2527
* folder to compare with our saved values.
2530
if(rd->t.i.stream->uid_validity == rd->t.i.uidvalidity){
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.
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;
2539
"rd_check_remvalid: remote data %s changed (uid)\n",
2540
rd->rn ? rd->rn : "?"));
2543
dprint((7,"rd_check_remvalid: uids match\n"));
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.
2553
rd->flags |= NO_STATUSCMD;
2555
"rd_check_remvalid: remote data %s uidvalidity changed, don't use uid\n",
2556
rd->rn ? rd->rn : "?"));
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){
2568
if(mm_status_result.uidnext == rd->t.i.uidnext){
2570
* Uidnext valid and unchanged, so the folder is unchanged.
2572
dprint((7, "rd_check_remvalid: uidnexts match\n"));
2575
else{ /* uidnext changed, folder _may_ have changed */
2578
"rd_check_remvalid: uidnexts don't match, ours=%lu status=%lu\n",
2579
rd->t.i.uidnext, mm_status_result.uidnext));
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.
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,
2591
imapuid_t last_msg_uid;
2593
if(rd->t.i.stream->rdonly)
2594
rd->read_status = 'R';
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();
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;
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));
2610
else{ /* false hit */
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.
2620
"rd_check_remvalid: remote data %s false change: adjusting uidnext from %lu to %lu\n",
2621
rd->rn ? rd->rn : "?",
2623
mm_status_result.uidnext));
2624
rd->t.i.uidnext = mm_status_result.uidnext;
2625
rd_write_metadata(rd, 0);
2629
cancel_busy_cue(-1);
2634
ps_global->noshow_error = 0;
2636
"rd_check_remvalid: couldn't open %s\n",
2637
rd->rn ? rd->rn : "?"));
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.
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.
2655
rd->flags |= NO_STATUSCMD;
2657
"rd_check_remvalid: remote data %s don't use status\n",
2658
rd->rn ? rd->rn : "?"));
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,
2667
ps_global->noshow_error = 0;
2669
cancel_busy_cue(-1);
2673
goto try_looking_in_stream;
2676
"rd_check_remvalid: cannot open remote mailbox\n"));
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.
2693
"rd_check_remvalid: falling back to Date check\n"));
2696
* Fall back to looking in the folder at the Date header.
2700
we_cancel = busy_cue(NULL, NULL, 1);
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;
2707
if(rd->t.i.stream->rdonly)
2708
rd->read_status = 'R';
2710
if(rd->t.i.stream->nmsgs != rd->t.i.chk_nmsgs){
2711
rd->flags |= REM_OUTOFDATE;
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));
2717
else if(rd->t.i.stream->nmsgs > 1){
2718
env = pine_mail_fetchenvelope(rd->t.i.stream,rd->t.i.stream->nmsgs);
2720
if(!env || (env->date && strucmp((char *) env->date, rd->t.i.chk_date))){
2721
rd->flags |= REM_OUTOFDATE;
2723
"rd_check_remvalid: remote data %s changed (%s)\n",
2724
rd->rn ? rd->rn : "?", env ? "date" : "not enough msgs"));
2728
rd->last_use = get_adj_time();
2731
"%s: got envelope to check date (%ld)\n",
2732
rd->rn ? rd->rn : "?", (long)rd->last_use));
2734
/* else, we give up and go with the cache copy */
2736
ps_global->noshow_error = 0;
2739
cancel_busy_cue(-1);
2741
dprint((7, "rd_check_remvalid: falling off end\n"));
2746
* Returns nonzero if remote data is currently readonly.
2748
* Returns 0 -- apparently writeable
2749
* 1 -- stream not open
2750
* 2 -- stream open but not writeable
2753
rd_remote_is_readonly(REMDATA_S *rd)
2755
if(!rd || rd->access == ReadOnly || !rd_stream_exists(rd))
2760
if(rd->t.i.stream->rdonly)
2766
q_status_message(SM_ORDER, 3, 5,
2767
"rd_remote_is_readonly: type not supported");
2777
* -1 if not ok and user says No
2778
* 1 if not ok but user says Yes, ok to use it
2781
rd_check_for_suspect_data(REMDATA_S *rd)
2784
char *fields[3], *values[3], *h;
2785
unsigned long cookie;
2787
if(!rd || rd->type != RemImap || !rd->so || !rd->rn || !rd->t.i.special_hdr)
2790
fields[0] = rd->t.i.special_hdr;
2791
fields[1] = "received";
2794
if(h=pine_fetchheader_lines(rd->t.i.stream, rd->t.i.stream->nmsgs,
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);
2800
cookie = strtoul(values[0], (char **)NULL, 10);
2801
if(cookie == rd->cookie) /* all's well */
2804
ans = rd_prompt_about_forged_remote_data(cookie > 1L
2809
ans = rd_prompt_about_forged_remote_data(-2, rd, NULL);
2812
fs_give((void **)&values[0]);
2815
fs_give((void **)&values[1]);
2817
fs_give((void **)&h);
2819
else /* missing magic header */
2820
ans = rd_prompt_about_forged_remote_data(-2, rd, NULL);