2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
17 Implements remote IMAP config files (remote config, remote abook).
21 #include "../pith/headers.h"
22 #include "../pith/remote.h"
23 #include "../pith/conf.h"
24 #include "../pith/imap.h"
25 #include "../pith/msgno.h"
26 #include "../pith/mailview.h"
27 #include "../pith/status.h"
28 #include "../pith/flag.h"
29 #include "../pith/tempfile.h"
30 #include "../pith/adrbklib.h"
31 #include "../pith/detach.h"
32 #include "../pith/filter.h"
33 #include "../pith/stream.h"
34 #include "../pith/options.h"
35 #include "../pith/busy.h"
36 #include "../pith/readfile.h"
42 REMDATA_META_S
*rd_find_our_metadata(char *, unsigned long *);
43 int rd_meta_is_broken(FILE *);
44 int rd_add_hdr_msg(REMDATA_S
*, char *);
45 int rd_store_fake_hdrs(REMDATA_S
*, char *, char *, char *);
46 int rd_upgrade_cookies(REMDATA_S
*, long, int);
47 int rd_check_for_suspect_data(REMDATA_S
*);
50 char meta_prefix
[] = ".ab";
53 char *(*pith_opt_rd_metadata_name
)(void);
57 read_remote_pinerc(PINERC_S
*prc
, ParsePinerc which_vars
)
59 int try_cache
, no_perm_create_pass
= 0;
64 dprint((7, "read_remote_pinerc \"%s\"\n",
65 prc
->name
? prc
->name
: "?"));
68 * We don't cache the pinerc, we always copy it.
70 * Don't store the config in a temporary file, just leave it
71 * in memory while using it.
72 * It is currently required that NO_PERM_CACHE be set if NO_FILE is set.
74 flags
= (NO_PERM_CACHE
| NO_FILE
);
76 create_the_remote_folder
:
78 if(no_perm_create_pass
){
80 prc
->rd
->flags
&= ~DO_REMTRIM
;
81 rd_close_remdata(&prc
->rd
);
84 /* this will cause the remote folder to be created */
89 * We could parse the name here to find what type it is. So far we
90 * only have type RemImap.
92 prc
->rd
= rd_create_remote(RemImap
, prc
->name
,
93 REMOTE_PINERC_SUBTYPE
,
95 _(" Can't fetch remote configuration."));
100 * On first use we just use a temp file instead of memory (NO_FILE).
101 * In other words, for our convenience, we don't turn NO_FILE back on
102 * here. Why is that convenient? Because of the stuff that happened in
103 * rd_create_remote when flags was set to zero.
105 if(no_perm_create_pass
)
106 prc
->rd
->flags
|= NO_PERM_CACHE
;
108 try_cache
= rd_read_metadata(prc
->rd
);
110 if(prc
->rd
->access
== MaybeRorW
){
111 if(prc
->rd
->read_status
== 'R' ||
112 !(which_vars
== ParsePers
|| which_vars
== ParsePersPost
)){
113 prc
->rd
->access
= ReadOnly
;
114 prc
->rd
->read_status
= 'R';
117 prc
->rd
->access
= ReadWrite
;
120 if(prc
->rd
->access
!= NoExists
){
122 rd_check_remvalid(prc
->rd
, 1L);
125 * If the cached info says it is readonly but
126 * it looks like it's been fixed now, change it to readwrite.
128 if((which_vars
== ParsePers
|| which_vars
== ParsePersPost
) &&
129 prc
->rd
->read_status
== 'R'){
131 * We go to this trouble since readonly pinercs
132 * are likely a mistake. They are usually supposed to be
133 * readwrite so we open it and check if it's been fixed.
135 rd_check_readonly_access(prc
->rd
);
136 if(prc
->rd
->read_status
== 'W'){
137 prc
->rd
->access
= ReadWrite
;
138 prc
->rd
->flags
|= REM_OUTOFDATE
;
141 prc
->rd
->access
= ReadOnly
;
144 if(prc
->rd
->flags
& REM_OUTOFDATE
){
145 if(rd_update_local(prc
->rd
) != 0){
146 if(!no_perm_create_pass
&& prc
->rd
->flags
& NO_PERM_CACHE
147 && !(prc
->rd
->flags
& USER_SAID_NO
)){
149 * We don't check for the existence of the remote
150 * folder when this flag is turned on, so we could
151 * fail here because the remote folder doesn't exist.
152 * We try to create it.
154 no_perm_create_pass
++;
155 goto create_the_remote_folder
;
159 "read_pinerc_remote: rd_update_local failed\n"));
161 * Don't give up altogether. We still may be
162 * able to use a cached copy.
167 "%s: copied remote to local (%ld)\n",
168 prc
->rd
->rn
? prc
->rd
->rn
: "?",
169 (long)prc
->rd
->last_use
));
173 if(prc
->rd
->access
== ReadWrite
)
174 prc
->rd
->flags
|= DO_REMTRIM
;
177 /* If we couldn't get to remote folder, try using the cached copy */
178 if(prc
->rd
->access
== NoExists
|| prc
->rd
->flags
& REM_OUTOFDATE
){
180 prc
->rd
->access
= ReadOnly
;
181 prc
->rd
->flags
|= USE_OLD_CACHE
;
182 q_status_message(SM_ORDER
, 3, 4,
183 "Can't contact remote config server, using cached copy");
185 "Can't open remote pinerc %s, using local cached copy %s readonly\n",
186 prc
->rd
->rn
? prc
->rd
->rn
: "?",
187 prc
->rd
->lf
? prc
->rd
->lf
: "?"));
190 prc
->rd
->flags
&= ~DO_REMTRIM
;
195 if(prc
->rd
->flags
& NO_FILE
)
196 /* copy text, leave sonofile for later use */
197 file
= cpystr((char *)so_text(prc
->rd
->sonofile
));
199 file
= read_file(prc
->rd
->lf
, 0);
202 if((which_vars
== ParsePers
|| which_vars
== ParsePersPost
) &&
203 (!file
|| !prc
->rd
|| prc
->rd
->access
!= ReadWrite
)){
205 if(prc
== ps_global
->prc
)
206 ps_global
->readonly_pinerc
= 1;
214 * Check if the remote data folder exists and create an empty folder
215 * if it doesn't exist.
217 * Args - type -- The type of remote storage.
219 * type_spec -- Type-specific data.
221 * err_prefix -- Should usually end with a SPACE
222 * err_suffix -- Should usually begin with a SPACE
224 * Returns a pointer to a REMDATA_S with access set to either
225 * NoExists or MaybeRorW. On success, "so" will point to a storage object.
228 rd_create_remote(RemType type
, char *remote_name
, char *type_spec
,
229 unsigned int *flags
, char *err_prefix
, char *err_suffix
)
231 REMDATA_S
*rd
= NULL
;
232 CONTEXT_S
*dummy_cntxt
= NULL
;
234 dprint((7, "rd_create_remote \"%s\"\n",
235 remote_name
? remote_name
: "?"));
237 rd
= rd_new_remdata(type
, remote_name
, type_spec
);
243 if(rd
->flags
& NO_PERM_CACHE
){
244 if(rd
->rn
&& (rd
->so
= so_get(CharStar
, NULL
, WRITE_ACCESS
))){
245 if(rd
->flags
& NO_FILE
){
246 rd
->sonofile
= so_get(CharStar
, NULL
, WRITE_ACCESS
);
248 rd
->flags
&= ~NO_FILE
;
252 * We're not going to check if it is there in this case,
253 * in order to save ourselves some round trips and
254 * connections. We'll just try to select it and then
255 * recover at that point if it isn't already there.
257 rd
->flags
|= REM_OUTOFDATE
;
258 rd
->access
= MaybeRorW
;
264 * Open_fcc creates the folder if it didn't already exist.
266 if(rd
->rn
&& (rd
->so
= open_fcc(rd
->rn
, &dummy_cntxt
, 1,
267 err_prefix
, err_suffix
)) != NULL
){
269 * We know the folder is there but don't know what access
270 * rights we have until we try to select it, which we don't
271 * want to do unless we have to. So delay evaluating.
273 rd
->access
= MaybeRorW
;
280 q_status_message(SM_ORDER
, 3,5, "rd_create_remote: type not supported");
289 rd_new_remdata(RemType type
, char *remote_name
, char *type_spec
)
291 REMDATA_S
*rd
= NULL
;
293 rd
= (REMDATA_S
*)fs_get(sizeof(*rd
));
294 memset((void *)rd
, 0, sizeof(*rd
));
297 rd
->access
= NoExists
;
300 rd
->rn
= cpystr(remote_name
);
305 rd
->t
.i
.special_hdr
= cpystr(type_spec
);
310 q_status_message(SM_ORDER
, 3,5, "rd_new_remdata: type not supported");
319 * Closes the remote stream and frees.
322 rd_free_remdata(REMDATA_S
**rd
)
325 rd_close_remote(*rd
);
328 fs_give((void **)&(*rd
)->rn
);
331 fs_give((void **)&(*rd
)->lf
);
339 so_give(&(*rd
)->sonofile
);
340 (*rd
)->sonofile
= NULL
;
345 if((*rd
)->t
.i
.special_hdr
)
346 fs_give((void **)&(*rd
)->t
.i
.special_hdr
);
348 if((*rd
)->t
.i
.chk_date
)
349 fs_give((void **)&(*rd
)->t
.i
.chk_date
);
354 q_status_message(SM_ORDER
, 3, 5,
355 "rd_free_remdata: type not supported");
359 fs_give((void **)rd
);
365 * Call this when finished with the remdata. This does the REMTRIM if the
366 * flag is set, the DEL_FILE if the flag is set. It also closes the stream
370 rd_trim_remdata(REMDATA_S
**rd
)
378 * Trim the number of saved copies of remote data history.
379 * The first message is a fake message that always
380 * stays there, then come ps_global->remote_abook_history messages
381 * which are each a revision of the data, then comes the active
384 if((*rd
)->flags
& DO_REMTRIM
&&
385 !((*rd
)->flags
& REM_OUTOFDATE
) &&
386 (*rd
)->t
.i
.chk_nmsgs
> ps_global
->remote_abook_history
+ 2){
388 /* make sure stream is open */
392 if(!rd_remote_is_readonly(*rd
)){
393 if((*rd
)->t
.i
.stream
&&
394 (*rd
)->t
.i
.stream
->nmsgs
>
395 ps_global
->remote_abook_history
+ 2){
397 int user_deleted
= 0;
400 * If user manually deleted some, we'd better not delete
403 if(count_flagged((*rd
)->t
.i
.stream
, F_DEL
) == 0L){
405 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
: "?"));
406 snprintf(sequence
, sizeof(sequence
), "2:%ld",
407 (*rd
)->t
.i
.stream
->nmsgs
- 1 - ps_global
->remote_abook_history
);
408 mail_flag((*rd
)->t
.i
.stream
, sequence
,
409 "\\DELETED", ST_SET
);
414 mail_expunge((*rd
)->t
.i
.stream
);
417 rd_update_metadata(*rd
, NULL
);
419 * don't update metafile because user is messing with
420 * the remote folder manually. We'd better re-read it next
425 ps_global
->noshow_error
= 0;
431 q_status_message(SM_ORDER
, 3,5, "rd_trim_remdata: type not supported");
438 * All done with this remote data. Trim the folder, close the
442 rd_close_remdata(REMDATA_S
**rd
)
449 if((*rd
)->lf
&& (*rd
)->flags
& DEL_FILE
)
450 our_unlink((*rd
)->lf
);
452 /* this closes the stream and frees memory */
458 * Looks in the metadata file for the cache line corresponding to rd and
459 * fills in data in rd.
461 * Return value -- 1 if it is likely that the filename we're returning
462 * is the permanent name of the local cache file and it may already have
463 * a cached copy of the data. This is to tell us if it makes sense to use
464 * the cached copy when we are unable to contact the remote server.
468 rd_read_metadata(REMDATA_S
*rd
)
470 REMDATA_META_S
*rab
= NULL
;
473 dprint((7, "rd_read_metadata \"%s\"\n",
474 (rd
&& rd
->rn
) ? rd
->rn
: "?"));
479 if(rd
->flags
& NO_PERM_CACHE
)
482 rab
= rd_find_our_metadata(rd
->rn
, &rd
->flags
);
486 rd
->flags
|= (NO_META_UPDATE
| REM_OUTOFDATE
);
487 if(!(rd
->flags
& NO_FILE
)){
488 rd
->lf
= temp_nam(NULL
, "a6");
489 rd
->flags
|= DEL_FILE
;
493 if(!(rd
->flags
& NO_PERM_CACHE
))
494 display_message('x');
496 dprint((2, "using temp cache file %s\n",
497 rd
->lf
? rd
->lf
: "<none>"));
500 else if(rab
->local_cache_file
){ /* A-OK, it was in the file already */
501 if(!is_absolute_path(rab
->local_cache_file
)){
502 char dir
[MAXPATH
+1], path
[MAXPATH
+1];
506 * This should be the normal case. The file is stored as a
507 * filename in the pinerc dir, so that it can be
508 * accessed from the PC or from unix where the pathnames to
509 * get there will be different.
512 if((lc
= last_cmpnt(ps_global
->pinerc
)) != NULL
){
515 to_copy
= (lc
- ps_global
->pinerc
> 1)
516 ? (lc
- ps_global
->pinerc
- 1) : 1;
517 strncpy(dir
, ps_global
->pinerc
, MIN(to_copy
, sizeof(dir
)-1));
518 dir
[MIN(to_copy
, sizeof(dir
)-1)] = '\0';
525 build_path(path
, dir
, rab
->local_cache_file
, sizeof(path
));
526 rd
->lf
= cpystr(path
);
529 rd
->lf
= rab
->local_cache_file
;
530 /* don't free this below, we're using it */
531 rab
->local_cache_file
= NULL
;
534 rd
->read_status
= rab
->read_status
;
538 rd
->t
.i
.chk_date
= rab
->date
;
539 rab
->date
= NULL
; /* don't free this below, we're using it */
542 "in read_metadata, setting chk_date from metadata to ->%s<-\n",
543 rd
->t
.i
.chk_date
? rd
->t
.i
.chk_date
: "?"));
544 rd
->t
.i
.chk_nmsgs
= rab
->nmsgs
;
545 rd
->t
.i
.uidvalidity
= rab
->uidvalidity
;
546 rd
->t
.i
.uidnext
= rab
->uidnext
;
547 rd
->t
.i
.uid
= rab
->uid
;
548 rd
->t
.i
.chk_nmsgs
= rab
->nmsgs
;
550 "setting uid=%lu uidnext=%lu uidval=%lu read_stat=%c nmsgs=%lu\n",
551 rd
->t
.i
.uid
, rd
->t
.i
.uidnext
, rd
->t
.i
.uidvalidity
,
552 rd
->read_status
? rd
->read_status
: '0',
557 q_status_message(SM_ORDER
, 3, 5,
558 "rd_read_metadata: type not supported");
562 if(rd
->t
.i
.chk_nmsgs
> 0)
563 try_cache
++; /* cache should be valid if we can't contact server */
566 * The line for this data wasn't in the metadata file yet.
567 * Figure out what should go there and put it in.
571 * The local_cache_file is where we will store the cached local
572 * copy of the remote data.
574 rab
->local_cache_file
= tempfile_in_same_dir(ps_global
->pinerc
,
576 if(rab
->local_cache_file
){
577 rd
->lf
= rab
->local_cache_file
;
578 rd_write_metadata(rd
, 0);
579 rab
->local_cache_file
= NULL
;
582 rd
->lf
= temp_nam(NULL
, "a7");
586 if(rab
->local_cache_file
)
587 fs_give((void **)&rab
->local_cache_file
);
589 fs_give((void **)&rab
->date
);
590 fs_give((void **)&rab
);
598 * Write out the contents of the metadata file.
600 * Each line should be: folder_name cache_file uidvalidity uidnext uid nmsgs
603 * If delete_it is set, then remove the line instead of updating it.
605 * We have to be careful with the metadata, because it exists in user's
606 * existing metadata files now, and it is oriented towards only RemImap.
607 * We would have to change the version number and the format of the lines
608 * in the file if we add another type.
611 rd_write_metadata(REMDATA_S
*rd
, int delete_it
)
614 FILE *fp_old
= NULL
, *fp_new
= NULL
;
615 char *p
= NULL
, *pinerc_dir
= NULL
, *metafile
= NULL
;
616 char *rel_filename
, *key
;
617 char line
[MAILTMPLEN
];
620 dprint((7, "rd_write_metadata \"%s\"\n",
621 (rd
&& rd
->rn
) ? rd
->rn
: "?"));
623 if(!rd
|| rd
->flags
& NO_META_UPDATE
)
626 if(rd
->type
!= RemImap
){
627 q_status_message(SM_ORDER
, 3, 5,
628 "rd_write_metadata: type not supported");
632 dprint((9, " - rd_write_metadata: rn=%s lf=%s\n",
633 rd
->rn
? rd
->rn
: "?", rd
->lf
? rd
->lf
: "?"));
637 if(!(pith_opt_rd_metadata_name
&& (metafile
= (*pith_opt_rd_metadata_name
)())))
640 if(!(tempfile
= tempfile_in_same_dir(metafile
, "a9", &pinerc_dir
)))
643 if((fd
= our_open(tempfile
, O_TRUNC
|O_WRONLY
|O_CREAT
|O_BINARY
, 0600)) >= 0)
644 fp_new
= fdopen(fd
, "w");
646 if(pinerc_dir
&& rd
->lf
&& strlen(rd
->lf
) > strlen(pinerc_dir
))
647 rel_filename
= rd
->lf
+ strlen(pinerc_dir
) + 1;
649 rel_filename
= rd
->lf
;
652 fs_give((void **)&pinerc_dir
);
654 fp_old
= our_fopen(metafile
, "rb");
656 if(fp_new
&& fp_old
){
658 * Write the header line.
660 if(fprintf(fp_new
, "%s %s Pine Metadata\n",
661 PMAGIC
, METAFILE_VERSION_NUM
) == EOF
)
664 while((p
= fgets(line
, sizeof(line
), fp_old
)) != NULL
){
666 * Skip the header line and any lines that don't begin
672 /* skip the old cache line for this key */
673 if(strncmp(line
, key
, strlen(key
)) == 0 && line
[strlen(key
)] == TAB
)
676 /* add this line to new version of file */
677 if(fputs(p
, fp_new
) == EOF
)
682 /* add the cache line for this key */
683 /* Warning: this is type RemImap specific right now! */
689 fprintf(fp_new
, "%s\t%s\t%lu\t%lu\t%lu\t%lu\t%c\t%s\n",
691 rel_filename
? rel_filename
: "",
692 rd
->t
.i
.uidvalidity
, rd
->t
.i
.uidnext
, rd
->t
.i
.uid
,
694 rd
->read_status
? rd
->read_status
: '?',
695 rd
->t
.i
.chk_date
? rd
->t
.i
.chk_date
: "no-match")
700 if(fclose(fp_new
) == EOF
){
705 if(fclose(fp_old
) == EOF
){
710 if(rename_file(tempfile
, metafile
) < 0)
714 fs_give((void **)&tempfile
);
717 fs_give((void **)&metafile
);
722 dprint((2, "io_err in rd_write_metadata(%s), tempfile=%s: %s\n",
723 metafile
? metafile
: "<NULL>", tempfile
? tempfile
: "<NULL>",
724 error_description(errno
)));
725 q_status_message2(SM_ORDER
, 3, 5,
726 "Trouble updating metafile %s, continuing (%s)",
727 metafile
? metafile
: "<NULL>", error_description(errno
));
729 our_unlink(tempfile
);
730 fs_give((void **)&tempfile
);
733 fs_give((void **)&metafile
);
735 (void)fclose(fp_old
);
737 (void)fclose(fp_new
);
742 rd_update_metadata(REMDATA_S
*rd
, char *date
)
747 dprint((9, " - rd_update_metadata: rn=%s lf=%s\n",
748 rd
->rn
? rd
->rn
: "?", rd
->lf
? rd
->lf
: "<none>"));
753 ps_global
->noshow_error
= 1;
757 * If nmsgs < 2 then something is wrong. Maybe it is just
758 * that we haven't been told about the messages we've
759 * appended ourselves. Try closing and re-opening the stream
762 if(rd
->t
.i
.stream
->nmsgs
< 2 ||
763 (rd
->t
.i
.shouldbe_nmsgs
&&
764 (rd
->t
.i
.shouldbe_nmsgs
!= rd
->t
.i
.stream
->nmsgs
))){
765 pine_mail_check(rd
->t
.i
.stream
);
766 if(rd
->t
.i
.stream
->nmsgs
< 2 ||
767 (rd
->t
.i
.shouldbe_nmsgs
&&
768 (rd
->t
.i
.shouldbe_nmsgs
!= rd
->t
.i
.stream
->nmsgs
))){
774 rd
->t
.i
.chk_nmsgs
= rd
->t
.i
.stream
? rd
->t
.i
.stream
->nmsgs
: 0L;
777 * If nmsgs < 2 something is wrong.
779 if(rd
->t
.i
.chk_nmsgs
< 2){
780 rd
->t
.i
.uidvalidity
= 0L;
782 rd
->t
.i
.uidnext
= 0L;
785 rd
->t
.i
.uidvalidity
= rd
->t
.i
.stream
->uid_validity
;
786 rd
->t
.i
.uid
= mail_uid(rd
->t
.i
.stream
,
787 rd
->t
.i
.stream
->nmsgs
);
789 * Uid_last is not always valid. If the last known uid is
790 * greater than uid_last, go with it instead (uid+1).
791 * If our guess is wrong (too low), the penalty is not
792 * harsh. When the uidnexts don't match we open the
793 * mailbox to check the uid of the actual last message.
794 * If it was a false hit then we adjust uidnext so it
795 * will be correct the next time through.
797 rd
->t
.i
.uidnext
= MAX(rd
->t
.i
.stream
->uid_last
,rd
->t
.i
.uid
)
802 ps_global
->noshow_error
= 0;
805 * Save the date so that we can check if it changed next time
810 fs_give((void **)&rd
->t
.i
.chk_date
);
812 rd
->t
.i
.chk_date
= cpystr(date
);
815 rd_write_metadata(rd
, 0);
818 rd
->t
.i
.shouldbe_nmsgs
= 0;
823 q_status_message(SM_ORDER
, 3, 5,
824 "rd_update_metadata: type not supported");
833 rd_find_our_metadata(char *key
, long unsigned int *flags
)
835 char *p
, *q
, *metafile
= NULL
;
836 char line
[MAILTMPLEN
];
837 REMDATA_META_S
*rab
= NULL
;
840 dprint((9, "rd_find_our_metadata \"%s\"\n", key
? key
: "?"));
845 if(!(pith_opt_rd_metadata_name
&& (metafile
= (*pith_opt_rd_metadata_name
)()) != NULL
))
849 * Open the metadata file and get some information out.
851 fp
= our_fopen(metafile
, "rb");
853 q_status_message2(SM_ORDER
, 3, 5,
854 _("can't open metadata file %s, continuing (%s)"),
855 metafile
, error_description(errno
));
857 "can't open existing metadata file %s: %s\n",
858 metafile
? metafile
: "?", error_description(errno
)));
860 (*flags
) |= NO_META_UPDATE
;
862 fs_give((void **)&metafile
);
868 * If we make it to this point where we have opened the metadata file
869 * we return a structure (possibly empty) instead of just a NULL pointer.
871 rab
= (REMDATA_META_S
*)fs_get(sizeof(*rab
));
872 memset(rab
, 0, sizeof(*rab
));
875 * Check for header line. If it isn't there or is incorrect,
876 * return with the empty rab. This call also positions the file pointer
877 * past the header line.
879 if(rd_meta_is_broken(fp
)){
882 "metadata file is broken, creating new one: %s\n",
883 metafile
? metafile
: "?"));
885 /* Make it size zero so we won't copy any bad stuff */
886 if(fp_file_size(fp
) != 0){
890 our_unlink(metafile
);
892 if((fd
= our_open(metafile
, O_TRUNC
|O_WRONLY
|O_CREAT
|O_BINARY
, 0600)) < 0){
893 q_status_message2(SM_ORDER
, 3, 5,
894 _("can't create metadata file %s, continuing (%s)"),
895 metafile
, error_description(errno
));
897 "can't create metadata file %s: %s\n",
898 metafile
? metafile
: "?",
899 error_description(errno
)));
900 fs_give((void **)&rab
);
901 fs_give((void **)&metafile
);
910 fs_give((void **)&metafile
);
914 fs_give((void **)&metafile
);
916 /* Look for our line */
917 while((p
= fgets(line
, sizeof(line
), fp
)) != NULL
)
918 if(strncmp(line
, key
, strlen(key
)) == 0 && line
[strlen(key
)] == TAB
)
921 #define SKIP_TO_TAB(p) do{while(*p && *p != TAB)p++;}while(0)
924 * The line should be TAB-separated with fields:
925 * folder_name cache_file uidvalidity uidnext uid nmsgs read_status date
926 * This part is highly RemImap-specific right now.
928 if(p
){ /* Found the line, parse it. */
929 SKIP_TO_TAB(p
); /* skip to TAB following folder_name */
931 q
= ++p
; /* q points to cache_file */
932 SKIP_TO_TAB(p
); /* skip to TAB following cache_file */
935 rab
->local_cache_file
= cpystr(q
);
936 q
= ++p
; /* q points to uidvalidity */
937 SKIP_TO_TAB(p
); /* skip to TAB following uidvalidity */
940 rab
->uidvalidity
= strtoul(q
,(char **)NULL
,10);
941 q
= ++p
; /* q points to uidnext */
942 SKIP_TO_TAB(p
); /* skip to TAB following uidnext */
945 rab
->uidnext
= strtoul(q
,(char **)NULL
,10);
946 q
= ++p
; /* q points to uid */
947 SKIP_TO_TAB(p
); /* skip to TAB following uid */
950 rab
->uid
= strtoul(q
,(char **)NULL
,10);
951 q
= ++p
; /* q points to nmsgs */
952 SKIP_TO_TAB(p
); /* skip to TAB following nmsgs */
955 rab
->nmsgs
= strtoul(q
,(char **)NULL
,10);
956 q
= ++p
; /* q points to read_status */
957 SKIP_TO_TAB(p
); /* skip to TAB following read_status */
960 rab
->read_status
= *q
; /* just a char, not whole string */
961 q
= ++p
; /* q points to date */
962 while(*p
&& *p
!= '\n' && *p
!= '\r') /* skip to newline */
966 rab
->date
= cpystr(q
);
982 * Returns: -1 if this doesn't look like a metafile or some error,
983 * or if it looks like a non-current metafile.
984 * 0 if it looks like a current metafile.
986 * A side effect is that the file pointer will be pointing to the second
987 * line if 0 is returned.
990 rd_meta_is_broken(FILE *fp
)
992 char buf
[MAILTMPLEN
];
994 if(fp
== (FILE *)NULL
)
997 if(fp_file_size(fp
) <
998 (long)(SIZEOF_PMAGIC
+ SIZEOF_SPACE
+ SIZEOF_VERSION_NUM
))
1001 if(fseek(fp
, (long)TO_FIND_HDR_PMAGIC
, 0))
1004 /* check for magic */
1005 if(fread(buf
, sizeof(char), (unsigned)SIZEOF_PMAGIC
, fp
) != SIZEOF_PMAGIC
)
1008 buf
[SIZEOF_PMAGIC
] = '\0';
1009 if(strcmp(buf
, PMAGIC
) != 0)
1013 * If we change to a new version, we may want to add code here to convert
1014 * or to cleanup after the old version. For example, it might just
1015 * remove the old cache files here.
1018 /* check for matching version number */
1019 if(fseek(fp
, (long)TO_FIND_VERSION_NUM
, 0))
1022 if(fread(buf
, sizeof(char), (unsigned)SIZEOF_VERSION_NUM
, fp
) !=
1026 buf
[SIZEOF_VERSION_NUM
] = '\0';
1027 if(strcmp(buf
, METAFILE_VERSION_NUM
) != 0)
1030 /* Position file pointer to second line */
1031 if(fseek(fp
, (long)TO_FIND_HDR_PMAGIC
, 0))
1034 if(fgets(buf
, sizeof(buf
), fp
) == NULL
)
1042 * Open a data stream to the remote data.
1045 rd_open_remote(REMDATA_S
*rd
)
1047 long openmode
= SP_USEPOOL
| SP_TEMPUSE
;
1049 dprint((7, "rd_open_remote \"%s\"\n",
1050 (rd
&& rd
->rn
) ? rd
->rn
: "?"));
1055 if(rd_stream_exists(rd
)){
1056 rd
->last_use
= get_adj_time();
1063 if(rd
->access
== ReadOnly
)
1064 openmode
|= OP_READONLY
;
1066 ps_global
->noshow_error
= 1;
1067 rd
->t
.i
.stream
= context_open(NULL
, NULL
, rd
->rn
, openmode
, NULL
);
1068 ps_global
->noshow_error
= 0;
1070 /* Don't try to reopen if there was a problem (auth failure, etc.) */
1072 rd
->flags
|= USER_SAID_NO
; /* Caution: overloading USER_SAID_NO */
1074 if(rd
->t
.i
.stream
&& rd
->t
.i
.stream
->halfopen
){
1075 /* this is a failure */
1076 rd_close_remote(rd
);
1080 rd
->last_use
= get_adj_time();
1085 q_status_message(SM_ORDER
, 3, 5, "rd_open_remote: type not supported");
1092 * Close a data stream to the remote data.
1095 rd_close_remote(REMDATA_S
*rd
)
1097 if(!rd
|| !rd_stream_exists(rd
))
1102 ps_global
->noshow_error
= 1;
1103 pine_mail_close(rd
->t
.i
.stream
);
1104 rd
->t
.i
.stream
= NULL
;
1105 ps_global
->noshow_error
= 0;
1109 q_status_message(SM_ORDER
, 3, 5, "rd_close_remote: type not supported");
1116 rd_stream_exists(REMDATA_S
*rd
)
1123 return(rd
->t
.i
.stream
? 1 : 0);
1126 q_status_message(SM_ORDER
, 3,5, "rd_stream_exists: type not supported");
1135 * Ping the stream and return 1 if it is still alive.
1138 rd_ping_stream(REMDATA_S
*rd
)
1142 dprint((7, "rd_ping_stream \"%s\"\n",
1143 (rd
&& rd
->rn
) ? rd
->rn
: "?"));
1148 if(!rd_stream_exists(rd
)){
1152 * If this stream is already actually open, officially open
1155 if(sp_stream_get(rd
->rn
, SP_MATCH
)){
1156 long openflags
= SP_USEPOOL
| SP_TEMPUSE
;
1158 if(rd
->access
== ReadOnly
)
1159 openflags
|= OP_READONLY
;
1161 rd
->t
.i
.stream
= pine_mail_open(NULL
, rd
->rn
, openflags
, NULL
);
1171 if(!rd_stream_exists(rd
))
1176 ps_global
->noshow_error
= 1;
1177 if(pine_mail_ping(rd
->t
.i
.stream
))
1180 rd
->t
.i
.stream
= NULL
;
1182 ps_global
->noshow_error
= 0;
1186 q_status_message(SM_ORDER
, 3, 5, "rd_ping_stream: type not supported");
1195 * Change readonly access to readwrite if appropriate. Call this if
1196 * the remote data ought to be readwrite but it is marked readonly in
1200 rd_check_readonly_access(REMDATA_S
*rd
)
1202 if(rd
&& rd
->read_status
== 'R'){
1206 if(rd
->t
.i
.stream
&& !rd
->t
.i
.stream
->rdonly
){
1207 rd
->read_status
= 'W';
1208 rd
->access
= ReadWrite
;
1214 q_status_message(SM_ORDER
, 3, 5,
1215 "rd_check_readonly_access: type not supported");
1223 * Initialize remote data. The remote data is initialized
1224 * with no data. On success, the local file will be empty and the remote
1225 * folder (if type RemImap) will contain a header message and an empty
1227 * The remote folder already exists before we get here, though it may be empty.
1229 * Returns -1 on failure
1233 rd_init_remote(REMDATA_S
*rd
, int add_only_first_msg
)
1238 dprint((7, "rd_init_remote \"%s\"\n",
1239 (rd
&& rd
->rn
) ? rd
->rn
: "?"));
1241 if(rd
&& rd
->type
!= RemImap
){
1242 dprint((1, "rd_init_remote: type not supported\n"));
1247 * The rest is currently type RemImap-specific.
1250 if(!(rd
->flags
& NO_FILE
|| rd
->lf
) ||
1251 !rd
->rn
|| !rd
->so
|| !rd
->t
.i
.stream
||
1252 !rd
->t
.i
.special_hdr
){
1254 "rd_init_remote: Unexpected error: %s is NULL\n",
1255 !(rd
->flags
& NO_FILE
|| rd
->lf
) ? "localfile" :
1256 !rd
->rn
? "remotename" :
1258 !rd
->t
.i
.stream
? "stream" :
1259 !rd
->t
.i
.special_hdr
? "special_hdr" : "?"));
1263 /* already init'd */
1264 if(rd
->t
.i
.stream
->nmsgs
>= 2 ||
1265 (rd
->t
.i
.stream
->nmsgs
>= 1 && add_only_first_msg
))
1268 dprint((7, " - rd_init_remote(%s): %s\n",
1269 rd
->t
.i
.special_hdr
? rd
->t
.i
.special_hdr
: "?",
1270 rd
->rn
? rd
->rn
: "?"));
1273 * We want to protect the user who specifies an actual address book
1274 * file on the remote system as a remote address book. For example,
1275 * they might say address-book={host}.addressbook where .addressbook
1276 * is just a file on host. Remote address books are folders, not
1277 * files. We also want to protect the user who specifies an existing
1278 * mail folder as a remote address book. We do that by requiring the
1279 * first message in the folder to be our header message.
1282 if(rd
->t
.i
.stream
->rdonly
){
1283 q_status_message1(SM_ORDER
| SM_DING
, 7, 10,
1284 _("Can't initialize folder \"%s\" (write permission)"), rd
->rn
);
1285 if(rd
->t
.i
.stream
->nmsgs
> 0)
1286 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
1287 _("Choose a new, unused folder for the remote data"));
1290 "Can't initialize remote folder \"%s\"\n",
1291 rd
->rn
? rd
->rn
: "?"));
1293 " No write permission for that remote folder.\n"));
1295 " Choose a new unused folder for the remote data.\n"));
1300 if(rd
->t
.i
.stream
->nmsgs
== 0){
1303 we_cancel
= busy_cue(_("Initializing remote data"), NULL
, 1);
1306 * The first message in a remote data folder is a special
1307 * header message. It is there as a way to explain what the
1308 * folder is to users who open it trying to read it. It is also
1309 * used as a consistency check so that we don't use a folder
1310 * that was being used for something else as a remote
1313 err
= rd_add_hdr_msg(rd
, date
);
1314 if(rd
->t
.i
.stream
->nmsgs
== 0)
1316 if(rd
->t
.i
.stream
&& rd
->t
.i
.stream
->nmsgs
== 0)
1317 pine_mail_check(rd
->t
.i
.stream
);
1320 cancel_busy_cue(-1);
1325 err
= rd_chk_for_hdr_msg(&(rd
->t
.i
.stream
), rd
, &eptr
);
1327 q_status_message1(SM_ORDER
| SM_DING
, 5, 5,
1328 _("\"%s\" has invalid format, can't initialize"), rd
->rn
);
1331 "Can't initialize remote data \"%s\"\n",
1332 rd
->rn
? rd
->rn
: "?"));
1335 q_status_message1(SM_ORDER
, 3, 5, "%s", eptr
);
1336 dprint((1, "%s\n", eptr
? eptr
: "?"));
1337 fs_give((void **)&eptr
);
1344 * Add the second (empty) message.
1346 if(!err
&& !add_only_first_msg
){
1347 char *tempfile
= NULL
;
1350 if(rd
->flags
& NO_FILE
){
1351 if(so_truncate(rd
->sonofile
, 0L) == 0)
1355 if(!(tempfile
= tempfile_in_same_dir(rd
->lf
, "a8", NULL
))){
1356 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
1357 _("Error opening temporary file: %s"),
1358 error_description(errno
));
1359 dprint((2, "init_remote: Error opening file: %s\n",
1360 error_description(errno
)));
1365 (fd
= our_open(tempfile
, O_TRUNC
|O_WRONLY
|O_CREAT
|O_BINARY
, 0600)) < 0){
1366 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1367 "Error opening temporary file %.200s: %.200s",
1368 tempfile
, error_description(errno
));
1370 "init_remote: Error opening temporary file: %s: %s\n",
1371 tempfile
? tempfile
: "?", error_description(errno
)));
1372 our_unlink(tempfile
);
1378 if(!err
&& rename_file(tempfile
, rd
->lf
) < 0){
1379 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1380 "Error creating cache file %.200s: %.200s",
1381 rd
->lf
, error_description(errno
));
1382 our_unlink(tempfile
);
1387 fs_give((void **)&tempfile
);
1391 err
= rd_update_remote(rd
, date
);
1393 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
1394 _("Error copying to remote folder: %s"),
1395 error_description(errno
));
1397 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
1398 "Creation of remote data folder failed");
1404 rd_update_metadata(rd
, date
);
1405 /* turn off out of date flag */
1406 rd
->flags
&= ~REM_OUTOFDATE
;
1409 return(err
? -1 : 0);
1414 * IMAP stream should already be open to a remote folder.
1415 * Check the first message in the folder to be sure it is the right
1416 * kind of message, not some message from some other folder.
1418 * Returns 0 if ok, < 0 if invalid format.
1422 rd_chk_for_hdr_msg(MAILSTREAM
**streamp
, REMDATA_S
*rd
, char **errmsg
)
1424 char *fields
[3], *values
[3];
1426 int tried_again
= 0;
1428 MAILSTREAM
*st
= NULL
;
1430 fields
[0] = rd
->t
.i
.special_hdr
;
1431 fields
[1] = "received";
1437 if(!streamp
|| !*streamp
){
1438 dprint((1, "rd_chk_for_hdr_msg: stream is null\n"));
1440 else if((*streamp
)->nmsgs
== 0){
1443 "rd_chk_for_hdr_msg: stream has nmsgs=0, try a ping\n"));
1444 if(!pine_mail_ping(*streamp
))
1447 if(*streamp
&& (*streamp
)->nmsgs
== 0){
1449 "rd_chk_for_hdr_msg: still looks like nmsgs=0, try a check\n"));
1450 pine_mail_check(*streamp
);
1453 if(*streamp
&& (*streamp
)->nmsgs
== 0){
1455 "rd_chk_for_hdr_msg: still nmsgs=0, try re-opening stream\n"));
1457 if(rd_stream_exists(rd
))
1458 rd_close_remote(rd
);
1461 if(rd_stream_exists(rd
))
1462 st
= rd
->t
.i
.stream
;
1468 if(st
&& st
->nmsgs
== 0){
1470 "rd_chk_for_hdr_msg: can't see header message\n"));
1476 if(st
&& st
->nmsgs
!= 0
1477 && (h
=pine_fetchheader_lines(st
, 1L, NULL
, fields
))){
1478 simple_header_parse(h
, fields
, values
);
1483 rd
->cookie
= strtoul(values
[0], (char **)NULL
, 10);
1486 else if(rd
->cookie
== 1){
1487 if(rd
->flags
& COOKIE_ONE_OK
|| tried_again
)
1492 else if(rd
->cookie
> 1)
1497 fs_give((void **)&values
[0]);
1500 fs_give((void **)&values
[1]);
1502 fs_give((void **)&h
);
1506 if(ret
&& ret
!= -6 && errmsg
){
1507 *errmsg
= (char *)fs_get(500 * sizeof(char));
1508 (*errmsg
)[0] = '\0';
1514 snprintf(*errmsg
, 500, _("Can't open remote address book \"%s\""), rd
->rn
);
1517 /* no messages in folder */
1519 snprintf(*errmsg
, 500,
1520 _("Error: no messages in remote address book \"%s\"!"),
1526 snprintf(*errmsg
, 500,
1527 "First msg in \"%s\" should have \"%s\" header",
1528 rd
->rn
, rd
->t
.i
.special_hdr
);
1531 /* Received header */
1533 snprintf(*errmsg
, 500,
1534 _("Suspicious Received headers in first msg in \"%s\""),
1542 * This is a failure and should not happen, but we're not going to
1543 * fail on this condition.
1545 dprint((1, "Unexpected value in \"%s\" header of \"%s\"\n",
1546 rd
->t
.i
.special_hdr
? rd
->t
.i
.special_hdr
: "?",
1547 rd
->rn
? rd
->rn
: "?"));
1552 "rd_chk_for_hdr_msg: cookie is 1, try to upgrade it\n"));
1554 if(rd_remote_is_readonly(rd
)){
1556 "rd_chk_for_hdr_msg: can't upgrade, readonly\n"));
1557 ret
= 0; /* stick with 1 */
1560 /* cookie is 1, upgrade it */
1561 if(rd_upgrade_cookies(rd
, st
->nmsgs
, 0) == 0){
1562 /* now check again */
1570 * This is actually a failure but we've decided that this
1577 if(errmsg
&& *errmsg
)
1578 dprint((1, "rd_chk_for_hdr_msg: %s\n", *errmsg
));
1585 * For remote data, this adds the explanatory header
1586 * message to the remote IMAP folder.
1588 * Args: rd -- Remote data handle
1589 * date -- The date string to use
1595 rd_add_hdr_msg(REMDATA_S
*rd
, char *date
)
1599 if(!rd
|| rd
->type
!= RemImap
|| !rd
->rn
|| !rd
->so
|| !rd
->t
.i
.special_hdr
){
1601 "rd_add_hdr_msg: Unexpected error: %s is NULL\n",
1603 !rd
->rn
? "remotename" :
1605 !rd
->t
.i
.special_hdr
? "special_hdr" : "?"));
1609 err
= rd_store_fake_hdrs(rd
, "Header Message for Remote Data",
1612 /* Write the dummy message */
1613 if(!strucmp(rd
->t
.i
.special_hdr
, REMOTE_ABOOK_SUBTYPE
)){
1614 if(!err
&& so_puts(rd
->so
,
1615 "This folder contains a single Alpine addressbook.\015\012") == 0)
1617 if(!err
&& so_puts(rd
->so
,
1618 "This message is just an explanatory message.\015\012") == 0)
1620 if(!err
&& so_puts(rd
->so
,
1621 "The last message in the folder is the live addressbook data.\015\012") == 0)
1623 if(!err
&& so_puts(rd
->so
,
1624 "The rest of the messages contain previous revisions of the addressbook data.\015\012") == 0)
1626 if(!err
&& so_puts(rd
->so
,
1627 "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1629 if(!err
&& so_puts(rd
->so
,
1630 "which come after it.\015\012") == 0)
1633 else if(!strucmp(rd
->t
.i
.special_hdr
, REMOTE_PINERC_SUBTYPE
)){
1634 if(!err
&& so_puts(rd
->so
,
1635 "This folder contains a Alpine config file.\015\012") == 0)
1637 if(!err
&& so_puts(rd
->so
,
1638 "This message is just an explanatory message.\015\012") == 0)
1640 if(!err
&& so_puts(rd
->so
,
1641 "The last message in the folder is the live config data.\015\012") == 0)
1643 if(!err
&& so_puts(rd
->so
,
1644 "The rest of the messages contain previous revisions of the data.\015\012") == 0)
1646 if(!err
&& so_puts(rd
->so
,
1647 "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1649 if(!err
&& so_puts(rd
->so
,
1650 "which come after it.\015\012") == 0)
1654 if(!err
&& so_puts(rd
->so
,
1655 "This folder contains remote Alpine data.\015\012") == 0)
1657 if(!err
&& so_puts(rd
->so
,
1658 "This message is just an explanatory message.\015\012") == 0)
1660 if(!err
&& so_puts(rd
->so
,
1661 "The last message in the folder is the live data.\015\012") == 0)
1663 if(!err
&& so_puts(rd
->so
,
1664 "The rest of the messages contain previous revisions of the data.\015\012") == 0)
1666 if(!err
&& so_puts(rd
->so
,
1667 "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1669 if(!err
&& so_puts(rd
->so
,
1670 "which come after it.\015\012") == 0)
1674 /* Take the message from "so" to the remote folder */
1678 if((st
= rd
->t
.i
.stream
) != NULL
)
1679 rd
->t
.i
.shouldbe_nmsgs
= rd
->t
.i
.stream
->nmsgs
+ 1;
1681 st
= adrbk_handy_stream(rd
->rn
);
1683 err
= write_fcc(rd
->rn
, NULL
, rd
->so
, st
, "remote data", NULL
) ? 0 : -1;
1691 * Write some fake header lines into storage object rd->so.
1694 * subject -- subject to put in header
1695 * subtype -- subtype to put in header
1696 * date -- date to put in header
1699 rd_store_fake_hdrs(REMDATA_S
*rd
, char *subject
, char *subtype
, char *date
)
1706 unsigned long r
= 0L;
1709 if(!rd
|| rd
->type
!= RemImap
|| !rd
->so
|| !rd
->t
.i
.special_hdr
)
1712 fake_env
= (ENVELOPE
*)fs_get(sizeof(ENVELOPE
));
1713 memset(fake_env
, 0, sizeof(ENVELOPE
));
1714 fake_body
= (BODY
*)fs_get(sizeof(BODY
));
1715 memset(fake_body
, 0, sizeof(BODY
));
1716 fake_from
= (ADDRESS
*)fs_get(sizeof(ADDRESS
));
1717 memset(fake_from
, 0, sizeof(ADDRESS
));
1719 fake_env
->subject
= cpystr(subject
);
1720 fake_env
->date
= (unsigned char *) cpystr(date
);
1721 fake_from
->personal
= cpystr("Pine Remote Data");
1722 fake_from
->mailbox
= cpystr("nobody");
1723 fake_from
->host
= cpystr("nowhere");
1724 fake_env
->from
= fake_from
;
1725 fake_body
->type
= REMOTE_DATA_TYPE
;
1726 fake_body
->subtype
= cpystr(subtype
);
1727 set_parameter(&fake_body
->parameter
, "charset", "UTF-8");
1735 for(i
= 100; i
> 0 && r
< 1000000; i
--)
1739 r
= 1712836L + getpid();
1744 snprintf(vers
, sizeof(vers
), "%ld", r
);
1748 rbuf
.f
= dummy_soutr
;
1752 rbuf
.end
= p
+SIZEOF_20KBUF
-1;
1753 rfc822_output_header_line(&rbuf
, rd
->t
.i
.special_hdr
, 0L, vers
);
1754 rfc822_output_header(&rbuf
, fake_env
, fake_body
, NULL
, 0L);
1757 mail_free_envelope(&fake_env
);
1758 mail_free_body(&fake_body
);
1760 /* Write the fake headers */
1761 if(so_puts(rd
->so
, tmp_20k_buf
) == 0)
1769 * We have discovered that the data in the remote folder is suspicious.
1770 * In some cases it is just because it is from an old version of pine.
1771 * We have decided to update the data so that it won't look suspicious
1774 * Args -- only_update_last If set, that means to just add a new last message
1775 * by calling rd_update_remote. Don't create a new
1776 * header message and delete the old header message.
1777 * nmsgs Not used if only_update_last is set
1780 rd_upgrade_cookies(REMDATA_S
*rd
, long int nmsgs
, int only_update_last
)
1786 * We need to copy the data from the last message, add a new header
1787 * message with a random cookie, add the data back in with the
1788 * new cookie, and delete the old messages.
1792 rd
->flags
|= COOKIE_ONE_OK
;
1795 * The local copy may be newer than the remote copy. We don't want to
1796 * blast the local copy in that case. The BELIEVE_CACHE flag tells us
1797 * to not do the blasting.
1799 if(rd
->flags
& BELIEVE_CACHE
)
1800 rd
->flags
&= ~BELIEVE_CACHE
;
1802 dprint((1, "rd_upgrade_cookies: copy abook data\n"));
1803 err
= rd_update_local(rd
);
1806 if(!err
&& !only_update_last
){
1807 rd
->cookie
= 0; /* causes new cookie to be generated */
1809 dprint((1, "rd_upgrade_cookies: add new hdr msg to end\n"));
1810 err
= rd_add_hdr_msg(rd
, date
);
1814 dprint((1, "rd_upgrade_cookies: copy back data\n"));
1815 err
= rd_update_remote(rd
, NULL
);
1818 rd
->flags
&= ~COOKIE_ONE_OK
;
1820 if(!err
&& !only_update_last
){
1824 * We've created a new header message and added a new copy of the
1825 * data after it. Only problem is that the new copy will have used
1826 * the original header message to get its cookie (== 1) from. We
1827 * could have deleted the original messages before the last step
1828 * to get it right but that would delete all copies of the data
1829 * temporarily. Delete now and then re-update.
1833 if(rd
->t
.i
.stream
&& rd
->t
.i
.stream
->nmsgs
>= nmsgs
+2){
1834 snprintf(sequence
, sizeof(sequence
), "1:%ld", nmsgs
);
1835 mail_flag(rd
->t
.i
.stream
, sequence
, "\\DELETED", ST_SET
);
1836 mail_expunge(rd
->t
.i
.stream
);
1837 err
= rd_update_remote(rd
, NULL
);
1846 * Copy remote data to local file. If needed, the remote data is initialized
1847 * with no data. On success, the local file contains the remote data (which
1848 * means it will be empty if we initialized).
1850 * Returns != 0 on failure
1854 rd_update_local(REMDATA_S
*rd
)
1859 int i
, we_cancel
= 0;
1862 char *tempfile
= NULL
;
1865 if(!rd
|| !(rd
->flags
& NO_FILE
|| rd
->lf
) || !rd
->rn
){
1867 "rd_update_local: Unexpected error: %s is NULL\n",
1869 !(rd
->flags
& NO_FILE
|| rd
->lf
) ? "localfile" :
1870 !rd
->rn
? "remotename" : "?"));
1875 dprint((3, " - rd_update_local(%s): %s => %s\n",
1876 rd
->type
== RemImap
? "Imap" : "?", rd
->rn
? rd
->rn
: "?",
1877 (rd
->flags
& NO_FILE
) ? "<mem>" : (rd
->lf
? rd
->lf
: "?")));
1881 if(!rd
->so
|| !rd
->t
.i
.special_hdr
){
1883 "rd_update_local: Unexpected error: %s is NULL\n",
1885 !rd
->t
.i
.special_hdr
? "special_hdr" : "?"));
1890 if(!rd_stream_exists(rd
)){
1891 if(rd
->flags
& NO_PERM_CACHE
){
1893 "rd_update_local: backtrack, remote folder does not exist yet\n"));
1897 "rd_update_local: Unexpected error: stream is NULL\n"));
1908 /* force ReadOnly */
1909 if(rd
->t
.i
.stream
->rdonly
){
1910 rd
->read_status
= 'R';
1911 rd
->access
= ReadOnly
;
1914 rd
->read_status
= 'W';
1916 if(rd
->t
.i
.stream
->nmsgs
< 2)
1917 return(rd_init_remote(rd
, 0));
1918 else if(rd_chk_for_hdr_msg(&(rd
->t
.i
.stream
), rd
, &eptr
)){
1919 q_status_message1(SM_ORDER
| SM_DING
, 5, 5,
1920 _("Can't initialize \"%s\" (invalid format)"), rd
->rn
);
1923 q_status_message1(SM_ORDER
, 3, 5, "%s", eptr
);
1924 dprint((1, "%s\n", eptr
));
1925 fs_give((void **)&eptr
);
1929 "Can't initialize remote data \"%s\"\n",
1930 rd
->rn
? rd
->rn
: "?"));
1934 we_cancel
= busy_cue(_("Copying remote data"), NULL
, 1);
1936 if(rd
->flags
& NO_FILE
){
1937 store
= rd
->sonofile
;
1938 so_truncate(store
, 0L);
1941 if(!(tempfile
= tempfile_in_same_dir(rd
->lf
, "a8", NULL
))){
1942 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
1943 _("Error opening temporary file: %s"),
1944 error_description(errno
));
1946 "rd_update_local: Error opening temporary file: %s\n",
1947 error_description(errno
)));
1951 /* Copy the data into tempfile */
1952 if((store
= so_get(FileStar
, tempfile
, WRITE_ACCESS
|OWNER_ONLY
))
1954 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1955 _("Error opening temporary file %s: %s"),
1956 tempfile
, error_description(errno
));
1958 "rd_update_local: Error opening temporary file: %s: %s\n",
1959 tempfile
? tempfile
: "?",
1960 error_description(errno
)));
1961 our_unlink(tempfile
);
1962 fs_give((void **)&tempfile
);
1968 * Copy from the last message in the folder.
1970 if(!pine_mail_fetchstructure(rd
->t
.i
.stream
, rd
->t
.i
.stream
->nmsgs
,
1972 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1973 _("Can't access remote IMAP data"));
1974 dprint((2, "Can't access remote IMAP data\n"));
1976 our_unlink(tempfile
);
1977 fs_give((void **)&tempfile
);
1980 if(!(rd
->flags
& NO_FILE
))
1984 cancel_busy_cue(-1);
1990 body
->type
!= REMOTE_DATA_TYPE
||
1992 strucmp(body
->subtype
, rd
->t
.i
.special_hdr
)){
1993 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1994 _("Remote IMAP folder has wrong contents"));
1996 "Remote IMAP folder has wrong contents\n"));
1998 our_unlink(tempfile
);
1999 fs_give((void **)&tempfile
);
2002 if(!(rd
->flags
& NO_FILE
))
2006 cancel_busy_cue(-1);
2011 if(!(env
= pine_mail_fetchenvelope(rd
->t
.i
.stream
,
2012 rd
->t
.i
.stream
->nmsgs
))){
2013 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2014 _("Can't access check date in remote data"));
2016 "Can't access check date in remote data\n"));
2018 our_unlink(tempfile
);
2019 fs_give((void **)&tempfile
);
2022 if(!(rd
->flags
& NO_FILE
))
2026 cancel_busy_cue(-1);
2031 if(rd
&& rd
->flags
& USER_SAID_YES
)
2034 chk
= rd_check_for_suspect_data(rd
);
2037 case -1: /* suspicious data, user says abort */
2039 our_unlink(tempfile
);
2040 fs_give((void **)&tempfile
);
2043 if(!(rd
->flags
& NO_FILE
))
2047 cancel_busy_cue(-1);
2051 case 1: /* suspicious data, user says go ahead */
2052 if(rd_remote_is_readonly(rd
)){
2054 "rd_update_local: can't upgrade, readonly\n"));
2057 /* attempt to upgrade cookie in last message */
2058 (void)rd_upgrade_cookies(rd
, 0, 1);
2062 case 0: /* all is ok */
2068 /* store may have been written to at this point, so we'll clear it out */
2069 so_truncate(store
, 0L);
2071 gf_set_so_writec(&pc
, store
);
2073 error
= detach(rd
->t
.i
.stream
, rd
->t
.i
.stream
->nmsgs
, "1", 0L,
2074 NULL
, pc
, NULL
, DT_NODFILTER
);
2076 gf_clear_so_writec(store
);
2080 cancel_busy_cue(-1);
2082 if(!(rd
->flags
& NO_FILE
)){
2083 if(so_give(&store
)){
2084 snprintf(ebuf
, sizeof(ebuf
), _("Error writing temp file: %s"),
2085 error_description(errno
));
2091 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
2092 _("%s: Error copying remote IMAP data"), error
);
2093 dprint((2, "rd_update_local: Error copying: %s\n",
2094 error
? error
: "?"));
2096 our_unlink(tempfile
);
2097 fs_give((void **)&tempfile
);
2103 if(tempfile
&& (i
= rename_file(tempfile
, rd
->lf
)) < 0){
2106 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
2107 _("Error updating local file: %s: %s"),
2108 rd
->lf
, error_description(errno
));
2109 q_status_message(SM_ORDER
, 3, 4,
2110 _("Perhaps another process has the file open?"));
2112 "Rename_file(%s,%s): %s: returned -5, another process has file open?\n",
2113 tempfile
? tempfile
: "?",
2114 rd
->lf
? rd
->lf
: "?",
2115 error_description(errno
)));
2118 #endif /* _WINDOWS */
2120 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
2121 _("Error updating cache file %s: %s"),
2122 rd
->lf
, error_description(errno
));
2124 "Error updating cache file %s: rename(%s,%s): %s\n",
2125 tempfile
? tempfile
: "?",
2126 tempfile
? tempfile
: "?",
2127 rd
->lf
? rd
->lf
: "?",
2128 error_description(errno
)));
2131 our_unlink(tempfile
);
2132 fs_give((void **)&tempfile
);
2137 "in rd_update_local, setting chk_date to ->%s<-\n",
2138 env
->date
? (char *)env
->date
: "?"));
2139 rd_update_metadata(rd
, (char *) env
->date
);
2141 /* turn off out of date flag */
2142 rd
->flags
&= ~REM_OUTOFDATE
;
2145 fs_give((void **)&tempfile
);
2150 q_status_message1(SM_ORDER
| SM_DING
, 5, 5,
2151 _("Can't open remote IMAP folder \"%s\""), rd
->rn
);
2153 "Can't open remote IMAP folder \"%s\"\n",
2154 rd
->rn
? rd
->rn
: "?"));
2155 rd
->access
= ReadOnly
;
2162 dprint((1, "rd_update_local: Unsupported type\n"));
2169 * Copy local data to remote folder.
2171 * Args lf -- Local file name
2173 * header_to_check -- Name of indicator header in remote folder
2174 * imapstuff -- Structure holding info about connection
2175 * returndate -- Pointer to the date that was stored in the remote folder
2177 * Returns !=0 on failure
2181 rd_update_remote(REMDATA_S
*rd
, char *returndate
)
2185 long openmode
= SP_USEPOOL
| SP_TEMPUSE
;
2189 if(rd
&& rd
->type
!= RemImap
){
2190 dprint((1, "rd_update_remote: type not supported\n"));
2194 if(!rd
|| !(rd
->flags
& NO_FILE
|| rd
->lf
) || !rd
->rn
||
2195 !rd
->so
|| !rd
->t
.i
.special_hdr
){
2197 "rd_update_remote: Unexpected error: %s is NULL\n",
2199 !(rd
->flags
& NO_FILE
|| rd
->lf
) ? "localfile" :
2200 !rd
->rn
? "remotename" :
2202 !rd
->t
.i
.special_hdr
? "special_hdr" : "?"));
2206 dprint((7, " - rd_update_remote(%s): %s => %s\n",
2207 rd
->t
.i
.special_hdr
? rd
->t
.i
.special_hdr
: "?",
2208 rd
->lf
? rd
->lf
: "<mem>",
2209 rd
->rn
? rd
->rn
: "?"));
2211 if(!(st
= rd
->t
.i
.stream
))
2212 st
= adrbk_handy_stream(rd
->rn
);
2215 st
= rd
->t
.i
.stream
= context_open(NULL
, NULL
, rd
->rn
, openmode
, NULL
);
2218 q_status_message1(SM_ORDER
| SM_DING
, 5, 5,
2219 _("Can't open \"%s\" for copying"), rd
->rn
);
2221 "rd_update_remote: Can't open remote folder \"%s\" for copying\n",
2222 rd
->rn
? rd
->rn
: "?"));
2226 rd
->last_use
= get_adj_time();
2227 err
= rd_chk_for_hdr_msg(&st
, rd
, &eptr
);
2229 q_status_message1(SM_ORDER
| SM_DING
, 5, 5,
2230 _("\"%s\" has invalid format"), rd
->rn
);
2233 q_status_message1(SM_ORDER
, 3, 5, "%s", eptr
);
2234 dprint((1, "%s\n", eptr
));
2235 fs_give((void **)&eptr
);
2239 "rd_update_remote: \"%s\" has invalid format\n",
2240 rd
->rn
? rd
->rn
: "?"));
2247 * The data that will be going to the remote folder rn is
2248 * written into the following storage object and then copied to
2249 * the remote folder from there.
2252 if(rd
->flags
& NO_FILE
){
2253 store
= rd
->sonofile
;
2255 so_seek(store
, 0L, 0); /* rewind */
2258 store
= so_get(FileStar
, rd
->lf
, READ_ACCESS
);
2263 unsigned char last_c
= 0;
2265 /* Reset the storage object, since we may have already used it. */
2266 if(so_truncate(rd
->so
, 0L) == 0)
2271 "in rd_update_remote, storing date ->%s<-\n",
2272 date
? date
: "?"));
2273 if(!err
&& rd_store_fake_hdrs(rd
, "Pine Remote Data Container",
2274 rd
->t
.i
.special_hdr
, date
))
2277 /* save the date for later comparisons */
2278 if(!err
&& returndate
)
2279 strncpy(returndate
, date
, 100);
2281 /* Write the data */
2282 while(!err
&& so_readc(&c
, store
)){
2284 * C-client expects CRLF-terminated lines. Convert them
2285 * as we copy into c-client. Read ahead isn't available.
2286 * Leave CRLF as is, convert LF to CRLF, leave CR as is.
2288 if(last_c
!= '\r' && c
== '\n'){
2289 /* Convert \n to CRFL */
2290 if(so_writec('\r', rd
->so
) == 0 || so_writec('\n', rd
->so
) == 0)
2297 if(so_writec((int) c
, rd
->so
) == 0)
2303 * Take that message from so to the remote folder.
2304 * We append to that folder and always
2305 * use the final message as the active data.
2310 if((st
= rd
->t
.i
.stream
) != NULL
)
2311 rd
->t
.i
.shouldbe_nmsgs
= rd
->t
.i
.stream
->nmsgs
+ 1;
2313 st
= adrbk_handy_stream(rd
->rn
);
2315 err
= write_fcc(rd
->rn
, NULL
, rd
->so
, st
,
2316 "remote data", NULL
) ? 0 : 1;
2320 if(!(rd
->flags
& NO_FILE
))
2327 dprint((2, "error in rd_update_remote for %s => %s\n",
2328 rd
->lf
? rd
->lf
: "<mem>", rd
->rn
? rd
->rn
: "?"));
2335 * Check to see if the remote data has changed since we cached it.
2337 * Args rd -- REMDATA handle
2338 * do_it_now -- If > 0, check now regardless
2339 * If = 0, check if time since last chk more than default
2340 * If < 0, check if time since last chk more than -do_it_now
2343 rd_check_remvalid(REMDATA_S
*rd
, long int do_it_now
)
2345 time_t chk_interval
;
2346 long openmode
= SP_USEPOOL
| SP_TEMPUSE
;
2347 MAILSTREAM
*stat_stream
= NULL
;
2348 int we_cancel
= 0, got_cmsgs
= 0;
2349 unsigned long current_nmsgs
;
2351 dprint((7, "- rd_check_remvalid(%s) -\n",
2352 (rd
&& rd
->rn
) ? rd
->rn
: ""));
2354 if(rd
&& rd
->type
!= RemImap
){
2355 dprint((1, "rd_check_remvalid: type not supported\n"));
2359 if(!rd
|| rd
->flags
& REM_OUTOFDATE
|| rd
->flags
& USE_OLD_CACHE
)
2362 if(!rd
->t
.i
.chk_date
){
2364 "rd_check_remvalid: remote data %s changed (chk_date)\n",
2366 rd
->flags
|= REM_OUTOFDATE
;
2370 if(rd
->t
.i
.chk_nmsgs
<= 1){
2372 "rd_check_remvalid: remote data %s changed (chk_nmsgs <= 1)\n",
2373 rd
->rn
? rd
->rn
: "?"));
2374 rd
->flags
|= REM_OUTOFDATE
;
2379 chk_interval
= -1L * do_it_now
;
2383 chk_interval
= ps_global
->remote_abook_validity
* 60L;
2385 /* too soon to check again */
2387 (chk_interval
== 0L ||
2388 get_adj_time() <= rd
->last_valid_chk
+ chk_interval
))
2391 if(rd
->access
== ReadOnly
)
2392 openmode
|= OP_READONLY
;
2394 rd
->last_valid_chk
= get_adj_time();
2395 mm_status_result
.flags
= 0L;
2397 /* make sure the cache file is still there */
2398 if(rd
->lf
&& can_access(rd
->lf
, READ_ACCESS
) != 0){
2400 "rd_check_remvalid: %s: cache file %s disappeared\n",
2401 rd
->rn
? rd
->rn
: "?",
2402 rd
->lf
? rd
->lf
: "?"));
2403 rd
->flags
|= REM_OUTOFDATE
;
2408 * If the stream is open we should check there instead of using
2409 * a STATUS command. Check to see if it is really still alive with the
2410 * ping. It would be convenient if we could use a status command
2411 * on the open stream but apparently that won't work everywhere.
2415 try_looking_in_stream
:
2418 * Get the current number of messages in the folder to
2419 * compare with our saved number of messages.
2423 dprint((7, "using open remote data stream\n"));
2424 rd
->last_use
= get_adj_time();
2425 current_nmsgs
= rd
->t
.i
.stream
->nmsgs
;
2431 * Try to use the imap status command
2432 * to get the information as cheaply as possible.
2433 * If NO_STATUSCMD is set we just bypass all this stuff.
2436 if(!(rd
->flags
& NO_STATUSCMD
))
2437 stat_stream
= adrbk_handy_stream(rd
->rn
);
2440 * If we don't have a stream to use for the status command we
2441 * skip it and open the folder instead. Then, next time we want to
2442 * check we'll probably be able to use that open stream instead of
2443 * opening another one each time for the status command.
2446 if(!LEVELSTATUS(stat_stream
)){
2447 rd
->flags
|= NO_STATUSCMD
;
2449 "rd_check_remvalid: remote data %s: server doesn't support status\n",
2450 rd
->rn
? rd
->rn
: "?"));
2454 * This sure seems like a crock. We have to check to
2455 * see if the stream is actually open to the folder
2456 * we want to do the status on because c-client can't
2457 * do a status on an open folder. In this case, we fake
2458 * the status command results ourselves.
2459 * If we're so unlucky as to get back a stream that will
2460 * work for the status command while we also have another
2461 * stream that is rd->rn and we don't pick up on that,
2464 if(same_stream_and_mailbox(rd
->rn
, stat_stream
)){
2466 "rd_check_remvalid: faking status\n"));
2467 mm_status_result
.flags
= SA_MESSAGES
| SA_UIDVALIDITY
2469 mm_status_result
.messages
= stat_stream
->nmsgs
;
2470 mm_status_result
.uidvalidity
= stat_stream
->uid_validity
;
2471 mm_status_result
.uidnext
= stat_stream
->uid_last
+1;
2476 "rd_check_remvalid: trying status\n"));
2477 ps_global
->noshow_error
= 1;
2478 if(!pine_mail_status(stat_stream
, rd
->rn
,
2479 SA_UIDVALIDITY
| SA_UIDNEXT
| SA_MESSAGES
)){
2480 /* failed, mark it so we won't try again */
2481 rd
->flags
|= NO_STATUSCMD
;
2483 "rd_check_remvalid: addrbook %s: status command failed\n",
2484 rd
->rn
? rd
->rn
: "?"));
2485 mm_status_result
.flags
= 0L;
2489 ps_global
->noshow_error
= 0;
2493 /* if we got back a result from the status command, use it */
2494 if(mm_status_result
.flags
){
2496 "rd_check_remvalid: got status_result 0x%x\n",
2497 mm_status_result
.flags
));
2498 if(mm_status_result
.flags
& SA_MESSAGES
){
2499 current_nmsgs
= mm_status_result
.messages
;
2506 * Check current_nmsgs versus what we think it should be.
2507 * If they're different we know things have changed and we can
2508 * return now. If they're the same we don't know.
2510 if(got_cmsgs
&& current_nmsgs
!= rd
->t
.i
.chk_nmsgs
){
2511 rd
->flags
|= REM_OUTOFDATE
;
2513 "rd_check_remvalid: remote data %s changed (current msgs (%ld) != chk_nmsgs (%ld))\n",
2514 rd
->rn
? rd
->rn
: "?", current_nmsgs
, rd
->t
.i
.chk_nmsgs
));
2519 * Get the current uidvalidity and uidnext values from the
2520 * folder to compare with our saved values.
2523 if(rd
->t
.i
.stream
->uid_validity
== rd
->t
.i
.uidvalidity
){
2525 * Uid is valid so we just have to check whether or not the
2526 * uid of the last message has changed or not and return.
2528 if(mail_uid(rd
->t
.i
.stream
, rd
->t
.i
.stream
->nmsgs
) != rd
->t
.i
.uid
){
2529 /* uid has changed so we're out of date */
2530 rd
->flags
|= REM_OUTOFDATE
;
2532 "rd_check_remvalid: remote data %s changed (uid)\n",
2533 rd
->rn
? rd
->rn
: "?"));
2536 dprint((7,"rd_check_remvalid: uids match\n"));
2543 * If the uidvalidity changed that probably means it can't
2544 * be relied on to be meaningful, so don't use it in the future.
2546 rd
->flags
|= NO_STATUSCMD
;
2548 "rd_check_remvalid: remote data %s uidvalidity changed, don't use uid\n",
2549 rd
->rn
? rd
->rn
: "?"));
2552 else{ /* stream not open, use status results */
2553 if(mm_status_result
.flags
& SA_UIDVALIDITY
&&
2554 mm_status_result
.flags
& SA_UIDNEXT
&&
2555 mm_status_result
.uidvalidity
== rd
->t
.i
.uidvalidity
){
2561 if(mm_status_result
.uidnext
== rd
->t
.i
.uidnext
){
2563 * Uidnext valid and unchanged, so the folder is unchanged.
2565 dprint((7, "rd_check_remvalid: uidnexts match\n"));
2568 else{ /* uidnext changed, folder _may_ have changed */
2571 "rd_check_remvalid: uidnexts don't match, ours=%lu status=%lu\n",
2572 rd
->t
.i
.uidnext
, mm_status_result
.uidnext
));
2575 * Since c-client can't handle a status cmd on the selected
2576 * mailbox, we may have had to guess at the value of uidnext,
2577 * and we don't know for sure that this is a real change.
2578 * We need to open the mailbox and find out for sure.
2580 we_cancel
= busy_cue(NULL
, NULL
, 1);
2581 ps_global
->noshow_error
= 1;
2582 if((rd
->t
.i
.stream
= context_open(NULL
, NULL
, rd
->rn
, openmode
,
2584 imapuid_t last_msg_uid
;
2586 if(rd
->t
.i
.stream
->rdonly
)
2587 rd
->read_status
= 'R';
2589 last_msg_uid
= mail_uid(rd
->t
.i
.stream
,
2590 rd
->t
.i
.stream
->nmsgs
);
2591 ps_global
->noshow_error
= 0;
2592 rd
->last_use
= get_adj_time();
2594 "%s: opened to check uid (%ld)\n",
2595 rd
->rn
? rd
->rn
: "?", (long)rd
->last_use
));
2596 if(last_msg_uid
!= rd
->t
.i
.uid
){ /* really did change */
2597 rd
->flags
|= REM_OUTOFDATE
;
2599 "rd_check_remvalid: remote data %s changed, our uid=%lu real uid=%lu\n",
2600 rd
->rn
? rd
->rn
: "?",
2601 rd
->t
.i
.uid
, last_msg_uid
));
2603 else{ /* false hit */
2605 * The uid of the last message is the same as we
2606 * remembered, so the problem is that our guess
2607 * for the nextuid was wrong. It didn't actually
2608 * change. Since we know the correct uidnext now we
2609 * can reset that guess to the correct value for
2610 * next time, avoiding this extra mail_open.
2613 "rd_check_remvalid: remote data %s false change: adjusting uidnext from %lu to %lu\n",
2614 rd
->rn
? rd
->rn
: "?",
2616 mm_status_result
.uidnext
));
2617 rd
->t
.i
.uidnext
= mm_status_result
.uidnext
;
2618 rd_write_metadata(rd
, 0);
2622 cancel_busy_cue(-1);
2627 ps_global
->noshow_error
= 0;
2629 "rd_check_remvalid: couldn't open %s\n",
2630 rd
->rn
? rd
->rn
: "?"));
2636 * If the status command doesn't return these or
2637 * if the uidvalidity is changing that probably means it can't
2638 * be relied on to be meaningful, so don't use it.
2640 * We also come here if we don't have a stat_stream handy to
2641 * look up the status. This happens, for example, if our
2642 * address book is on a different server from the open mail
2643 * folders. In that case, instead of opening a stream,
2644 * doing a status command, and closing the stream, we open
2645 * the stream and use it to check next time, too.
2648 rd
->flags
|= NO_STATUSCMD
;
2650 "rd_check_remvalid: remote data %s don't use status\n",
2651 rd
->rn
? rd
->rn
: "?"));
2655 "opening remote data stream for validity check\n"));
2656 we_cancel
= busy_cue(NULL
, NULL
, 1);
2657 ps_global
->noshow_error
= 1;
2658 rd
->t
.i
.stream
= context_open(NULL
, NULL
, rd
->rn
, openmode
,
2660 ps_global
->noshow_error
= 0;
2662 cancel_busy_cue(-1);
2666 goto try_looking_in_stream
;
2669 "rd_check_remvalid: cannot open remote mailbox\n"));
2676 * If we got here that means that we still don't know if the folder
2677 * changed or not. If the stream is open then it must be the case that
2678 * uidvalidity changed so we can't rely on using uids. If the stream
2679 * isn't open, then either the status command didn't work or the
2680 * uidvalidity changed. In any case, we need to fall back to looking
2681 * inside the folder at the last message and checking whether or not the
2682 * Date of the last message is the one we remembered.
2686 "rd_check_remvalid: falling back to Date check\n"));
2689 * Fall back to looking in the folder at the Date header.
2693 we_cancel
= busy_cue(NULL
, NULL
, 1);
2695 ps_global
->noshow_error
= 1;
2696 if(rd
->t
.i
.stream
||
2697 (rd
->t
.i
.stream
= context_open(NULL
,NULL
,rd
->rn
,openmode
,NULL
))){
2698 ENVELOPE
*env
= NULL
;
2700 if(rd
->t
.i
.stream
->rdonly
)
2701 rd
->read_status
= 'R';
2703 if(rd
->t
.i
.stream
->nmsgs
!= rd
->t
.i
.chk_nmsgs
){
2704 rd
->flags
|= REM_OUTOFDATE
;
2706 "rd_check_remvalid: remote data %s changed (expected nmsgs %ld, got %ld)\n",
2707 rd
->rn
? rd
->rn
: "?",
2708 rd
->t
.i
.chk_nmsgs
, rd
->t
.i
.stream
->nmsgs
));
2710 else if(rd
->t
.i
.stream
->nmsgs
> 1){
2711 env
= pine_mail_fetchenvelope(rd
->t
.i
.stream
,rd
->t
.i
.stream
->nmsgs
);
2713 if(!env
|| (env
->date
&& strucmp((char *) env
->date
, rd
->t
.i
.chk_date
))){
2714 rd
->flags
|= REM_OUTOFDATE
;
2716 "rd_check_remvalid: remote data %s changed (%s)\n",
2717 rd
->rn
? rd
->rn
: "?", env
? "date" : "not enough msgs"));
2721 rd
->last_use
= get_adj_time();
2724 "%s: got envelope to check date (%ld)\n",
2725 rd
->rn
? rd
->rn
: "?", (long)rd
->last_use
));
2727 /* else, we give up and go with the cache copy */
2729 ps_global
->noshow_error
= 0;
2732 cancel_busy_cue(-1);
2734 dprint((7, "rd_check_remvalid: falling off end\n"));
2739 * Returns nonzero if remote data is currently readonly.
2741 * Returns 0 -- apparently writeable
2742 * 1 -- stream not open
2743 * 2 -- stream open but not writeable
2746 rd_remote_is_readonly(REMDATA_S
*rd
)
2748 if(!rd
|| rd
->access
== ReadOnly
|| !rd_stream_exists(rd
))
2753 if(rd
->t
.i
.stream
->rdonly
)
2759 q_status_message(SM_ORDER
, 3, 5,
2760 "rd_remote_is_readonly: type not supported");
2770 * -1 if not ok and user says No
2771 * 1 if not ok but user says Yes, ok to use it
2774 rd_check_for_suspect_data(REMDATA_S
*rd
)
2777 char *fields
[3], *values
[3], *h
;
2778 unsigned long cookie
;
2780 if(!rd
|| rd
->type
!= RemImap
|| !rd
->so
|| !rd
->rn
|| !rd
->t
.i
.special_hdr
)
2783 fields
[0] = rd
->t
.i
.special_hdr
;
2784 fields
[1] = "received";
2787 if((h
=pine_fetchheader_lines(rd
->t
.i
.stream
, rd
->t
.i
.stream
->nmsgs
,
2788 NULL
, fields
)) != NULL
){
2789 simple_header_parse(h
, fields
, values
);
2790 if(values
[1]) /* Received lines present! */
2791 ans
= rd_prompt_about_forged_remote_data(-1, rd
, NULL
);
2793 cookie
= strtoul(values
[0], (char **)NULL
, 10);
2794 if(cookie
== rd
->cookie
) /* all's well */
2797 ans
= rd_prompt_about_forged_remote_data(cookie
> 1L
2802 ans
= rd_prompt_about_forged_remote_data(-2, rd
, NULL
);
2805 fs_give((void **)&values
[0]);
2808 fs_give((void **)&values
[1]);
2810 fs_give((void **)&h
);
2812 else /* missing magic header */
2813 ans
= rd_prompt_about_forged_remote_data(-2, rd
, NULL
);