1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: remote.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2018 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
21 Implements remote IMAP config files (remote config, remote abook).
25 #include "../pith/headers.h"
26 #include "../pith/remote.h"
27 #include "../pith/conf.h"
28 #include "../pith/imap.h"
29 #include "../pith/msgno.h"
30 #include "../pith/mailview.h"
31 #include "../pith/status.h"
32 #include "../pith/flag.h"
33 #include "../pith/tempfile.h"
34 #include "../pith/adrbklib.h"
35 #include "../pith/detach.h"
36 #include "../pith/filter.h"
37 #include "../pith/stream.h"
38 #include "../pith/options.h"
39 #include "../pith/busy.h"
40 #include "../pith/readfile.h"
46 REMDATA_META_S
*rd_find_our_metadata(char *, unsigned long *);
47 int rd_meta_is_broken(FILE *);
48 int rd_add_hdr_msg(REMDATA_S
*, char *);
49 int rd_store_fake_hdrs(REMDATA_S
*, char *, char *, char *);
50 int rd_upgrade_cookies(REMDATA_S
*, long, int);
51 int rd_check_for_suspect_data(REMDATA_S
*);
54 char meta_prefix
[] = ".ab";
57 char *(*pith_opt_rd_metadata_name
)(void);
61 read_remote_pinerc(PINERC_S
*prc
, ParsePinerc which_vars
)
63 int try_cache
, no_perm_create_pass
= 0;
68 dprint((7, "read_remote_pinerc \"%s\"\n",
69 prc
->name
? prc
->name
: "?"));
72 * We don't cache the pinerc, we always copy it.
74 * Don't store the config in a temporary file, just leave it
75 * in memory while using it.
76 * It is currently required that NO_PERM_CACHE be set if NO_FILE is set.
78 flags
= (NO_PERM_CACHE
| NO_FILE
);
80 create_the_remote_folder
:
82 if(no_perm_create_pass
){
84 prc
->rd
->flags
&= ~DO_REMTRIM
;
85 rd_close_remdata(&prc
->rd
);
88 /* this will cause the remote folder to be created */
93 * We could parse the name here to find what type it is. So far we
94 * only have type RemImap.
96 prc
->rd
= rd_create_remote(RemImap
, prc
->name
,
97 REMOTE_PINERC_SUBTYPE
,
99 _(" Can't fetch remote configuration."));
104 * On first use we just use a temp file instead of memory (NO_FILE).
105 * In other words, for our convenience, we don't turn NO_FILE back on
106 * here. Why is that convenient? Because of the stuff that happened in
107 * rd_create_remote when flags was set to zero.
109 if(no_perm_create_pass
)
110 prc
->rd
->flags
|= NO_PERM_CACHE
;
112 try_cache
= rd_read_metadata(prc
->rd
);
114 if(prc
->rd
->access
== MaybeRorW
){
115 if(prc
->rd
->read_status
== 'R' ||
116 !(which_vars
== ParsePers
|| which_vars
== ParsePersPost
)){
117 prc
->rd
->access
= ReadOnly
;
118 prc
->rd
->read_status
= 'R';
121 prc
->rd
->access
= ReadWrite
;
124 if(prc
->rd
->access
!= NoExists
){
126 rd_check_remvalid(prc
->rd
, 1L);
129 * If the cached info says it is readonly but
130 * it looks like it's been fixed now, change it to readwrite.
132 if((which_vars
== ParsePers
|| which_vars
== ParsePersPost
) &&
133 prc
->rd
->read_status
== 'R'){
135 * We go to this trouble since readonly pinercs
136 * are likely a mistake. They are usually supposed to be
137 * readwrite so we open it and check if it's been fixed.
139 rd_check_readonly_access(prc
->rd
);
140 if(prc
->rd
->read_status
== 'W'){
141 prc
->rd
->access
= ReadWrite
;
142 prc
->rd
->flags
|= REM_OUTOFDATE
;
145 prc
->rd
->access
= ReadOnly
;
148 if(prc
->rd
->flags
& REM_OUTOFDATE
){
149 if(rd_update_local(prc
->rd
) != 0){
150 if(!no_perm_create_pass
&& prc
->rd
->flags
& NO_PERM_CACHE
151 && !(prc
->rd
->flags
& USER_SAID_NO
)){
153 * We don't check for the existence of the remote
154 * folder when this flag is turned on, so we could
155 * fail here because the remote folder doesn't exist.
156 * We try to create it.
158 no_perm_create_pass
++;
159 goto create_the_remote_folder
;
163 "read_pinerc_remote: rd_update_local failed\n"));
165 * Don't give up altogether. We still may be
166 * able to use a cached copy.
171 "%s: copied remote to local (%ld)\n",
172 prc
->rd
->rn
? prc
->rd
->rn
: "?",
173 (long)prc
->rd
->last_use
));
177 if(prc
->rd
->access
== ReadWrite
)
178 prc
->rd
->flags
|= DO_REMTRIM
;
181 /* If we couldn't get to remote folder, try using the cached copy */
182 if(prc
->rd
->access
== NoExists
|| prc
->rd
->flags
& REM_OUTOFDATE
){
184 prc
->rd
->access
= ReadOnly
;
185 prc
->rd
->flags
|= USE_OLD_CACHE
;
186 q_status_message(SM_ORDER
, 3, 4,
187 "Can't contact remote config server, using cached copy");
189 "Can't open remote pinerc %s, using local cached copy %s readonly\n",
190 prc
->rd
->rn
? prc
->rd
->rn
: "?",
191 prc
->rd
->lf
? prc
->rd
->lf
: "?"));
194 prc
->rd
->flags
&= ~DO_REMTRIM
;
199 if(prc
->rd
->flags
& NO_FILE
)
200 /* copy text, leave sonofile for later use */
201 file
= cpystr((char *)so_text(prc
->rd
->sonofile
));
203 file
= read_file(prc
->rd
->lf
, 0);
206 if((which_vars
== ParsePers
|| which_vars
== ParsePersPost
) &&
207 (!file
|| !prc
->rd
|| prc
->rd
->access
!= ReadWrite
)){
209 if(prc
== ps_global
->prc
)
210 ps_global
->readonly_pinerc
= 1;
218 * Check if the remote data folder exists and create an empty folder
219 * if it doesn't exist.
221 * Args - type -- The type of remote storage.
223 * type_spec -- Type-specific data.
225 * err_prefix -- Should usually end with a SPACE
226 * err_suffix -- Should usually begin with a SPACE
228 * Returns a pointer to a REMDATA_S with access set to either
229 * NoExists or MaybeRorW. On success, "so" will point to a storage object.
232 rd_create_remote(RemType type
, char *remote_name
, char *type_spec
,
233 unsigned int *flags
, char *err_prefix
, char *err_suffix
)
235 REMDATA_S
*rd
= NULL
;
236 CONTEXT_S
*dummy_cntxt
= NULL
;
238 dprint((7, "rd_create_remote \"%s\"\n",
239 remote_name
? remote_name
: "?"));
241 rd
= rd_new_remdata(type
, remote_name
, type_spec
);
247 if(rd
->flags
& NO_PERM_CACHE
){
248 if(rd
->rn
&& (rd
->so
= so_get(CharStar
, NULL
, WRITE_ACCESS
))){
249 if(rd
->flags
& NO_FILE
){
250 rd
->sonofile
= so_get(CharStar
, NULL
, WRITE_ACCESS
);
252 rd
->flags
&= ~NO_FILE
;
256 * We're not going to check if it is there in this case,
257 * in order to save ourselves some round trips and
258 * connections. We'll just try to select it and then
259 * recover at that point if it isn't already there.
261 rd
->flags
|= REM_OUTOFDATE
;
262 rd
->access
= MaybeRorW
;
268 * Open_fcc creates the folder if it didn't already exist.
270 if(rd
->rn
&& (rd
->so
= open_fcc(rd
->rn
, &dummy_cntxt
, 1,
271 err_prefix
, err_suffix
)) != NULL
){
273 * We know the folder is there but don't know what access
274 * rights we have until we try to select it, which we don't
275 * want to do unless we have to. So delay evaluating.
277 rd
->access
= MaybeRorW
;
284 q_status_message(SM_ORDER
, 3,5, "rd_create_remote: type not supported");
293 rd_new_remdata(RemType type
, char *remote_name
, char *type_spec
)
295 REMDATA_S
*rd
= NULL
;
297 rd
= (REMDATA_S
*)fs_get(sizeof(*rd
));
298 memset((void *)rd
, 0, sizeof(*rd
));
301 rd
->access
= NoExists
;
304 rd
->rn
= cpystr(remote_name
);
309 rd
->t
.i
.special_hdr
= cpystr(type_spec
);
314 q_status_message(SM_ORDER
, 3,5, "rd_new_remdata: type not supported");
323 * Closes the remote stream and frees.
326 rd_free_remdata(REMDATA_S
**rd
)
329 rd_close_remote(*rd
);
332 fs_give((void **)&(*rd
)->rn
);
335 fs_give((void **)&(*rd
)->lf
);
343 so_give(&(*rd
)->sonofile
);
344 (*rd
)->sonofile
= NULL
;
349 if((*rd
)->t
.i
.special_hdr
)
350 fs_give((void **)&(*rd
)->t
.i
.special_hdr
);
352 if((*rd
)->t
.i
.chk_date
)
353 fs_give((void **)&(*rd
)->t
.i
.chk_date
);
358 q_status_message(SM_ORDER
, 3, 5,
359 "rd_free_remdata: type not supported");
363 fs_give((void **)rd
);
369 * Call this when finished with the remdata. This does the REMTRIM if the
370 * flag is set, the DEL_FILE if the flag is set. It also closes the stream
374 rd_trim_remdata(REMDATA_S
**rd
)
382 * Trim the number of saved copies of remote data history.
383 * The first message is a fake message that always
384 * stays there, then come ps_global->remote_abook_history messages
385 * which are each a revision of the data, then comes the active
388 if((*rd
)->flags
& DO_REMTRIM
&&
389 !((*rd
)->flags
& REM_OUTOFDATE
) &&
390 (*rd
)->t
.i
.chk_nmsgs
> ps_global
->remote_abook_history
+ 2){
392 /* make sure stream is open */
396 if(!rd_remote_is_readonly(*rd
)){
397 if((*rd
)->t
.i
.stream
&&
398 (*rd
)->t
.i
.stream
->nmsgs
>
399 ps_global
->remote_abook_history
+ 2){
401 int user_deleted
= 0;
404 * If user manually deleted some, we'd better not delete
407 if(count_flagged((*rd
)->t
.i
.stream
, F_DEL
) == 0L){
409 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
: "?"));
410 snprintf(sequence
, sizeof(sequence
), "2:%ld",
411 (*rd
)->t
.i
.stream
->nmsgs
- 1 - ps_global
->remote_abook_history
);
412 mail_flag((*rd
)->t
.i
.stream
, sequence
,
413 "\\DELETED", ST_SET
);
418 mail_expunge((*rd
)->t
.i
.stream
);
421 rd_update_metadata(*rd
, NULL
);
423 * don't update metafile because user is messing with
424 * the remote folder manually. We'd better re-read it next
429 ps_global
->noshow_error
= 0;
435 q_status_message(SM_ORDER
, 3,5, "rd_trim_remdata: type not supported");
442 * All done with this remote data. Trim the folder, close the
446 rd_close_remdata(REMDATA_S
**rd
)
453 if((*rd
)->lf
&& (*rd
)->flags
& DEL_FILE
)
454 our_unlink((*rd
)->lf
);
456 /* this closes the stream and frees memory */
462 * Looks in the metadata file for the cache line corresponding to rd and
463 * fills in data in rd.
465 * Return value -- 1 if it is likely that the filename we're returning
466 * is the permanent name of the local cache file and it may already have
467 * a cached copy of the data. This is to tell us if it makes sense to use
468 * the cached copy when we are unable to contact the remote server.
472 rd_read_metadata(REMDATA_S
*rd
)
474 REMDATA_META_S
*rab
= NULL
;
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");
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
,
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");
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
= NULL
, *pinerc_dir
= NULL
, *metafile
= NULL
;
620 char *rel_filename
, *key
;
621 char line
[MAILTMPLEN
];
624 dprint((7, "rd_write_metadata \"%s\"\n",
625 (rd
&& rd
->rn
) ? rd
->rn
: "?"));
627 if(!rd
|| rd
->flags
& NO_META_UPDATE
)
630 if(rd
->type
!= RemImap
){
631 q_status_message(SM_ORDER
, 3, 5,
632 "rd_write_metadata: type not supported");
636 dprint((9, " - rd_write_metadata: rn=%s lf=%s\n",
637 rd
->rn
? rd
->rn
: "?", rd
->lf
? rd
->lf
: "?"));
641 if(!(pith_opt_rd_metadata_name
&& (metafile
= (*pith_opt_rd_metadata_name
)())))
644 if(!(tempfile
= tempfile_in_same_dir(metafile
, "a9", &pinerc_dir
)))
647 if((fd
= our_open(tempfile
, O_TRUNC
|O_WRONLY
|O_CREAT
|O_BINARY
, 0600)) >= 0)
648 fp_new
= fdopen(fd
, "w");
650 if(pinerc_dir
&& rd
->lf
&& strlen(rd
->lf
) > strlen(pinerc_dir
))
651 rel_filename
= rd
->lf
+ strlen(pinerc_dir
) + 1;
653 rel_filename
= rd
->lf
;
656 fs_give((void **)&pinerc_dir
);
658 fp_old
= our_fopen(metafile
, "rb");
660 if(fp_new
&& fp_old
){
662 * Write the header line.
664 if(fprintf(fp_new
, "%s %s Pine Metadata\n",
665 PMAGIC
, METAFILE_VERSION_NUM
) == EOF
)
668 while((p
= fgets(line
, sizeof(line
), fp_old
)) != NULL
){
670 * Skip the header line and any lines that don't begin
676 /* skip the old cache line for this key */
677 if(strncmp(line
, key
, strlen(key
)) == 0 && line
[strlen(key
)] == TAB
)
680 /* add this line to new version of file */
681 if(fputs(p
, fp_new
) == EOF
)
686 /* add the cache line for this key */
687 /* Warning: this is type RemImap specific right now! */
693 fprintf(fp_new
, "%s\t%s\t%lu\t%lu\t%lu\t%lu\t%c\t%s\n",
695 rel_filename
? rel_filename
: "",
696 rd
->t
.i
.uidvalidity
, rd
->t
.i
.uidnext
, rd
->t
.i
.uid
,
698 rd
->read_status
? rd
->read_status
: '?',
699 rd
->t
.i
.chk_date
? rd
->t
.i
.chk_date
: "no-match")
704 if(fclose(fp_new
) == EOF
){
709 if(fclose(fp_old
) == EOF
){
714 if(rename_file(tempfile
, metafile
) < 0)
718 fs_give((void **)&tempfile
);
721 fs_give((void **)&metafile
);
726 dprint((2, "io_err in rd_write_metadata(%s), tempfile=%s: %s\n",
727 metafile
? metafile
: "<NULL>", tempfile
? tempfile
: "<NULL>",
728 error_description(errno
)));
729 q_status_message2(SM_ORDER
, 3, 5,
730 "Trouble updating metafile %s, continuing (%s)",
731 metafile
? metafile
: "<NULL>", error_description(errno
));
733 our_unlink(tempfile
);
734 fs_give((void **)&tempfile
);
737 fs_give((void **)&metafile
);
739 (void)fclose(fp_old
);
741 (void)fclose(fp_new
);
746 rd_update_metadata(REMDATA_S
*rd
, char *date
)
751 dprint((9, " - rd_update_metadata: rn=%s lf=%s\n",
752 rd
->rn
? rd
->rn
: "?", rd
->lf
? rd
->lf
: "<none>"));
757 ps_global
->noshow_error
= 1;
761 * If nmsgs < 2 then something is wrong. Maybe it is just
762 * that we haven't been told about the messages we've
763 * appended ourselves. Try closing and re-opening the 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
))){
769 pine_mail_check(rd
->t
.i
.stream
);
770 if(rd
->t
.i
.stream
->nmsgs
< 2 ||
771 (rd
->t
.i
.shouldbe_nmsgs
&&
772 (rd
->t
.i
.shouldbe_nmsgs
!= rd
->t
.i
.stream
->nmsgs
))){
778 rd
->t
.i
.chk_nmsgs
= rd
->t
.i
.stream
? rd
->t
.i
.stream
->nmsgs
: 0L;
781 * If nmsgs < 2 something is wrong.
783 if(rd
->t
.i
.chk_nmsgs
< 2){
784 rd
->t
.i
.uidvalidity
= 0L;
786 rd
->t
.i
.uidnext
= 0L;
789 rd
->t
.i
.uidvalidity
= rd
->t
.i
.stream
->uid_validity
;
790 rd
->t
.i
.uid
= mail_uid(rd
->t
.i
.stream
,
791 rd
->t
.i
.stream
->nmsgs
);
793 * Uid_last is not always valid. If the last known uid is
794 * greater than uid_last, go with it instead (uid+1).
795 * If our guess is wrong (too low), the penalty is not
796 * harsh. When the uidnexts don't match we open the
797 * mailbox to check the uid of the actual last message.
798 * If it was a false hit then we adjust uidnext so it
799 * will be correct the next time through.
801 rd
->t
.i
.uidnext
= MAX(rd
->t
.i
.stream
->uid_last
,rd
->t
.i
.uid
)
806 ps_global
->noshow_error
= 0;
809 * Save the date so that we can check if it changed next time
814 fs_give((void **)&rd
->t
.i
.chk_date
);
816 rd
->t
.i
.chk_date
= cpystr(date
);
819 rd_write_metadata(rd
, 0);
822 rd
->t
.i
.shouldbe_nmsgs
= 0;
827 q_status_message(SM_ORDER
, 3, 5,
828 "rd_update_metadata: type not supported");
837 rd_find_our_metadata(char *key
, long unsigned int *flags
)
839 char *p
, *q
, *metafile
= NULL
;
840 char line
[MAILTMPLEN
];
841 REMDATA_META_S
*rab
= NULL
;
844 dprint((9, "rd_find_our_metadata \"%s\"\n", key
? key
: "?"));
849 if(!(pith_opt_rd_metadata_name
&& (metafile
= (*pith_opt_rd_metadata_name
)()) != NULL
))
853 * Open the metadata file and get some information out.
855 fp
= our_fopen(metafile
, "rb");
857 q_status_message2(SM_ORDER
, 3, 5,
858 _("can't open metadata file %s, continuing (%s)"),
859 metafile
, error_description(errno
));
861 "can't open existing metadata file %s: %s\n",
862 metafile
? metafile
: "?", error_description(errno
)));
864 (*flags
) |= NO_META_UPDATE
;
866 fs_give((void **)&metafile
);
872 * If we make it to this point where we have opened the metadata file
873 * we return a structure (possibly empty) instead of just a NULL pointer.
875 rab
= (REMDATA_META_S
*)fs_get(sizeof(*rab
));
876 memset(rab
, 0, sizeof(*rab
));
879 * Check for header line. If it isn't there or is incorrect,
880 * return with the empty rab. This call also positions the file pointer
881 * past the header line.
883 if(rd_meta_is_broken(fp
)){
886 "metadata file is broken, creating new one: %s\n",
887 metafile
? metafile
: "?"));
889 /* Make it size zero so we won't copy any bad stuff */
890 if(fp_file_size(fp
) != 0){
894 our_unlink(metafile
);
896 if((fd
= our_open(metafile
, O_TRUNC
|O_WRONLY
|O_CREAT
|O_BINARY
, 0600)) < 0){
897 q_status_message2(SM_ORDER
, 3, 5,
898 _("can't create metadata file %s, continuing (%s)"),
899 metafile
, error_description(errno
));
901 "can't create metadata file %s: %s\n",
902 metafile
? metafile
: "?",
903 error_description(errno
)));
904 fs_give((void **)&rab
);
905 fs_give((void **)&metafile
);
914 fs_give((void **)&metafile
);
918 fs_give((void **)&metafile
);
920 /* Look for our line */
921 while((p
= fgets(line
, sizeof(line
), fp
)) != NULL
)
922 if(strncmp(line
, key
, strlen(key
)) == 0 && line
[strlen(key
)] == TAB
)
925 #define SKIP_TO_TAB(p) do{while(*p && *p != TAB)p++;}while(0)
928 * The line should be TAB-separated with fields:
929 * folder_name cache_file uidvalidity uidnext uid nmsgs read_status date
930 * This part is highly RemImap-specific right now.
932 if(p
){ /* Found the line, parse it. */
933 SKIP_TO_TAB(p
); /* skip to TAB following folder_name */
935 q
= ++p
; /* q points to cache_file */
936 SKIP_TO_TAB(p
); /* skip to TAB following cache_file */
939 rab
->local_cache_file
= cpystr(q
);
940 q
= ++p
; /* q points to uidvalidity */
941 SKIP_TO_TAB(p
); /* skip to TAB following uidvalidity */
944 rab
->uidvalidity
= strtoul(q
,(char **)NULL
,10);
945 q
= ++p
; /* q points to uidnext */
946 SKIP_TO_TAB(p
); /* skip to TAB following uidnext */
949 rab
->uidnext
= strtoul(q
,(char **)NULL
,10);
950 q
= ++p
; /* q points to uid */
951 SKIP_TO_TAB(p
); /* skip to TAB following uid */
954 rab
->uid
= strtoul(q
,(char **)NULL
,10);
955 q
= ++p
; /* q points to nmsgs */
956 SKIP_TO_TAB(p
); /* skip to TAB following nmsgs */
959 rab
->nmsgs
= strtoul(q
,(char **)NULL
,10);
960 q
= ++p
; /* q points to read_status */
961 SKIP_TO_TAB(p
); /* skip to TAB following read_status */
964 rab
->read_status
= *q
; /* just a char, not whole string */
965 q
= ++p
; /* q points to date */
966 while(*p
&& *p
!= '\n' && *p
!= '\r') /* skip to newline */
970 rab
->date
= cpystr(q
);
986 * Returns: -1 if this doesn't look like a metafile or some error,
987 * or if it looks like a non-current metafile.
988 * 0 if it looks like a current metafile.
990 * A side effect is that the file pointer will be pointing to the second
991 * line if 0 is returned.
994 rd_meta_is_broken(FILE *fp
)
996 char buf
[MAILTMPLEN
];
998 if(fp
== (FILE *)NULL
)
1001 if(fp_file_size(fp
) <
1002 (long)(SIZEOF_PMAGIC
+ SIZEOF_SPACE
+ SIZEOF_VERSION_NUM
))
1005 if(fseek(fp
, (long)TO_FIND_HDR_PMAGIC
, 0))
1008 /* check for magic */
1009 if(fread(buf
, sizeof(char), (unsigned)SIZEOF_PMAGIC
, fp
) != SIZEOF_PMAGIC
)
1012 buf
[SIZEOF_PMAGIC
] = '\0';
1013 if(strcmp(buf
, PMAGIC
) != 0)
1017 * If we change to a new version, we may want to add code here to convert
1018 * or to cleanup after the old version. For example, it might just
1019 * remove the old cache files here.
1022 /* check for matching version number */
1023 if(fseek(fp
, (long)TO_FIND_VERSION_NUM
, 0))
1026 if(fread(buf
, sizeof(char), (unsigned)SIZEOF_VERSION_NUM
, fp
) !=
1030 buf
[SIZEOF_VERSION_NUM
] = '\0';
1031 if(strcmp(buf
, METAFILE_VERSION_NUM
) != 0)
1034 /* Position file pointer to second line */
1035 if(fseek(fp
, (long)TO_FIND_HDR_PMAGIC
, 0))
1038 if(fgets(buf
, sizeof(buf
), fp
) == NULL
)
1046 * Open a data stream to the remote data.
1049 rd_open_remote(REMDATA_S
*rd
)
1051 long openmode
= SP_USEPOOL
| SP_TEMPUSE
;
1053 dprint((7, "rd_open_remote \"%s\"\n",
1054 (rd
&& rd
->rn
) ? rd
->rn
: "?"));
1059 if(rd_stream_exists(rd
)){
1060 rd
->last_use
= get_adj_time();
1067 if(rd
->access
== ReadOnly
)
1068 openmode
|= OP_READONLY
;
1070 ps_global
->noshow_error
= 1;
1071 rd
->t
.i
.stream
= context_open(NULL
, NULL
, rd
->rn
, openmode
, NULL
);
1072 ps_global
->noshow_error
= 0;
1074 /* Don't try to reopen if there was a problem (auth failure, etc.) */
1076 rd
->flags
|= USER_SAID_NO
; /* Caution: overloading USER_SAID_NO */
1078 if(rd
->t
.i
.stream
&& rd
->t
.i
.stream
->halfopen
){
1079 /* this is a failure */
1080 rd_close_remote(rd
);
1084 rd
->last_use
= get_adj_time();
1089 q_status_message(SM_ORDER
, 3, 5, "rd_open_remote: type not supported");
1096 * Close a data stream to the remote data.
1099 rd_close_remote(REMDATA_S
*rd
)
1101 if(!rd
|| !rd_stream_exists(rd
))
1106 ps_global
->noshow_error
= 1;
1107 pine_mail_close(rd
->t
.i
.stream
);
1108 rd
->t
.i
.stream
= NULL
;
1109 ps_global
->noshow_error
= 0;
1113 q_status_message(SM_ORDER
, 3, 5, "rd_close_remote: type not supported");
1120 rd_stream_exists(REMDATA_S
*rd
)
1127 return(rd
->t
.i
.stream
? 1 : 0);
1130 q_status_message(SM_ORDER
, 3,5, "rd_stream_exists: type not supported");
1139 * Ping the stream and return 1 if it is still alive.
1142 rd_ping_stream(REMDATA_S
*rd
)
1146 dprint((7, "rd_ping_stream \"%s\"\n",
1147 (rd
&& rd
->rn
) ? rd
->rn
: "?"));
1152 if(!rd_stream_exists(rd
)){
1156 * If this stream is already actually open, officially open
1159 if(sp_stream_get(rd
->rn
, SP_MATCH
)){
1160 long openflags
= SP_USEPOOL
| SP_TEMPUSE
;
1162 if(rd
->access
== ReadOnly
)
1163 openflags
|= OP_READONLY
;
1165 rd
->t
.i
.stream
= pine_mail_open(NULL
, rd
->rn
, openflags
, NULL
);
1175 if(!rd_stream_exists(rd
))
1180 ps_global
->noshow_error
= 1;
1181 if(pine_mail_ping(rd
->t
.i
.stream
))
1184 rd
->t
.i
.stream
= NULL
;
1186 ps_global
->noshow_error
= 0;
1190 q_status_message(SM_ORDER
, 3, 5, "rd_ping_stream: type not supported");
1199 * Change readonly access to readwrite if appropriate. Call this if
1200 * the remote data ought to be readwrite but it is marked readonly in
1204 rd_check_readonly_access(REMDATA_S
*rd
)
1206 if(rd
&& rd
->read_status
== 'R'){
1210 if(rd
->t
.i
.stream
&& !rd
->t
.i
.stream
->rdonly
){
1211 rd
->read_status
= 'W';
1212 rd
->access
= ReadWrite
;
1218 q_status_message(SM_ORDER
, 3, 5,
1219 "rd_check_readonly_access: type not supported");
1227 * Initialize remote data. The remote data is initialized
1228 * with no data. On success, the local file will be empty and the remote
1229 * folder (if type RemImap) will contain a header message and an empty
1231 * The remote folder already exists before we get here, though it may be empty.
1233 * Returns -1 on failure
1237 rd_init_remote(REMDATA_S
*rd
, int add_only_first_msg
)
1242 dprint((7, "rd_init_remote \"%s\"\n",
1243 (rd
&& rd
->rn
) ? rd
->rn
: "?"));
1245 if(rd
&& rd
->type
!= RemImap
){
1246 dprint((1, "rd_init_remote: type not supported\n"));
1251 * The rest is currently type RemImap-specific.
1254 if(!(rd
->flags
& NO_FILE
|| rd
->lf
) ||
1255 !rd
->rn
|| !rd
->so
|| !rd
->t
.i
.stream
||
1256 !rd
->t
.i
.special_hdr
){
1258 "rd_init_remote: Unexpected error: %s is NULL\n",
1259 !(rd
->flags
& NO_FILE
|| rd
->lf
) ? "localfile" :
1260 !rd
->rn
? "remotename" :
1262 !rd
->t
.i
.stream
? "stream" :
1263 !rd
->t
.i
.special_hdr
? "special_hdr" : "?"));
1267 /* already init'd */
1268 if(rd
->t
.i
.stream
->nmsgs
>= 2 ||
1269 (rd
->t
.i
.stream
->nmsgs
>= 1 && add_only_first_msg
))
1272 dprint((7, " - rd_init_remote(%s): %s\n",
1273 rd
->t
.i
.special_hdr
? rd
->t
.i
.special_hdr
: "?",
1274 rd
->rn
? rd
->rn
: "?"));
1277 * We want to protect the user who specifies an actual address book
1278 * file on the remote system as a remote address book. For example,
1279 * they might say address-book={host}.addressbook where .addressbook
1280 * is just a file on host. Remote address books are folders, not
1281 * files. We also want to protect the user who specifies an existing
1282 * mail folder as a remote address book. We do that by requiring the
1283 * first message in the folder to be our header message.
1286 if(rd
->t
.i
.stream
->rdonly
){
1287 q_status_message1(SM_ORDER
| SM_DING
, 7, 10,
1288 _("Can't initialize folder \"%s\" (write permission)"), rd
->rn
);
1289 if(rd
->t
.i
.stream
->nmsgs
> 0)
1290 q_status_message(SM_ORDER
| SM_DING
, 7, 10,
1291 _("Choose a new, unused folder for the remote data"));
1294 "Can't initialize remote folder \"%s\"\n",
1295 rd
->rn
? rd
->rn
: "?"));
1297 " No write permission for that remote folder.\n"));
1299 " Choose a new unused folder for the remote data.\n"));
1304 if(rd
->t
.i
.stream
->nmsgs
== 0){
1307 we_cancel
= busy_cue(_("Initializing remote data"), NULL
, 1);
1310 * The first message in a remote data folder is a special
1311 * header message. It is there as a way to explain what the
1312 * folder is to users who open it trying to read it. It is also
1313 * used as a consistency check so that we don't use a folder
1314 * that was being used for something else as a remote
1317 err
= rd_add_hdr_msg(rd
, date
);
1318 if(rd
->t
.i
.stream
->nmsgs
== 0)
1320 if(rd
->t
.i
.stream
&& rd
->t
.i
.stream
->nmsgs
== 0)
1321 pine_mail_check(rd
->t
.i
.stream
);
1324 cancel_busy_cue(-1);
1329 err
= rd_chk_for_hdr_msg(&(rd
->t
.i
.stream
), rd
, &eptr
);
1331 q_status_message1(SM_ORDER
| SM_DING
, 5, 5,
1332 _("\"%s\" has invalid format, can't initialize"), rd
->rn
);
1335 "Can't initialize remote data \"%s\"\n",
1336 rd
->rn
? rd
->rn
: "?"));
1339 q_status_message1(SM_ORDER
, 3, 5, "%s", eptr
);
1340 dprint((1, "%s\n", eptr
? eptr
: "?"));
1341 fs_give((void **)&eptr
);
1348 * Add the second (empty) message.
1350 if(!err
&& !add_only_first_msg
){
1351 char *tempfile
= NULL
;
1354 if(rd
->flags
& NO_FILE
){
1355 if(so_truncate(rd
->sonofile
, 0L) == 0)
1359 if(!(tempfile
= tempfile_in_same_dir(rd
->lf
, "a8", NULL
))){
1360 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
1361 _("Error opening temporary file: %s"),
1362 error_description(errno
));
1363 dprint((2, "init_remote: Error opening file: %s\n",
1364 error_description(errno
)));
1369 (fd
= our_open(tempfile
, O_TRUNC
|O_WRONLY
|O_CREAT
|O_BINARY
, 0600)) < 0){
1370 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1371 "Error opening temporary file %.200s: %.200s",
1372 tempfile
, error_description(errno
));
1374 "init_remote: Error opening temporary file: %s: %s\n",
1375 tempfile
? tempfile
: "?", error_description(errno
)));
1376 our_unlink(tempfile
);
1382 if(!err
&& rename_file(tempfile
, rd
->lf
) < 0){
1383 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1384 "Error creating cache file %.200s: %.200s",
1385 rd
->lf
, error_description(errno
));
1386 our_unlink(tempfile
);
1391 fs_give((void **)&tempfile
);
1395 err
= rd_update_remote(rd
, date
);
1397 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
1398 _("Error copying to remote folder: %s"),
1399 error_description(errno
));
1401 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
1402 "Creation of remote data folder failed");
1408 rd_update_metadata(rd
, date
);
1409 /* turn off out of date flag */
1410 rd
->flags
&= ~REM_OUTOFDATE
;
1413 return(err
? -1 : 0);
1418 * IMAP stream should already be open to a remote folder.
1419 * Check the first message in the folder to be sure it is the right
1420 * kind of message, not some message from some other folder.
1422 * Returns 0 if ok, < 0 if invalid format.
1426 rd_chk_for_hdr_msg(MAILSTREAM
**streamp
, REMDATA_S
*rd
, char **errmsg
)
1428 char *fields
[3], *values
[3];
1430 int tried_again
= 0;
1432 MAILSTREAM
*st
= NULL
;
1434 fields
[0] = rd
->t
.i
.special_hdr
;
1435 fields
[1] = "received";
1441 if(!streamp
|| !*streamp
){
1442 dprint((1, "rd_chk_for_hdr_msg: stream is null\n"));
1444 else if((*streamp
)->nmsgs
== 0){
1447 "rd_chk_for_hdr_msg: stream has nmsgs=0, try a ping\n"));
1448 if(!pine_mail_ping(*streamp
))
1451 if(*streamp
&& (*streamp
)->nmsgs
== 0){
1453 "rd_chk_for_hdr_msg: still looks like nmsgs=0, try a check\n"));
1454 pine_mail_check(*streamp
);
1457 if(*streamp
&& (*streamp
)->nmsgs
== 0){
1459 "rd_chk_for_hdr_msg: still nmsgs=0, try re-opening stream\n"));
1461 if(rd_stream_exists(rd
))
1462 rd_close_remote(rd
);
1465 if(rd_stream_exists(rd
))
1466 st
= rd
->t
.i
.stream
;
1472 if(st
&& st
->nmsgs
== 0){
1474 "rd_chk_for_hdr_msg: can't see header message\n"));
1480 if(st
&& st
->nmsgs
!= 0
1481 && (h
=pine_fetchheader_lines(st
, 1L, NULL
, fields
))){
1482 simple_header_parse(h
, fields
, values
);
1487 rd
->cookie
= strtoul(values
[0], (char **)NULL
, 10);
1490 else if(rd
->cookie
== 1){
1491 if(rd
->flags
& COOKIE_ONE_OK
|| tried_again
)
1496 else if(rd
->cookie
> 1)
1501 fs_give((void **)&values
[0]);
1504 fs_give((void **)&values
[1]);
1506 fs_give((void **)&h
);
1510 if(ret
&& ret
!= -6 && errmsg
){
1511 *errmsg
= (char *)fs_get(500 * sizeof(char));
1512 (*errmsg
)[0] = '\0';
1518 snprintf(*errmsg
, 500, _("Can't open remote address book \"%s\""), rd
->rn
);
1521 /* no messages in folder */
1523 snprintf(*errmsg
, 500,
1524 _("Error: no messages in remote address book \"%s\"!"),
1530 snprintf(*errmsg
, 500,
1531 "First msg in \"%s\" should have \"%s\" header",
1532 rd
->rn
, rd
->t
.i
.special_hdr
);
1535 /* Received header */
1537 snprintf(*errmsg
, 500,
1538 _("Suspicious Received headers in first msg in \"%s\""),
1546 * This is a failure and should not happen, but we're not going to
1547 * fail on this condition.
1549 dprint((1, "Unexpected value in \"%s\" header of \"%s\"\n",
1550 rd
->t
.i
.special_hdr
? rd
->t
.i
.special_hdr
: "?",
1551 rd
->rn
? rd
->rn
: "?"));
1556 "rd_chk_for_hdr_msg: cookie is 1, try to upgrade it\n"));
1558 if(rd_remote_is_readonly(rd
)){
1560 "rd_chk_for_hdr_msg: can't upgrade, readonly\n"));
1561 ret
= 0; /* stick with 1 */
1564 /* cookie is 1, upgrade it */
1565 if(rd_upgrade_cookies(rd
, st
->nmsgs
, 0) == 0){
1566 /* now check again */
1574 * This is actually a failure but we've decided that this
1581 if(errmsg
&& *errmsg
)
1582 dprint((1, "rd_chk_for_hdr_msg: %s\n", *errmsg
));
1589 * For remote data, this adds the explanatory header
1590 * message to the remote IMAP folder.
1592 * Args: rd -- Remote data handle
1593 * date -- The date string to use
1599 rd_add_hdr_msg(REMDATA_S
*rd
, char *date
)
1603 if(!rd
|| rd
->type
!= RemImap
|| !rd
->rn
|| !rd
->so
|| !rd
->t
.i
.special_hdr
){
1605 "rd_add_hdr_msg: Unexpected error: %s is NULL\n",
1607 !rd
->rn
? "remotename" :
1609 !rd
->t
.i
.special_hdr
? "special_hdr" : "?"));
1613 err
= rd_store_fake_hdrs(rd
, "Header Message for Remote Data",
1616 /* Write the dummy message */
1617 if(!strucmp(rd
->t
.i
.special_hdr
, REMOTE_ABOOK_SUBTYPE
)){
1618 if(!err
&& so_puts(rd
->so
,
1619 "This folder contains a single Alpine addressbook.\015\012") == 0)
1621 if(!err
&& so_puts(rd
->so
,
1622 "This message is just an explanatory message.\015\012") == 0)
1624 if(!err
&& so_puts(rd
->so
,
1625 "The last message in the folder is the live addressbook data.\015\012") == 0)
1627 if(!err
&& so_puts(rd
->so
,
1628 "The rest of the messages contain previous revisions of the addressbook data.\015\012") == 0)
1630 if(!err
&& so_puts(rd
->so
,
1631 "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1633 if(!err
&& so_puts(rd
->so
,
1634 "which come after it.\015\012") == 0)
1637 else if(!strucmp(rd
->t
.i
.special_hdr
, REMOTE_PINERC_SUBTYPE
)){
1638 if(!err
&& so_puts(rd
->so
,
1639 "This folder contains a Alpine config file.\015\012") == 0)
1641 if(!err
&& so_puts(rd
->so
,
1642 "This message is just an explanatory message.\015\012") == 0)
1644 if(!err
&& so_puts(rd
->so
,
1645 "The last message in the folder is the live config data.\015\012") == 0)
1647 if(!err
&& so_puts(rd
->so
,
1648 "The rest of the messages contain previous revisions of the data.\015\012") == 0)
1650 if(!err
&& so_puts(rd
->so
,
1651 "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1653 if(!err
&& so_puts(rd
->so
,
1654 "which come after it.\015\012") == 0)
1658 if(!err
&& so_puts(rd
->so
,
1659 "This folder contains remote Alpine data.\015\012") == 0)
1661 if(!err
&& so_puts(rd
->so
,
1662 "This message is just an explanatory message.\015\012") == 0)
1664 if(!err
&& so_puts(rd
->so
,
1665 "The last message in the folder is the live data.\015\012") == 0)
1667 if(!err
&& so_puts(rd
->so
,
1668 "The rest of the messages contain previous revisions of the data.\015\012") == 0)
1670 if(!err
&& so_puts(rd
->so
,
1671 "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1673 if(!err
&& so_puts(rd
->so
,
1674 "which come after it.\015\012") == 0)
1678 /* Take the message from "so" to the remote folder */
1682 if((st
= rd
->t
.i
.stream
) != NULL
)
1683 rd
->t
.i
.shouldbe_nmsgs
= rd
->t
.i
.stream
->nmsgs
+ 1;
1685 st
= adrbk_handy_stream(rd
->rn
);
1687 err
= write_fcc(rd
->rn
, NULL
, rd
->so
, st
, "remote data", NULL
) ? 0 : -1;
1695 * Write some fake header lines into storage object rd->so.
1698 * subject -- subject to put in header
1699 * subtype -- subtype to put in header
1700 * date -- date to put in header
1703 rd_store_fake_hdrs(REMDATA_S
*rd
, char *subject
, char *subtype
, char *date
)
1710 unsigned long r
= 0L;
1713 if(!rd
|| rd
->type
!= RemImap
|| !rd
->so
|| !rd
->t
.i
.special_hdr
)
1716 fake_env
= (ENVELOPE
*)fs_get(sizeof(ENVELOPE
));
1717 memset(fake_env
, 0, sizeof(ENVELOPE
));
1718 fake_body
= (BODY
*)fs_get(sizeof(BODY
));
1719 memset(fake_body
, 0, sizeof(BODY
));
1720 fake_from
= (ADDRESS
*)fs_get(sizeof(ADDRESS
));
1721 memset(fake_from
, 0, sizeof(ADDRESS
));
1723 fake_env
->subject
= cpystr(subject
);
1724 fake_env
->date
= (unsigned char *) cpystr(date
);
1725 fake_from
->personal
= cpystr("Pine Remote Data");
1726 fake_from
->mailbox
= cpystr("nobody");
1727 fake_from
->host
= cpystr("nowhere");
1728 fake_env
->from
= fake_from
;
1729 fake_body
->type
= REMOTE_DATA_TYPE
;
1730 fake_body
->subtype
= cpystr(subtype
);
1731 set_parameter(&fake_body
->parameter
, "charset", "UTF-8");
1739 for(i
= 100; i
> 0 && r
< 1000000; i
--)
1743 r
= 1712836L + getpid();
1748 snprintf(vers
, sizeof(vers
), "%ld", r
);
1752 rbuf
.f
= dummy_soutr
;
1756 rbuf
.end
= p
+SIZEOF_20KBUF
-1;
1757 rfc822_output_header_line(&rbuf
, rd
->t
.i
.special_hdr
, 0L, vers
);
1758 rfc822_output_header(&rbuf
, fake_env
, fake_body
, NULL
, 0L);
1761 mail_free_envelope(&fake_env
);
1762 mail_free_body(&fake_body
);
1764 /* Write the fake headers */
1765 if(so_puts(rd
->so
, tmp_20k_buf
) == 0)
1773 * We have discovered that the data in the remote folder is suspicious.
1774 * In some cases it is just because it is from an old version of pine.
1775 * We have decided to update the data so that it won't look suspicious
1778 * Args -- only_update_last If set, that means to just add a new last message
1779 * by calling rd_update_remote. Don't create a new
1780 * header message and delete the old header message.
1781 * nmsgs Not used if only_update_last is set
1784 rd_upgrade_cookies(REMDATA_S
*rd
, long int nmsgs
, int only_update_last
)
1790 * We need to copy the data from the last message, add a new header
1791 * message with a random cookie, add the data back in with the
1792 * new cookie, and delete the old messages.
1796 rd
->flags
|= COOKIE_ONE_OK
;
1799 * The local copy may be newer than the remote copy. We don't want to
1800 * blast the local copy in that case. The BELIEVE_CACHE flag tells us
1801 * to not do the blasting.
1803 if(rd
->flags
& BELIEVE_CACHE
)
1804 rd
->flags
&= ~BELIEVE_CACHE
;
1806 dprint((1, "rd_upgrade_cookies: copy abook data\n"));
1807 err
= rd_update_local(rd
);
1810 if(!err
&& !only_update_last
){
1811 rd
->cookie
= 0; /* causes new cookie to be generated */
1813 dprint((1, "rd_upgrade_cookies: add new hdr msg to end\n"));
1814 err
= rd_add_hdr_msg(rd
, date
);
1818 dprint((1, "rd_upgrade_cookies: copy back data\n"));
1819 err
= rd_update_remote(rd
, NULL
);
1822 rd
->flags
&= ~COOKIE_ONE_OK
;
1824 if(!err
&& !only_update_last
){
1828 * We've created a new header message and added a new copy of the
1829 * data after it. Only problem is that the new copy will have used
1830 * the original header message to get its cookie (== 1) from. We
1831 * could have deleted the original messages before the last step
1832 * to get it right but that would delete all copies of the data
1833 * temporarily. Delete now and then re-update.
1837 if(rd
->t
.i
.stream
&& rd
->t
.i
.stream
->nmsgs
>= nmsgs
+2){
1838 snprintf(sequence
, sizeof(sequence
), "1:%ld", nmsgs
);
1839 mail_flag(rd
->t
.i
.stream
, sequence
, "\\DELETED", ST_SET
);
1840 mail_expunge(rd
->t
.i
.stream
);
1841 err
= rd_update_remote(rd
, NULL
);
1850 * Copy remote data to local file. If needed, the remote data is initialized
1851 * with no data. On success, the local file contains the remote data (which
1852 * means it will be empty if we initialized).
1854 * Returns != 0 on failure
1858 rd_update_local(REMDATA_S
*rd
)
1863 int i
, we_cancel
= 0;
1866 char *tempfile
= NULL
;
1869 if(!rd
|| !(rd
->flags
& NO_FILE
|| rd
->lf
) || !rd
->rn
){
1871 "rd_update_local: Unexpected error: %s is NULL\n",
1873 !(rd
->flags
& NO_FILE
|| rd
->lf
) ? "localfile" :
1874 !rd
->rn
? "remotename" : "?"));
1879 dprint((3, " - rd_update_local(%s): %s => %s\n",
1880 rd
->type
== RemImap
? "Imap" : "?", rd
->rn
? rd
->rn
: "?",
1881 (rd
->flags
& NO_FILE
) ? "<mem>" : (rd
->lf
? rd
->lf
: "?")));
1885 if(!rd
->so
|| !rd
->t
.i
.special_hdr
){
1887 "rd_update_local: Unexpected error: %s is NULL\n",
1889 !rd
->t
.i
.special_hdr
? "special_hdr" : "?"));
1894 if(!rd_stream_exists(rd
)){
1895 if(rd
->flags
& NO_PERM_CACHE
){
1897 "rd_update_local: backtrack, remote folder does not exist yet\n"));
1901 "rd_update_local: Unexpected error: stream is NULL\n"));
1912 /* force ReadOnly */
1913 if(rd
->t
.i
.stream
->rdonly
){
1914 rd
->read_status
= 'R';
1915 rd
->access
= ReadOnly
;
1918 rd
->read_status
= 'W';
1920 if(rd
->t
.i
.stream
->nmsgs
< 2)
1921 return(rd_init_remote(rd
, 0));
1922 else if(rd_chk_for_hdr_msg(&(rd
->t
.i
.stream
), rd
, &eptr
)){
1923 q_status_message1(SM_ORDER
| SM_DING
, 5, 5,
1924 _("Can't initialize \"%s\" (invalid format)"), rd
->rn
);
1927 q_status_message1(SM_ORDER
, 3, 5, "%s", eptr
);
1928 dprint((1, "%s\n", eptr
));
1929 fs_give((void **)&eptr
);
1933 "Can't initialize remote data \"%s\"\n",
1934 rd
->rn
? rd
->rn
: "?"));
1938 we_cancel
= busy_cue(_("Copying remote data"), NULL
, 1);
1940 if(rd
->flags
& NO_FILE
){
1941 store
= rd
->sonofile
;
1942 so_truncate(store
, 0L);
1945 if(!(tempfile
= tempfile_in_same_dir(rd
->lf
, "a8", NULL
))){
1946 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
1947 _("Error opening temporary file: %s"),
1948 error_description(errno
));
1950 "rd_update_local: Error opening temporary file: %s\n",
1951 error_description(errno
)));
1955 /* Copy the data into tempfile */
1956 if((store
= so_get(FileStar
, tempfile
, WRITE_ACCESS
|OWNER_ONLY
))
1958 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1959 _("Error opening temporary file %s: %s"),
1960 tempfile
, error_description(errno
));
1962 "rd_update_local: Error opening temporary file: %s: %s\n",
1963 tempfile
? tempfile
: "?",
1964 error_description(errno
)));
1965 our_unlink(tempfile
);
1966 fs_give((void **)&tempfile
);
1972 * Copy from the last message in the folder.
1974 if(!pine_mail_fetchstructure(rd
->t
.i
.stream
, rd
->t
.i
.stream
->nmsgs
,
1976 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1977 _("Can't access remote IMAP data"));
1978 dprint((2, "Can't access remote IMAP data\n"));
1980 our_unlink(tempfile
);
1981 fs_give((void **)&tempfile
);
1984 if(!(rd
->flags
& NO_FILE
))
1988 cancel_busy_cue(-1);
1994 body
->type
!= REMOTE_DATA_TYPE
||
1996 strucmp(body
->subtype
, rd
->t
.i
.special_hdr
)){
1997 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1998 _("Remote IMAP folder has wrong contents"));
2000 "Remote IMAP folder has wrong contents\n"));
2002 our_unlink(tempfile
);
2003 fs_give((void **)&tempfile
);
2006 if(!(rd
->flags
& NO_FILE
))
2010 cancel_busy_cue(-1);
2015 if(!(env
= pine_mail_fetchenvelope(rd
->t
.i
.stream
,
2016 rd
->t
.i
.stream
->nmsgs
))){
2017 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2018 _("Can't access check date in remote data"));
2020 "Can't access check date in remote data\n"));
2022 our_unlink(tempfile
);
2023 fs_give((void **)&tempfile
);
2026 if(!(rd
->flags
& NO_FILE
))
2030 cancel_busy_cue(-1);
2035 if(rd
&& rd
->flags
& USER_SAID_YES
)
2038 chk
= rd_check_for_suspect_data(rd
);
2041 case -1: /* suspicious data, user says abort */
2043 our_unlink(tempfile
);
2044 fs_give((void **)&tempfile
);
2047 if(!(rd
->flags
& NO_FILE
))
2051 cancel_busy_cue(-1);
2055 case 1: /* suspicious data, user says go ahead */
2056 if(rd_remote_is_readonly(rd
)){
2058 "rd_update_local: can't upgrade, readonly\n"));
2061 /* attempt to upgrade cookie in last message */
2062 (void)rd_upgrade_cookies(rd
, 0, 1);
2066 case 0: /* all is ok */
2072 /* store may have been written to at this point, so we'll clear it out */
2073 so_truncate(store
, 0L);
2075 gf_set_so_writec(&pc
, store
);
2077 error
= detach(rd
->t
.i
.stream
, rd
->t
.i
.stream
->nmsgs
, "1", 0L,
2078 NULL
, pc
, NULL
, DT_NODFILTER
);
2080 gf_clear_so_writec(store
);
2084 cancel_busy_cue(-1);
2086 if(!(rd
->flags
& NO_FILE
)){
2087 if(so_give(&store
)){
2088 snprintf(ebuf
, sizeof(ebuf
), _("Error writing temp file: %s"),
2089 error_description(errno
));
2095 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
2096 _("%s: Error copying remote IMAP data"), error
);
2097 dprint((2, "rd_update_local: Error copying: %s\n",
2098 error
? error
: "?"));
2100 our_unlink(tempfile
);
2101 fs_give((void **)&tempfile
);
2107 if(tempfile
&& (i
= rename_file(tempfile
, rd
->lf
)) < 0){
2110 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
2111 _("Error updating local file: %s: %s"),
2112 rd
->lf
, error_description(errno
));
2113 q_status_message(SM_ORDER
, 3, 4,
2114 _("Perhaps another process has the file open?"));
2116 "Rename_file(%s,%s): %s: returned -5, another process has file open?\n",
2117 tempfile
? tempfile
: "?",
2118 rd
->lf
? rd
->lf
: "?",
2119 error_description(errno
)));
2122 #endif /* _WINDOWS */
2124 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
2125 _("Error updating cache file %s: %s"),
2126 rd
->lf
, error_description(errno
));
2128 "Error updating cache file %s: rename(%s,%s): %s\n",
2129 tempfile
? tempfile
: "?",
2130 tempfile
? tempfile
: "?",
2131 rd
->lf
? rd
->lf
: "?",
2132 error_description(errno
)));
2135 our_unlink(tempfile
);
2136 fs_give((void **)&tempfile
);
2141 "in rd_update_local, setting chk_date to ->%s<-\n",
2142 env
->date
? (char *)env
->date
: "?"));
2143 rd_update_metadata(rd
, (char *) env
->date
);
2145 /* turn off out of date flag */
2146 rd
->flags
&= ~REM_OUTOFDATE
;
2149 fs_give((void **)&tempfile
);
2154 q_status_message1(SM_ORDER
| SM_DING
, 5, 5,
2155 _("Can't open remote IMAP folder \"%s\""), rd
->rn
);
2157 "Can't open remote IMAP folder \"%s\"\n",
2158 rd
->rn
? rd
->rn
: "?"));
2159 rd
->access
= ReadOnly
;
2166 dprint((1, "rd_update_local: Unsupported type\n"));
2173 * Copy local data to remote folder.
2175 * Args lf -- Local file name
2177 * header_to_check -- Name of indicator header in remote folder
2178 * imapstuff -- Structure holding info about connection
2179 * returndate -- Pointer to the date that was stored in the remote folder
2181 * Returns !=0 on failure
2185 rd_update_remote(REMDATA_S
*rd
, char *returndate
)
2189 long openmode
= SP_USEPOOL
| SP_TEMPUSE
;
2193 if(rd
&& rd
->type
!= RemImap
){
2194 dprint((1, "rd_update_remote: type not supported\n"));
2198 if(!rd
|| !(rd
->flags
& NO_FILE
|| rd
->lf
) || !rd
->rn
||
2199 !rd
->so
|| !rd
->t
.i
.special_hdr
){
2201 "rd_update_remote: Unexpected error: %s is NULL\n",
2203 !(rd
->flags
& NO_FILE
|| rd
->lf
) ? "localfile" :
2204 !rd
->rn
? "remotename" :
2206 !rd
->t
.i
.special_hdr
? "special_hdr" : "?"));
2210 dprint((7, " - rd_update_remote(%s): %s => %s\n",
2211 rd
->t
.i
.special_hdr
? rd
->t
.i
.special_hdr
: "?",
2212 rd
->lf
? rd
->lf
: "<mem>",
2213 rd
->rn
? rd
->rn
: "?"));
2215 if(!(st
= rd
->t
.i
.stream
))
2216 st
= adrbk_handy_stream(rd
->rn
);
2219 st
= rd
->t
.i
.stream
= context_open(NULL
, NULL
, rd
->rn
, openmode
, NULL
);
2222 q_status_message1(SM_ORDER
| SM_DING
, 5, 5,
2223 _("Can't open \"%s\" for copying"), rd
->rn
);
2225 "rd_update_remote: Can't open remote folder \"%s\" for copying\n",
2226 rd
->rn
? rd
->rn
: "?"));
2230 rd
->last_use
= get_adj_time();
2231 err
= rd_chk_for_hdr_msg(&st
, rd
, &eptr
);
2233 q_status_message1(SM_ORDER
| SM_DING
, 5, 5,
2234 _("\"%s\" has invalid format"), rd
->rn
);
2237 q_status_message1(SM_ORDER
, 3, 5, "%s", eptr
);
2238 dprint((1, "%s\n", eptr
));
2239 fs_give((void **)&eptr
);
2243 "rd_update_remote: \"%s\" has invalid format\n",
2244 rd
->rn
? rd
->rn
: "?"));
2251 * The data that will be going to the remote folder rn is
2252 * written into the following storage object and then copied to
2253 * the remote folder from there.
2256 if(rd
->flags
& NO_FILE
){
2257 store
= rd
->sonofile
;
2259 so_seek(store
, 0L, 0); /* rewind */
2262 store
= so_get(FileStar
, rd
->lf
, READ_ACCESS
);
2267 unsigned char last_c
= 0;
2269 /* Reset the storage object, since we may have already used it. */
2270 if(so_truncate(rd
->so
, 0L) == 0)
2275 "in rd_update_remote, storing date ->%s<-\n",
2276 date
? date
: "?"));
2277 if(!err
&& rd_store_fake_hdrs(rd
, "Pine Remote Data Container",
2278 rd
->t
.i
.special_hdr
, date
))
2281 /* save the date for later comparisons */
2282 if(!err
&& returndate
)
2283 strncpy(returndate
, date
, 100);
2285 /* Write the data */
2286 while(!err
&& so_readc(&c
, store
)){
2288 * C-client expects CRLF-terminated lines. Convert them
2289 * as we copy into c-client. Read ahead isn't available.
2290 * Leave CRLF as is, convert LF to CRLF, leave CR as is.
2292 if(last_c
!= '\r' && c
== '\n'){
2293 /* Convert \n to CRFL */
2294 if(so_writec('\r', rd
->so
) == 0 || so_writec('\n', rd
->so
) == 0)
2301 if(so_writec((int) c
, rd
->so
) == 0)
2307 * Take that message from so to the remote folder.
2308 * We append to that folder and always
2309 * use the final message as the active data.
2314 if((st
= rd
->t
.i
.stream
) != NULL
)
2315 rd
->t
.i
.shouldbe_nmsgs
= rd
->t
.i
.stream
->nmsgs
+ 1;
2317 st
= adrbk_handy_stream(rd
->rn
);
2319 err
= write_fcc(rd
->rn
, NULL
, rd
->so
, st
,
2320 "remote data", NULL
) ? 0 : 1;
2324 if(!(rd
->flags
& NO_FILE
))
2331 dprint((2, "error in rd_update_remote for %s => %s\n",
2332 rd
->lf
? rd
->lf
: "<mem>", rd
->rn
? rd
->rn
: "?"));
2339 * Check to see if the remote data has changed since we cached it.
2341 * Args rd -- REMDATA handle
2342 * do_it_now -- If > 0, check now regardless
2343 * If = 0, check if time since last chk more than default
2344 * If < 0, check if time since last chk more than -do_it_now
2347 rd_check_remvalid(REMDATA_S
*rd
, long int do_it_now
)
2349 time_t chk_interval
;
2350 long openmode
= SP_USEPOOL
| SP_TEMPUSE
;
2351 MAILSTREAM
*stat_stream
= NULL
;
2352 int we_cancel
= 0, got_cmsgs
= 0;
2353 unsigned long current_nmsgs
;
2355 dprint((7, "- rd_check_remvalid(%s) -\n",
2356 (rd
&& rd
->rn
) ? rd
->rn
: ""));
2358 if(rd
&& rd
->type
!= RemImap
){
2359 dprint((1, "rd_check_remvalid: type not supported\n"));
2363 if(!rd
|| rd
->flags
& REM_OUTOFDATE
|| rd
->flags
& USE_OLD_CACHE
)
2366 if(!rd
->t
.i
.chk_date
){
2368 "rd_check_remvalid: remote data %s changed (chk_date)\n",
2370 rd
->flags
|= REM_OUTOFDATE
;
2374 if(rd
->t
.i
.chk_nmsgs
<= 1){
2376 "rd_check_remvalid: remote data %s changed (chk_nmsgs <= 1)\n",
2377 rd
->rn
? rd
->rn
: "?"));
2378 rd
->flags
|= REM_OUTOFDATE
;
2383 chk_interval
= -1L * do_it_now
;
2387 chk_interval
= ps_global
->remote_abook_validity
* 60L;
2389 /* too soon to check again */
2391 (chk_interval
== 0L ||
2392 get_adj_time() <= rd
->last_valid_chk
+ chk_interval
))
2395 if(rd
->access
== ReadOnly
)
2396 openmode
|= OP_READONLY
;
2398 rd
->last_valid_chk
= get_adj_time();
2399 mm_status_result
.flags
= 0L;
2401 /* make sure the cache file is still there */
2402 if(rd
->lf
&& can_access(rd
->lf
, READ_ACCESS
) != 0){
2404 "rd_check_remvalid: %s: cache file %s disappeared\n",
2405 rd
->rn
? rd
->rn
: "?",
2406 rd
->lf
? rd
->lf
: "?"));
2407 rd
->flags
|= REM_OUTOFDATE
;
2412 * If the stream is open we should check there instead of using
2413 * a STATUS command. Check to see if it is really still alive with the
2414 * ping. It would be convenient if we could use a status command
2415 * on the open stream but apparently that won't work everywhere.
2419 try_looking_in_stream
:
2422 * Get the current number of messages in the folder to
2423 * compare with our saved number of messages.
2427 dprint((7, "using open remote data stream\n"));
2428 rd
->last_use
= get_adj_time();
2429 current_nmsgs
= rd
->t
.i
.stream
->nmsgs
;
2435 * Try to use the imap status command
2436 * to get the information as cheaply as possible.
2437 * If NO_STATUSCMD is set we just bypass all this stuff.
2440 if(!(rd
->flags
& NO_STATUSCMD
))
2441 stat_stream
= adrbk_handy_stream(rd
->rn
);
2444 * If we don't have a stream to use for the status command we
2445 * skip it and open the folder instead. Then, next time we want to
2446 * check we'll probably be able to use that open stream instead of
2447 * opening another one each time for the status command.
2450 if(!LEVELSTATUS(stat_stream
)){
2451 rd
->flags
|= NO_STATUSCMD
;
2453 "rd_check_remvalid: remote data %s: server doesn't support status\n",
2454 rd
->rn
? rd
->rn
: "?"));
2458 * This sure seems like a crock. We have to check to
2459 * see if the stream is actually open to the folder
2460 * we want to do the status on because c-client can't
2461 * do a status on an open folder. In this case, we fake
2462 * the status command results ourselves.
2463 * If we're so unlucky as to get back a stream that will
2464 * work for the status command while we also have another
2465 * stream that is rd->rn and we don't pick up on that,
2468 if(same_stream_and_mailbox(rd
->rn
, stat_stream
)){
2470 "rd_check_remvalid: faking status\n"));
2471 mm_status_result
.flags
= SA_MESSAGES
| SA_UIDVALIDITY
2473 mm_status_result
.messages
= stat_stream
->nmsgs
;
2474 mm_status_result
.uidvalidity
= stat_stream
->uid_validity
;
2475 mm_status_result
.uidnext
= stat_stream
->uid_last
+1;
2480 "rd_check_remvalid: trying status\n"));
2481 ps_global
->noshow_error
= 1;
2482 if(!pine_mail_status(stat_stream
, rd
->rn
,
2483 SA_UIDVALIDITY
| SA_UIDNEXT
| SA_MESSAGES
)){
2484 /* failed, mark it so we won't try again */
2485 rd
->flags
|= NO_STATUSCMD
;
2487 "rd_check_remvalid: addrbook %s: status command failed\n",
2488 rd
->rn
? rd
->rn
: "?"));
2489 mm_status_result
.flags
= 0L;
2493 ps_global
->noshow_error
= 0;
2497 /* if we got back a result from the status command, use it */
2498 if(mm_status_result
.flags
){
2500 "rd_check_remvalid: got status_result 0x%x\n",
2501 mm_status_result
.flags
));
2502 if(mm_status_result
.flags
& SA_MESSAGES
){
2503 current_nmsgs
= mm_status_result
.messages
;
2510 * Check current_nmsgs versus what we think it should be.
2511 * If they're different we know things have changed and we can
2512 * return now. If they're the same we don't know.
2514 if(got_cmsgs
&& current_nmsgs
!= rd
->t
.i
.chk_nmsgs
){
2515 rd
->flags
|= REM_OUTOFDATE
;
2517 "rd_check_remvalid: remote data %s changed (current msgs (%ld) != chk_nmsgs (%ld))\n",
2518 rd
->rn
? rd
->rn
: "?", current_nmsgs
, rd
->t
.i
.chk_nmsgs
));
2523 * Get the current uidvalidity and uidnext values from the
2524 * folder to compare with our saved values.
2527 if(rd
->t
.i
.stream
->uid_validity
== rd
->t
.i
.uidvalidity
){
2529 * Uid is valid so we just have to check whether or not the
2530 * uid of the last message has changed or not and return.
2532 if(mail_uid(rd
->t
.i
.stream
, rd
->t
.i
.stream
->nmsgs
) != rd
->t
.i
.uid
){
2533 /* uid has changed so we're out of date */
2534 rd
->flags
|= REM_OUTOFDATE
;
2536 "rd_check_remvalid: remote data %s changed (uid)\n",
2537 rd
->rn
? rd
->rn
: "?"));
2540 dprint((7,"rd_check_remvalid: uids match\n"));
2547 * If the uidvalidity changed that probably means it can't
2548 * be relied on to be meaningful, so don't use it in the future.
2550 rd
->flags
|= NO_STATUSCMD
;
2552 "rd_check_remvalid: remote data %s uidvalidity changed, don't use uid\n",
2553 rd
->rn
? rd
->rn
: "?"));
2556 else{ /* stream not open, use status results */
2557 if(mm_status_result
.flags
& SA_UIDVALIDITY
&&
2558 mm_status_result
.flags
& SA_UIDNEXT
&&
2559 mm_status_result
.uidvalidity
== rd
->t
.i
.uidvalidity
){
2565 if(mm_status_result
.uidnext
== rd
->t
.i
.uidnext
){
2567 * Uidnext valid and unchanged, so the folder is unchanged.
2569 dprint((7, "rd_check_remvalid: uidnexts match\n"));
2572 else{ /* uidnext changed, folder _may_ have changed */
2575 "rd_check_remvalid: uidnexts don't match, ours=%lu status=%lu\n",
2576 rd
->t
.i
.uidnext
, mm_status_result
.uidnext
));
2579 * Since c-client can't handle a status cmd on the selected
2580 * mailbox, we may have had to guess at the value of uidnext,
2581 * and we don't know for sure that this is a real change.
2582 * We need to open the mailbox and find out for sure.
2584 we_cancel
= busy_cue(NULL
, NULL
, 1);
2585 ps_global
->noshow_error
= 1;
2586 if((rd
->t
.i
.stream
= context_open(NULL
, NULL
, rd
->rn
, openmode
,
2588 imapuid_t last_msg_uid
;
2590 if(rd
->t
.i
.stream
->rdonly
)
2591 rd
->read_status
= 'R';
2593 last_msg_uid
= mail_uid(rd
->t
.i
.stream
,
2594 rd
->t
.i
.stream
->nmsgs
);
2595 ps_global
->noshow_error
= 0;
2596 rd
->last_use
= get_adj_time();
2598 "%s: opened to check uid (%ld)\n",
2599 rd
->rn
? rd
->rn
: "?", (long)rd
->last_use
));
2600 if(last_msg_uid
!= rd
->t
.i
.uid
){ /* really did change */
2601 rd
->flags
|= REM_OUTOFDATE
;
2603 "rd_check_remvalid: remote data %s changed, our uid=%lu real uid=%lu\n",
2604 rd
->rn
? rd
->rn
: "?",
2605 rd
->t
.i
.uid
, last_msg_uid
));
2607 else{ /* false hit */
2609 * The uid of the last message is the same as we
2610 * remembered, so the problem is that our guess
2611 * for the nextuid was wrong. It didn't actually
2612 * change. Since we know the correct uidnext now we
2613 * can reset that guess to the correct value for
2614 * next time, avoiding this extra mail_open.
2617 "rd_check_remvalid: remote data %s false change: adjusting uidnext from %lu to %lu\n",
2618 rd
->rn
? rd
->rn
: "?",
2620 mm_status_result
.uidnext
));
2621 rd
->t
.i
.uidnext
= mm_status_result
.uidnext
;
2622 rd_write_metadata(rd
, 0);
2626 cancel_busy_cue(-1);
2631 ps_global
->noshow_error
= 0;
2633 "rd_check_remvalid: couldn't open %s\n",
2634 rd
->rn
? rd
->rn
: "?"));
2640 * If the status command doesn't return these or
2641 * if the uidvalidity is changing that probably means it can't
2642 * be relied on to be meaningful, so don't use it.
2644 * We also come here if we don't have a stat_stream handy to
2645 * look up the status. This happens, for example, if our
2646 * address book is on a different server from the open mail
2647 * folders. In that case, instead of opening a stream,
2648 * doing a status command, and closing the stream, we open
2649 * the stream and use it to check next time, too.
2652 rd
->flags
|= NO_STATUSCMD
;
2654 "rd_check_remvalid: remote data %s don't use status\n",
2655 rd
->rn
? rd
->rn
: "?"));
2659 "opening remote data stream for validity check\n"));
2660 we_cancel
= busy_cue(NULL
, NULL
, 1);
2661 ps_global
->noshow_error
= 1;
2662 rd
->t
.i
.stream
= context_open(NULL
, NULL
, rd
->rn
, openmode
,
2664 ps_global
->noshow_error
= 0;
2666 cancel_busy_cue(-1);
2670 goto try_looking_in_stream
;
2673 "rd_check_remvalid: cannot open remote mailbox\n"));
2680 * If we got here that means that we still don't know if the folder
2681 * changed or not. If the stream is open then it must be the case that
2682 * uidvalidity changed so we can't rely on using uids. If the stream
2683 * isn't open, then either the status command didn't work or the
2684 * uidvalidity changed. In any case, we need to fall back to looking
2685 * inside the folder at the last message and checking whether or not the
2686 * Date of the last message is the one we remembered.
2690 "rd_check_remvalid: falling back to Date check\n"));
2693 * Fall back to looking in the folder at the Date header.
2697 we_cancel
= busy_cue(NULL
, NULL
, 1);
2699 ps_global
->noshow_error
= 1;
2700 if(rd
->t
.i
.stream
||
2701 (rd
->t
.i
.stream
= context_open(NULL
,NULL
,rd
->rn
,openmode
,NULL
))){
2702 ENVELOPE
*env
= NULL
;
2704 if(rd
->t
.i
.stream
->rdonly
)
2705 rd
->read_status
= 'R';
2707 if(rd
->t
.i
.stream
->nmsgs
!= rd
->t
.i
.chk_nmsgs
){
2708 rd
->flags
|= REM_OUTOFDATE
;
2710 "rd_check_remvalid: remote data %s changed (expected nmsgs %ld, got %ld)\n",
2711 rd
->rn
? rd
->rn
: "?",
2712 rd
->t
.i
.chk_nmsgs
, rd
->t
.i
.stream
->nmsgs
));
2714 else if(rd
->t
.i
.stream
->nmsgs
> 1){
2715 env
= pine_mail_fetchenvelope(rd
->t
.i
.stream
,rd
->t
.i
.stream
->nmsgs
);
2717 if(!env
|| (env
->date
&& strucmp((char *) env
->date
, rd
->t
.i
.chk_date
))){
2718 rd
->flags
|= REM_OUTOFDATE
;
2720 "rd_check_remvalid: remote data %s changed (%s)\n",
2721 rd
->rn
? rd
->rn
: "?", env
? "date" : "not enough msgs"));
2725 rd
->last_use
= get_adj_time();
2728 "%s: got envelope to check date (%ld)\n",
2729 rd
->rn
? rd
->rn
: "?", (long)rd
->last_use
));
2731 /* else, we give up and go with the cache copy */
2733 ps_global
->noshow_error
= 0;
2736 cancel_busy_cue(-1);
2738 dprint((7, "rd_check_remvalid: falling off end\n"));
2743 * Returns nonzero if remote data is currently readonly.
2745 * Returns 0 -- apparently writeable
2746 * 1 -- stream not open
2747 * 2 -- stream open but not writeable
2750 rd_remote_is_readonly(REMDATA_S
*rd
)
2752 if(!rd
|| rd
->access
== ReadOnly
|| !rd_stream_exists(rd
))
2757 if(rd
->t
.i
.stream
->rdonly
)
2763 q_status_message(SM_ORDER
, 3, 5,
2764 "rd_remote_is_readonly: type not supported");
2774 * -1 if not ok and user says No
2775 * 1 if not ok but user says Yes, ok to use it
2778 rd_check_for_suspect_data(REMDATA_S
*rd
)
2781 char *fields
[3], *values
[3], *h
;
2782 unsigned long cookie
;
2784 if(!rd
|| rd
->type
!= RemImap
|| !rd
->so
|| !rd
->rn
|| !rd
->t
.i
.special_hdr
)
2787 fields
[0] = rd
->t
.i
.special_hdr
;
2788 fields
[1] = "received";
2791 if((h
=pine_fetchheader_lines(rd
->t
.i
.stream
, rd
->t
.i
.stream
->nmsgs
,
2792 NULL
, fields
)) != NULL
){
2793 simple_header_parse(h
, fields
, values
);
2794 if(values
[1]) /* Received lines present! */
2795 ans
= rd_prompt_about_forged_remote_data(-1, rd
, NULL
);
2797 cookie
= strtoul(values
[0], (char **)NULL
, 10);
2798 if(cookie
== rd
->cookie
) /* all's well */
2801 ans
= rd_prompt_about_forged_remote_data(cookie
> 1L
2806 ans
= rd_prompt_about_forged_remote_data(-2, rd
, NULL
);
2809 fs_give((void **)&values
[0]);
2812 fs_give((void **)&values
[1]);
2814 fs_give((void **)&h
);
2816 else /* missing magic header */
2817 ans
= rd_prompt_about_forged_remote_data(-2, rd
, NULL
);