* For mailing lists, Alpine adds a description of the type of link
[alpine.git] / pith / remote.c
blobfeddf1c2aa7d912c5f2643ed843659d3e8d66872
1 /*
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 /*======================================================================
16 remote.c
17 Implements remote IMAP config files (remote config, remote abook).
18 ====*/
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"
40 * Internal prototypes
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);
56 char *
57 read_remote_pinerc(PINERC_S *prc, ParsePinerc which_vars)
59 int try_cache, no_perm_create_pass = 0;
60 char *file = NULL;
61 unsigned flags;
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){
79 if(prc->rd){
80 prc->rd->flags &= ~DO_REMTRIM;
81 rd_close_remdata(&prc->rd);
84 /* this will cause the remote folder to be created */
85 flags = 0;
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,
94 &flags, _("Error: "),
95 _(" Can't fetch remote configuration."));
96 if(!prc->rd)
97 goto bail_out;
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';
116 else
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;
140 else
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;
158 dprint((1,
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.
165 else{
166 dprint((7,
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){
179 if(try_cache){
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");
184 dprint((2,
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 : "?"));
189 else{
190 prc->rd->flags &= ~DO_REMTRIM;
191 goto bail_out;
195 if(prc->rd->flags & NO_FILE)
196 /* copy text, leave sonofile for later use */
197 file = cpystr((char *)so_text(prc->rd->sonofile));
198 else
199 file = read_file(prc->rd->lf, 0);
201 bail_out:
202 if((which_vars == ParsePers || which_vars == ParsePersPost) &&
203 (!file || !prc->rd || prc->rd->access != ReadWrite)){
204 prc->readonly = 1;
205 if(prc == ps_global->prc)
206 ps_global->readonly_pinerc = 1;
209 return(file);
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.
218 * remote_name --
219 * type_spec -- Type-specific data.
220 * flags --
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.
227 REMDATA_S *
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);
238 if(flags)
239 rd->flags = *flags;
241 switch(rd->type){
242 case RemImap:
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);
247 if(!rd->sonofile)
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;
262 else{
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;
277 break;
279 default:
280 q_status_message(SM_ORDER, 3,5, "rd_create_remote: type not supported");
281 break;
284 return(rd);
288 REMDATA_S *
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));
296 rd->type = type;
297 rd->access = NoExists;
299 if(remote_name)
300 rd->rn = cpystr(remote_name);
302 switch(rd->type){
303 case RemImap:
304 if(type_spec)
305 rd->t.i.special_hdr = cpystr(type_spec);
307 break;
309 default:
310 q_status_message(SM_ORDER, 3,5, "rd_new_remdata: type not supported");
311 break;
314 return(rd);
319 * Closes the remote stream and frees.
321 void
322 rd_free_remdata(REMDATA_S **rd)
324 if(rd && *rd){
325 rd_close_remote(*rd);
327 if((*rd)->rn)
328 fs_give((void **)&(*rd)->rn);
330 if((*rd)->lf)
331 fs_give((void **)&(*rd)->lf);
333 if((*rd)->so){
334 so_give(&(*rd)->so);
335 (*rd)->so = NULL;
338 if((*rd)->sonofile){
339 so_give(&(*rd)->sonofile);
340 (*rd)->sonofile = NULL;
343 switch((*rd)->type){
344 case RemImap:
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);
351 break;
353 default:
354 q_status_message(SM_ORDER, 3, 5,
355 "rd_free_remdata: type not supported");
356 break;
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
367 * and frees the rd.
369 void
370 rd_trim_remdata(REMDATA_S **rd)
372 if(!(rd && *rd))
373 return;
375 switch((*rd)->type){
376 case RemImap:
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
382 * data.
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 */
389 rd_ping_stream(*rd);
390 rd_open_remote(*rd);
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){
396 char sequence[20];
397 int user_deleted = 0;
400 * If user manually deleted some, we'd better not delete
401 * any more.
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);
411 else
412 user_deleted++;
414 mail_expunge((*rd)->t.i.stream);
416 if(!user_deleted)
417 rd_update_metadata(*rd, NULL);
418 /* else
419 * don't update metafile because user is messing with
420 * the remote folder manually. We'd better re-read it next
421 * time. */
425 ps_global->noshow_error = 0;
428 break;
430 default:
431 q_status_message(SM_ORDER, 3,5, "rd_trim_remdata: type not supported");
432 break;
438 * All done with this remote data. Trim the folder, close the
439 * stream, and free.
441 void
442 rd_close_remdata(REMDATA_S **rd)
444 if(!(rd && *rd))
445 return;
447 rd_trim_remdata(rd);
449 if((*rd)->lf && (*rd)->flags & DEL_FILE)
450 our_unlink((*rd)->lf);
452 /* this closes the stream and frees memory */
453 rd_free_remdata(rd);
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.
465 * 0 otherwise.
468 rd_read_metadata(REMDATA_S *rd)
470 REMDATA_META_S *rab = NULL;
471 int try_cache = 0;
473 dprint((7, "rd_read_metadata \"%s\"\n",
474 (rd && rd->rn) ? rd->rn : "?"));
476 if(!rd)
477 return try_cache;
479 if(rd->flags & NO_PERM_CACHE)
480 rab = NULL;
481 else
482 rab = rd_find_our_metadata(rd->rn, &rd->flags);
484 if(!rab){
485 if(!rd->lf){
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;
492 /* display error */
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];
503 char *lc;
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){
513 int to_copy;
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';
520 else{
521 dir[0] = '.';
522 dir[1] = '\0';
525 build_path(path, dir, rab->local_cache_file, sizeof(path));
526 rd->lf = cpystr(path);
528 else{
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;
536 switch(rd->type){
537 case RemImap:
538 rd->t.i.chk_date = rab->date;
539 rab->date = NULL; /* don't free this below, we're using it */
541 dprint((7,
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;
549 dprint((7,
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',
553 rd->t.i.chk_nmsgs));
554 break;
556 default:
557 q_status_message(SM_ORDER, 3, 5,
558 "rd_read_metadata: type not supported");
559 break;
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.
569 else{
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,
575 meta_prefix, NULL);
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;
581 else
582 rd->lf = temp_nam(NULL, "a7");
585 if(rab){
586 if(rab->local_cache_file)
587 fs_give((void **)&rab->local_cache_file);
588 if(rab->date)
589 fs_give((void **)&rab->date);
590 fs_give((void **)&rab);
593 return(try_cache);
598 * Write out the contents of the metadata file.
600 * Each line should be: folder_name cache_file uidvalidity uidnext uid nmsgs
601 * read_status date
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.
610 void
611 rd_write_metadata(REMDATA_S *rd, int delete_it)
613 char *tempfile;
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];
618 int fd;
620 dprint((7, "rd_write_metadata \"%s\"\n",
621 (rd && rd->rn) ? rd->rn : "?"));
623 if(!rd || rd->flags & NO_META_UPDATE)
624 return;
626 if(rd->type != RemImap){
627 q_status_message(SM_ORDER, 3, 5,
628 "rd_write_metadata: type not supported");
629 return;
632 dprint((9, " - rd_write_metadata: rn=%s lf=%s\n",
633 rd->rn ? rd->rn : "?", rd->lf ? rd->lf : "?"));
635 key = rd->rn;
637 if(!(pith_opt_rd_metadata_name && (metafile = (*pith_opt_rd_metadata_name)())))
638 goto io_err;
640 if(!(tempfile = tempfile_in_same_dir(metafile, "a9", &pinerc_dir)))
641 goto io_err;
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;
648 else
649 rel_filename = rd->lf;
651 if(pinerc_dir)
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)
662 goto io_err;
664 while((p = fgets(line, sizeof(line), fp_old)) != NULL){
666 * Skip the header line and any lines that don't begin
667 * with a "{".
669 if(line[0] != '{')
670 continue;
672 /* skip the old cache line for this key */
673 if(strncmp(line, key, strlen(key)) == 0 && line[strlen(key)] == TAB)
674 continue;
676 /* add this line to new version of file */
677 if(fputs(p, fp_new) == EOF)
678 goto io_err;
682 /* add the cache line for this key */
683 /* Warning: this is type RemImap specific right now! */
684 if(!delete_it){
685 if(!tempfile ||
686 !fp_old ||
687 !fp_new ||
688 p ||
689 fprintf(fp_new, "%s\t%s\t%lu\t%lu\t%lu\t%lu\t%c\t%s\n",
690 key ? key : "",
691 rel_filename ? rel_filename : "",
692 rd->t.i.uidvalidity, rd->t.i.uidnext, rd->t.i.uid,
693 rd->t.i.chk_nmsgs,
694 rd->read_status ? rd->read_status : '?',
695 rd->t.i.chk_date ? rd->t.i.chk_date : "no-match")
696 == EOF)
697 goto io_err;
700 if(fclose(fp_new) == EOF){
701 fp_new = NULL;
702 goto io_err;
705 if(fclose(fp_old) == EOF){
706 fp_old = NULL;
707 goto io_err;
710 if(rename_file(tempfile, metafile) < 0)
711 goto io_err;
713 if(tempfile)
714 fs_give((void **)&tempfile);
716 if(metafile)
717 fs_give((void **)&metafile);
719 return;
721 io_err:
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));
728 if(tempfile){
729 our_unlink(tempfile);
730 fs_give((void **)&tempfile);
732 if(metafile)
733 fs_give((void **)&metafile);
734 if(fp_old)
735 (void)fclose(fp_old);
736 if(fp_new)
737 (void)fclose(fp_new);
741 void
742 rd_update_metadata(REMDATA_S *rd, char *date)
744 if(!rd)
745 return;
747 dprint((9, " - rd_update_metadata: rn=%s lf=%s\n",
748 rd->rn ? rd->rn : "?", rd->lf ? rd->lf : "<none>"));
750 switch(rd->type){
751 case RemImap:
752 if(rd->t.i.stream){
753 ps_global->noshow_error = 1;
754 rd_ping_stream(rd);
755 if(rd->t.i.stream){
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
760 * to see those.
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))){
769 rd_close_remote(rd);
770 rd_open_remote(rd);
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;
781 rd->t.i.uid = 0L;
782 rd->t.i.uidnext = 0L;
784 else{
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)
798 + 1;
802 ps_global->noshow_error = 0;
805 * Save the date so that we can check if it changed next time
806 * we go to write.
808 if(date){
809 if(rd->t.i.chk_date)
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;
820 break;
822 default:
823 q_status_message(SM_ORDER, 3, 5,
824 "rd_update_metadata: type not supported");
825 break;
832 REMDATA_META_S *
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;
838 FILE *fp;
840 dprint((9, "rd_find_our_metadata \"%s\"\n", key ? key : "?"));
842 if(!(key && *key))
843 return rab;
845 if(!(pith_opt_rd_metadata_name && (metafile = (*pith_opt_rd_metadata_name)()) != NULL))
846 return rab;
849 * Open the metadata file and get some information out.
851 fp = our_fopen(metafile, "rb");
852 if(fp == NULL){
853 q_status_message2(SM_ORDER, 3, 5,
854 _("can't open metadata file %s, continuing (%s)"),
855 metafile, error_description(errno));
856 dprint((2,
857 "can't open existing metadata file %s: %s\n",
858 metafile ? metafile : "?", error_description(errno)));
859 if(flags)
860 (*flags) |= NO_META_UPDATE;
862 fs_give((void **)&metafile);
864 return rab;
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)){
881 dprint((2,
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){
887 int fd;
889 (void)fclose(fp);
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));
896 dprint((2,
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);
902 return NULL;
905 (void)close(fd);
907 else
908 (void)fclose(fp);
910 fs_give((void **)&metafile);
911 return(rab);
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)
919 break;
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 */
930 if(*p == TAB){
931 q = ++p; /* q points to cache_file */
932 SKIP_TO_TAB(p); /* skip to TAB following cache_file */
933 if(*p == TAB){
934 *p = '\0';
935 rab->local_cache_file = cpystr(q);
936 q = ++p; /* q points to uidvalidity */
937 SKIP_TO_TAB(p); /* skip to TAB following uidvalidity */
938 if(*p == TAB){
939 *p = '\0';
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 */
943 if(*p == TAB){
944 *p = '\0';
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 */
948 if(*p == TAB){
949 *p = '\0';
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 */
953 if(*p == TAB){
954 *p = '\0';
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 */
958 if(*p == TAB){
959 *p = '\0';
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 */
963 p++;
965 *p = '\0';
966 rab->date = cpystr(q);
976 (void)fclose(fp);
977 return(rab);
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)
995 return -1;
997 if(fp_file_size(fp) <
998 (long)(SIZEOF_PMAGIC + SIZEOF_SPACE + SIZEOF_VERSION_NUM))
999 return -1;
1001 if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0))
1002 return -1;
1004 /* check for magic */
1005 if(fread(buf, sizeof(char), (unsigned)SIZEOF_PMAGIC, fp) != SIZEOF_PMAGIC)
1006 return -1;
1008 buf[SIZEOF_PMAGIC] = '\0';
1009 if(strcmp(buf, PMAGIC) != 0)
1010 return -1;
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))
1020 return -1;
1022 if(fread(buf, sizeof(char), (unsigned)SIZEOF_VERSION_NUM, fp) !=
1023 SIZEOF_VERSION_NUM)
1024 return -1;
1026 buf[SIZEOF_VERSION_NUM] = '\0';
1027 if(strcmp(buf, METAFILE_VERSION_NUM) != 0)
1028 return -1;
1030 /* Position file pointer to second line */
1031 if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0))
1032 return -1;
1034 if(fgets(buf, sizeof(buf), fp) == NULL)
1035 return -1;
1037 return 0;
1042 * Open a data stream to the remote data.
1044 void
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 : "?"));
1052 if(!rd)
1053 return;
1055 if(rd_stream_exists(rd)){
1056 rd->last_use = get_adj_time();
1057 return;
1060 switch(rd->type){
1061 case RemImap:
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.) */
1071 if(!rd->t.i.stream)
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);
1079 if(rd->t.i.stream)
1080 rd->last_use = get_adj_time();
1082 break;
1084 default:
1085 q_status_message(SM_ORDER, 3, 5, "rd_open_remote: type not supported");
1086 break;
1092 * Close a data stream to the remote data.
1094 void
1095 rd_close_remote(REMDATA_S *rd)
1097 if(!rd || !rd_stream_exists(rd))
1098 return;
1100 switch(rd->type){
1101 case RemImap:
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;
1106 break;
1108 default:
1109 q_status_message(SM_ORDER, 3, 5, "rd_close_remote: type not supported");
1110 break;
1116 rd_stream_exists(REMDATA_S *rd)
1118 if(!rd)
1119 return(0);
1121 switch(rd->type){
1122 case RemImap:
1123 return(rd->t.i.stream ? 1 : 0);
1125 default:
1126 q_status_message(SM_ORDER, 3,5, "rd_stream_exists: type not supported");
1127 break;
1130 return(0);
1135 * Ping the stream and return 1 if it is still alive.
1138 rd_ping_stream(REMDATA_S *rd)
1140 int ret = 0;
1142 dprint((7, "rd_ping_stream \"%s\"\n",
1143 (rd && rd->rn) ? rd->rn : "?"));
1145 if(!rd)
1146 return(ret);
1148 if(!rd_stream_exists(rd)){
1149 switch(rd->type){
1150 case RemImap:
1152 * If this stream is already actually open, officially open
1153 * it and use it.
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);
1164 break;
1166 default:
1167 break;
1171 if(!rd_stream_exists(rd))
1172 return(ret);
1174 switch(rd->type){
1175 case RemImap:
1176 ps_global->noshow_error = 1;
1177 if(pine_mail_ping(rd->t.i.stream))
1178 ret = 1;
1179 else
1180 rd->t.i.stream = NULL;
1182 ps_global->noshow_error = 0;
1183 break;
1185 default:
1186 q_status_message(SM_ORDER, 3, 5, "rd_ping_stream: type not supported");
1187 break;
1190 return(ret);
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
1197 * the metadata.
1199 void
1200 rd_check_readonly_access(REMDATA_S *rd)
1202 if(rd && rd->read_status == 'R'){
1203 switch(rd->type){
1204 case RemImap:
1205 rd_open_remote(rd);
1206 if(rd->t.i.stream && !rd->t.i.stream->rdonly){
1207 rd->read_status = 'W';
1208 rd->access = ReadWrite;
1211 break;
1213 default:
1214 q_status_message(SM_ORDER, 3, 5,
1215 "rd_check_readonly_access: type not supported");
1216 break;
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
1226 * data message.
1227 * The remote folder already exists before we get here, though it may be empty.
1229 * Returns -1 on failure
1230 * 0 on success
1233 rd_init_remote(REMDATA_S *rd, int add_only_first_msg)
1235 int err = 0;
1236 char date[200];
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"));
1243 return -1;
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){
1253 dprint((1,
1254 "rd_init_remote: Unexpected error: %s is NULL\n",
1255 !(rd->flags & NO_FILE || rd->lf) ? "localfile" :
1256 !rd->rn ? "remotename" :
1257 !rd->so ? "so" :
1258 !rd->t.i.stream ? "stream" :
1259 !rd->t.i.special_hdr ? "special_hdr" : "?"));
1260 return -1;
1263 /* already init'd */
1264 if(rd->t.i.stream->nmsgs >= 2 ||
1265 (rd->t.i.stream->nmsgs >= 1 && add_only_first_msg))
1266 return err;
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"));
1289 dprint((1,
1290 "Can't initialize remote folder \"%s\"\n",
1291 rd->rn ? rd->rn : "?"));
1292 dprint((1,
1293 " No write permission for that remote folder.\n"));
1294 dprint((1,
1295 " Choose a new unused folder for the remote data.\n"));
1296 err = -1;
1299 if(!err){
1300 if(rd->t.i.stream->nmsgs == 0){
1301 int we_cancel;
1303 we_cancel = busy_cue(_("Initializing remote data"), NULL, 1);
1304 rfc822_date(date);
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
1311 * data folder.
1313 err = rd_add_hdr_msg(rd, date);
1314 if(rd->t.i.stream->nmsgs == 0)
1315 rd_ping_stream(rd);
1316 if(rd->t.i.stream && rd->t.i.stream->nmsgs == 0)
1317 pine_mail_check(rd->t.i.stream);
1319 if(we_cancel)
1320 cancel_busy_cue(-1);
1322 else{
1323 char *eptr = NULL;
1325 err = rd_chk_for_hdr_msg(&(rd->t.i.stream), rd, &eptr);
1326 if(err){
1327 q_status_message1(SM_ORDER | SM_DING, 5, 5,
1328 _("\"%s\" has invalid format, can't initialize"), rd->rn);
1330 dprint((1,
1331 "Can't initialize remote data \"%s\"\n",
1332 rd->rn ? rd->rn : "?"));
1334 if(eptr){
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;
1348 int fd = -1;
1350 if(rd->flags & NO_FILE){
1351 if(so_truncate(rd->sonofile, 0L) == 0)
1352 err = -1;
1354 else{
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)));
1361 err = -1;
1364 if(!err &&
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));
1369 dprint((2,
1370 "init_remote: Error opening temporary file: %s: %s\n",
1371 tempfile ? tempfile : "?", error_description(errno)));
1372 our_unlink(tempfile);
1373 err = -1;
1375 else
1376 (void)close(fd);
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);
1383 err = -1;
1386 if(tempfile)
1387 fs_give((void **)&tempfile);
1390 if(!err){
1391 err = rd_update_remote(rd, date);
1392 if(err){
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");
1403 if(!err){
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];
1425 char *h;
1426 int tried_again = 0;
1427 int ret;
1428 MAILSTREAM *st = NULL;
1430 fields[0] = rd->t.i.special_hdr;
1431 fields[1] = "received";
1432 fields[2] = NULL;
1434 try_again:
1435 ret = -1;
1437 if(!streamp || !*streamp){
1438 dprint((1, "rd_chk_for_hdr_msg: stream is null\n"));
1440 else if((*streamp)->nmsgs == 0){
1441 ret = -2;
1442 dprint((1,
1443 "rd_chk_for_hdr_msg: stream has nmsgs=0, try a ping\n"));
1444 if(!pine_mail_ping(*streamp))
1445 *streamp = NULL;
1447 if(*streamp && (*streamp)->nmsgs == 0){
1448 dprint((1,
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){
1454 dprint((1,
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);
1460 rd_open_remote(rd);
1461 if(rd_stream_exists(rd))
1462 st = rd->t.i.stream;
1465 if(!st)
1466 st = *streamp;
1468 if(st && st->nmsgs == 0){
1469 dprint((1,
1470 "rd_chk_for_hdr_msg: can't see header message\n"));
1473 else
1474 st = *streamp;
1476 if(st && st->nmsgs != 0
1477 && (h=pine_fetchheader_lines(st, 1L, NULL, fields))){
1478 simple_header_parse(h, fields, values);
1479 ret = -3;
1480 if(values[1])
1481 ret = -4;
1482 else if(values[0]){
1483 rd->cookie = strtoul(values[0], (char **)NULL, 10);
1484 if(rd->cookie == 0)
1485 ret = -5;
1486 else if(rd->cookie == 1){
1487 if(rd->flags & COOKIE_ONE_OK || tried_again)
1488 ret = 0;
1489 else
1490 ret = -6;
1492 else if(rd->cookie > 1)
1493 ret = 0;
1496 if(values[0])
1497 fs_give((void **)&values[0]);
1499 if(values[1])
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';
1511 if(ret == -1){
1512 /* null stream */
1513 if(errmsg)
1514 snprintf(*errmsg, 500, _("Can't open remote address book \"%s\""), rd->rn);
1516 else if(ret == -2){
1517 /* no messages in folder */
1518 if(errmsg)
1519 snprintf(*errmsg, 500,
1520 _("Error: no messages in remote address book \"%s\"!"),
1521 rd->rn);
1523 else if(ret == -3){
1524 /* no cookie */
1525 if(errmsg)
1526 snprintf(*errmsg, 500,
1527 "First msg in \"%s\" should have \"%s\" header",
1528 rd->rn, rd->t.i.special_hdr);
1530 else if(ret == -4){
1531 /* Received header */
1532 if(errmsg)
1533 snprintf(*errmsg, 500,
1534 _("Suspicious Received headers in first msg in \"%s\""),
1535 rd->rn);
1537 else if(ret == -5){
1539 /* cookie is 0 */
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 : "?"));
1548 ret = 0;
1550 else if(ret == -6){
1551 dprint((1,
1552 "rd_chk_for_hdr_msg: cookie is 1, try to upgrade it\n"));
1554 if(rd_remote_is_readonly(rd)){
1555 dprint((1,
1556 "rd_chk_for_hdr_msg: can't upgrade, readonly\n"));
1557 ret = 0; /* stick with 1 */
1559 else{
1560 /* cookie is 1, upgrade it */
1561 if(rd_upgrade_cookies(rd, st->nmsgs, 0) == 0){
1562 /* now check again */
1563 if(!tried_again){
1564 tried_again++;
1565 goto try_again;
1570 * This is actually a failure but we've decided that this
1571 * failure is ok.
1573 ret = 0;
1577 if(errmsg && *errmsg)
1578 dprint((1, "rd_chk_for_hdr_msg: %s\n", *errmsg));
1580 return ret;
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
1591 * Result: 0 success
1592 * -1 failure
1595 rd_add_hdr_msg(REMDATA_S *rd, char *date)
1597 int err = 0;
1599 if(!rd|| rd->type != RemImap || !rd->rn || !rd->so || !rd->t.i.special_hdr){
1600 dprint((1,
1601 "rd_add_hdr_msg: Unexpected error: %s is NULL\n",
1602 !rd ? "rd" :
1603 !rd->rn ? "remotename" :
1604 !rd->so ? "so" :
1605 !rd->t.i.special_hdr ? "special_hdr" : "?"));
1606 return -1;
1609 err = rd_store_fake_hdrs(rd, "Header Message for Remote Data",
1610 "plain", date);
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)
1616 err = -1;
1617 if(!err && so_puts(rd->so,
1618 "This message is just an explanatory message.\015\012") == 0)
1619 err = -1;
1620 if(!err && so_puts(rd->so,
1621 "The last message in the folder is the live addressbook data.\015\012") == 0)
1622 err = -1;
1623 if(!err && so_puts(rd->so,
1624 "The rest of the messages contain previous revisions of the addressbook data.\015\012") == 0)
1625 err = -1;
1626 if(!err && so_puts(rd->so,
1627 "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1628 err = -1;
1629 if(!err && so_puts(rd->so,
1630 "which come after it.\015\012") == 0)
1631 err = -1;
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)
1636 err = -1;
1637 if(!err && so_puts(rd->so,
1638 "This message is just an explanatory message.\015\012") == 0)
1639 err = -1;
1640 if(!err && so_puts(rd->so,
1641 "The last message in the folder is the live config data.\015\012") == 0)
1642 err = -1;
1643 if(!err && so_puts(rd->so,
1644 "The rest of the messages contain previous revisions of the data.\015\012") == 0)
1645 err = -1;
1646 if(!err && so_puts(rd->so,
1647 "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1648 err = -1;
1649 if(!err && so_puts(rd->so,
1650 "which come after it.\015\012") == 0)
1651 err = -1;
1653 else{
1654 if(!err && so_puts(rd->so,
1655 "This folder contains remote Alpine data.\015\012") == 0)
1656 err = -1;
1657 if(!err && so_puts(rd->so,
1658 "This message is just an explanatory message.\015\012") == 0)
1659 err = -1;
1660 if(!err && so_puts(rd->so,
1661 "The last message in the folder is the live data.\015\012") == 0)
1662 err = -1;
1663 if(!err && so_puts(rd->so,
1664 "The rest of the messages contain previous revisions of the data.\015\012") == 0)
1665 err = -1;
1666 if(!err && so_puts(rd->so,
1667 "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1668 err = -1;
1669 if(!err && so_puts(rd->so,
1670 "which come after it.\015\012") == 0)
1671 err = -1;
1674 /* Take the message from "so" to the remote folder */
1675 if(!err){
1676 MAILSTREAM *st;
1678 if((st = rd->t.i.stream) != NULL)
1679 rd->t.i.shouldbe_nmsgs = rd->t.i.stream->nmsgs + 1;
1680 else
1681 st = adrbk_handy_stream(rd->rn);
1683 err = write_fcc(rd->rn, NULL, rd->so, st, "remote data", NULL) ? 0 : -1;
1686 return err;
1691 * Write some fake header lines into storage object rd->so.
1693 * Args: rd
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)
1701 ENVELOPE *fake_env;
1702 BODY *fake_body;
1703 ADDRESS *fake_from;
1704 int err = 0;
1705 char vers[50], *p;
1706 unsigned long r = 0L;
1707 RFC822BUFFER rbuf;
1709 if(!rd|| rd->type != RemImap || !rd->so || !rd->t.i.special_hdr)
1710 return -1;
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");
1729 if(rd->cookie > 0)
1730 r = rd->cookie;
1732 if(!r){
1733 int i;
1735 for(i = 100; i > 0 && r < 1000000; i--)
1736 r = random();
1738 if(r < 1000000)
1739 r = 1712836L + getpid();
1741 rd->cookie = r;
1744 snprintf(vers, sizeof(vers), "%ld", r);
1746 p = tmp_20k_buf;
1747 *p = '\0';
1748 rbuf.f = dummy_soutr;
1749 rbuf.s = NULL;
1750 rbuf.beg = p;
1751 rbuf.cur = p;
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);
1755 *rbuf.cur = '\0';
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)
1762 err = -1;
1764 return err;
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
1772 * next time.
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)
1782 char date[200];
1783 int err = 0;
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.
1791 /* get data */
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;
1801 else{
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 */
1808 rfc822_date(date);
1809 dprint((1, "rd_upgrade_cookies: add new hdr msg to end\n"));
1810 err = rd_add_hdr_msg(rd, date);
1813 if(!err){
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){
1821 char sequence[20];
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.
1831 rd_ping_stream(rd);
1832 rd_open_remote(rd);
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);
1841 return(err);
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
1851 * 0 on success
1854 rd_update_local(REMDATA_S *rd)
1856 char *error;
1857 STORE_S *store;
1858 gf_io_t pc;
1859 int i, we_cancel = 0;
1860 BODY *body = NULL;
1861 ENVELOPE *env;
1862 char *tempfile = NULL;
1865 if(!rd || !(rd->flags & NO_FILE || rd->lf) || !rd->rn){
1866 dprint((1,
1867 "rd_update_local: Unexpected error: %s is NULL\n",
1868 !rd ? "rd" :
1869 !(rd->flags & NO_FILE || rd->lf) ? "localfile" :
1870 !rd->rn ? "remotename" : "?"));
1872 return -1;
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 : "?")));
1879 switch(rd->type){
1880 case RemImap:
1881 if(!rd->so || !rd->t.i.special_hdr){
1882 dprint((1,
1883 "rd_update_local: Unexpected error: %s is NULL\n",
1884 !rd->so ? "so" :
1885 !rd->t.i.special_hdr ? "special_hdr" : "?"));
1886 return -1;
1889 rd_open_remote(rd);
1890 if(!rd_stream_exists(rd)){
1891 if(rd->flags & NO_PERM_CACHE){
1892 dprint((1,
1893 "rd_update_local: backtrack, remote folder does not exist yet\n"));
1895 else{
1896 dprint((1,
1897 "rd_update_local: Unexpected error: stream is NULL\n"));
1900 return -1;
1903 if(rd->t.i.stream){
1904 char ebuf[500];
1905 char *eptr = NULL;
1906 int chk;
1908 /* force ReadOnly */
1909 if(rd->t.i.stream->rdonly){
1910 rd->read_status = 'R';
1911 rd->access = ReadOnly;
1913 else
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);
1922 if(eptr){
1923 q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
1924 dprint((1, "%s\n", eptr));
1925 fs_give((void **)&eptr);
1928 dprint((1,
1929 "Can't initialize remote data \"%s\"\n",
1930 rd->rn ? rd->rn : "?"));
1931 return -1;
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);
1940 else{
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));
1945 dprint((2,
1946 "rd_update_local: Error opening temporary file: %s\n",
1947 error_description(errno)));
1948 return -1;
1951 /* Copy the data into tempfile */
1952 if((store = so_get(FileStar, tempfile, WRITE_ACCESS|OWNER_ONLY))
1953 == NULL){
1954 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1955 _("Error opening temporary file %s: %s"),
1956 tempfile, error_description(errno));
1957 dprint((2,
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);
1963 return -1;
1968 * Copy from the last message in the folder.
1970 if(!pine_mail_fetchstructure(rd->t.i.stream, rd->t.i.stream->nmsgs,
1971 &body)){
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"));
1975 if(tempfile){
1976 our_unlink(tempfile);
1977 fs_give((void **)&tempfile);
1980 if(!(rd->flags & NO_FILE))
1981 so_give(&store);
1983 if(we_cancel)
1984 cancel_busy_cue(-1);
1986 return -1;
1989 if(!body ||
1990 body->type != REMOTE_DATA_TYPE ||
1991 !body->subtype ||
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"));
1995 dprint((2,
1996 "Remote IMAP folder has wrong contents\n"));
1997 if(tempfile){
1998 our_unlink(tempfile);
1999 fs_give((void **)&tempfile);
2002 if(!(rd->flags & NO_FILE))
2003 so_give(&store);
2005 if(we_cancel)
2006 cancel_busy_cue(-1);
2008 return -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"));
2015 dprint((2,
2016 "Can't access check date in remote data\n"));
2017 if(tempfile){
2018 our_unlink(tempfile);
2019 fs_give((void **)&tempfile);
2022 if(!(rd->flags & NO_FILE))
2023 so_give(&store);
2025 if(we_cancel)
2026 cancel_busy_cue(-1);
2028 return -1;
2031 if(rd && rd->flags & USER_SAID_YES)
2032 chk = 0;
2033 else
2034 chk = rd_check_for_suspect_data(rd);
2036 switch(chk){
2037 case -1: /* suspicious data, user says abort */
2038 if(tempfile){
2039 our_unlink(tempfile);
2040 fs_give((void **)&tempfile);
2043 if(!(rd->flags & NO_FILE))
2044 so_give(&store);
2046 if(we_cancel)
2047 cancel_busy_cue(-1);
2049 return -1;
2051 case 1: /* suspicious data, user says go ahead */
2052 if(rd_remote_is_readonly(rd)){
2053 dprint((1,
2054 "rd_update_local: can't upgrade, readonly\n"));
2056 else
2057 /* attempt to upgrade cookie in last message */
2058 (void)rd_upgrade_cookies(rd, 0, 1);
2060 break;
2062 case 0: /* all is ok */
2063 default:
2064 break;
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);
2079 if(we_cancel)
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));
2086 error = ebuf;
2090 if(error){
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 : "?"));
2095 if(tempfile){
2096 our_unlink(tempfile);
2097 fs_give((void **)&tempfile);
2100 return -1;
2103 if(tempfile && (i = rename_file(tempfile, rd->lf)) < 0){
2104 #ifdef _WINDOWS
2105 if(i == -5){
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?"));
2111 dprint((2,
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)));
2117 else
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));
2123 dprint((2,
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);
2133 return -1;
2136 dprint((5,
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;
2144 if(tempfile)
2145 fs_give((void **)&tempfile);
2147 return 0;
2149 else{
2150 q_status_message1(SM_ORDER | SM_DING, 5, 5,
2151 _("Can't open remote IMAP folder \"%s\""), rd->rn);
2152 dprint((1,
2153 "Can't open remote IMAP folder \"%s\"\n",
2154 rd->rn ? rd->rn : "?"));
2155 rd->access = ReadOnly;
2156 return -1;
2159 break;
2161 default:
2162 dprint((1, "rd_update_local: Unsupported type\n"));
2163 return -1;
2169 * Copy local data to remote folder.
2171 * Args lf -- Local file name
2172 * rn -- Remote 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
2178 * 0 on success
2181 rd_update_remote(REMDATA_S *rd, char *returndate)
2183 STORE_S *store;
2184 int err = 0;
2185 long openmode = SP_USEPOOL | SP_TEMPUSE;
2186 MAILSTREAM *st;
2187 char *eptr = NULL;
2189 if(rd && rd->type != RemImap){
2190 dprint((1, "rd_update_remote: type not supported\n"));
2191 return -1;
2194 if(!rd || !(rd->flags & NO_FILE || rd->lf) || !rd->rn ||
2195 !rd->so || !rd->t.i.special_hdr){
2196 dprint((1,
2197 "rd_update_remote: Unexpected error: %s is NULL\n",
2198 !rd ? "rd" :
2199 !(rd->flags & NO_FILE || rd->lf) ? "localfile" :
2200 !rd->rn ? "remotename" :
2201 !rd->so ? "so" :
2202 !rd->t.i.special_hdr ? "special_hdr" : "?"));
2203 return -1;
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);
2214 if(!st)
2215 st = rd->t.i.stream = context_open(NULL, NULL, rd->rn, openmode, NULL);
2217 if(!st){
2218 q_status_message1(SM_ORDER | SM_DING, 5, 5,
2219 _("Can't open \"%s\" for copying"), rd->rn);
2220 dprint((1,
2221 "rd_update_remote: Can't open remote folder \"%s\" for copying\n",
2222 rd->rn ? rd->rn : "?"));
2223 return 1;
2226 rd->last_use = get_adj_time();
2227 err = rd_chk_for_hdr_msg(&st, rd, &eptr);
2228 if(err){
2229 q_status_message1(SM_ORDER | SM_DING, 5, 5,
2230 _("\"%s\" has invalid format"), rd->rn);
2232 if(eptr){
2233 q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
2234 dprint((1, "%s\n", eptr));
2235 fs_give((void **)&eptr);
2238 dprint((1,
2239 "rd_update_remote: \"%s\" has invalid format\n",
2240 rd->rn ? rd->rn : "?"));
2241 return 1;
2244 errno = 0;
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;
2254 if(store)
2255 so_seek(store, 0L, 0); /* rewind */
2257 else
2258 store = so_get(FileStar, rd->lf, READ_ACCESS);
2260 if(store != NULL){
2261 char date[200];
2262 unsigned char c;
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)
2267 err = 1;
2269 rfc822_date(date);
2270 dprint((7,
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))
2275 err = 1;
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)
2291 err = 1;
2293 last_c = 0;
2295 else{
2296 last_c = c;
2297 if(so_writec((int) c, rd->so) == 0)
2298 err = 1;
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.
2307 if(!err){
2308 MAILSTREAM *st;
2310 if((st = rd->t.i.stream) != NULL)
2311 rd->t.i.shouldbe_nmsgs = rd->t.i.stream->nmsgs + 1;
2312 else
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))
2321 so_give(&store);
2323 else
2324 err = -1;
2326 if(err)
2327 dprint((2, "error in rd_update_remote for %s => %s\n",
2328 rd->lf ? rd->lf : "<mem>", rd->rn ? rd->rn : "?"));
2330 return(err);
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
2342 void
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"));
2356 return;
2359 if(!rd || rd->flags & REM_OUTOFDATE || rd->flags & USE_OLD_CACHE)
2360 return;
2362 if(!rd->t.i.chk_date){
2363 dprint((2,
2364 "rd_check_remvalid: remote data %s changed (chk_date)\n",
2365 rd->rn));
2366 rd->flags |= REM_OUTOFDATE;
2367 return;
2370 if(rd->t.i.chk_nmsgs <= 1){
2371 dprint((2,
2372 "rd_check_remvalid: remote data %s changed (chk_nmsgs <= 1)\n",
2373 rd->rn ? rd->rn : "?"));
2374 rd->flags |= REM_OUTOFDATE;
2375 return;
2378 if(do_it_now < 0L){
2379 chk_interval = -1L * do_it_now;
2380 do_it_now = 0L;
2382 else
2383 chk_interval = ps_global->remote_abook_validity * 60L;
2385 /* too soon to check again */
2386 if(!do_it_now &&
2387 (chk_interval == 0L ||
2388 get_adj_time() <= rd->last_valid_chk + chk_interval))
2389 return;
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){
2399 dprint((2,
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;
2404 return;
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.
2413 rd_ping_stream(rd);
2415 try_looking_in_stream:
2418 * Get the current number of messages in the folder to
2419 * compare with our saved number of messages.
2421 current_nmsgs = 0;
2422 if(rd->t.i.stream){
2423 dprint((7, "using open remote data stream\n"));
2424 rd->last_use = get_adj_time();
2425 current_nmsgs = rd->t.i.stream->nmsgs;
2426 got_cmsgs++;
2428 else{
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.
2445 if(stat_stream){
2446 if(!LEVELSTATUS(stat_stream)){
2447 rd->flags |= NO_STATUSCMD;
2448 dprint((2,
2449 "rd_check_remvalid: remote data %s: server doesn't support status\n",
2450 rd->rn ? rd->rn : "?"));
2452 else{
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,
2462 * too bad.
2464 if(same_stream_and_mailbox(rd->rn, stat_stream)){
2465 dprint((7,
2466 "rd_check_remvalid: faking status\n"));
2467 mm_status_result.flags = SA_MESSAGES | SA_UIDVALIDITY
2468 | SA_UIDNEXT;
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;
2473 else{
2475 dprint((7,
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;
2482 dprint((2,
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){
2495 dprint((7,
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;
2500 got_cmsgs++;
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;
2512 dprint((2,
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));
2515 return;
2519 * Get the current uidvalidity and uidnext values from the
2520 * folder to compare with our saved values.
2522 if(rd->t.i.stream){
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;
2531 dprint((2,
2532 "rd_check_remvalid: remote data %s changed (uid)\n",
2533 rd->rn ? rd->rn : "?"));
2535 else{
2536 dprint((7,"rd_check_remvalid: uids match\n"));
2539 return;
2541 else{
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;
2547 dprint((7,
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){
2558 * Uids are valid.
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"));
2566 return;
2568 else{ /* uidnext changed, folder _may_ have changed */
2570 dprint((7,
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,
2583 NULL)) != NULL){
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();
2593 dprint((7,
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;
2598 dprint((2,
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.
2612 dprint((2,
2613 "rd_check_remvalid: remote data %s false change: adjusting uidnext from %lu to %lu\n",
2614 rd->rn ? rd->rn : "?",
2615 rd->t.i.uidnext,
2616 mm_status_result.uidnext));
2617 rd->t.i.uidnext = mm_status_result.uidnext;
2618 rd_write_metadata(rd, 0);
2621 if(we_cancel)
2622 cancel_busy_cue(-1);
2624 return;
2626 else{
2627 ps_global->noshow_error = 0;
2628 dprint((2,
2629 "rd_check_remvalid: couldn't open %s\n",
2630 rd->rn ? rd->rn : "?"));
2634 else{
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.
2647 if(stat_stream){
2648 rd->flags |= NO_STATUSCMD;
2649 dprint((7,
2650 "rd_check_remvalid: remote data %s don't use status\n",
2651 rd->rn ? rd->rn : "?"));
2654 dprint((7,
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,
2659 NULL);
2660 ps_global->noshow_error = 0;
2661 if(we_cancel)
2662 cancel_busy_cue(-1);
2664 we_cancel = 0;
2665 if(rd->t.i.stream)
2666 goto try_looking_in_stream;
2667 else{
2668 dprint((7,
2669 "rd_check_remvalid: cannot open remote mailbox\n"));
2670 return;
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.
2685 dprint((7,
2686 "rd_check_remvalid: falling back to Date check\n"));
2689 * Fall back to looking in the folder at the Date header.
2692 if(!rd->t.i.stream)
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;
2705 dprint((2,
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;
2715 dprint((2,
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();
2722 if(env)
2723 dprint((7,
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;
2731 if(we_cancel)
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))
2749 return(1);
2751 switch(rd->type){
2752 case RemImap:
2753 if(rd->t.i.stream->rdonly)
2754 return(2);
2756 break;
2758 default:
2759 q_status_message(SM_ORDER, 3, 5,
2760 "rd_remote_is_readonly: type not supported");
2761 break;
2764 return(0);
2769 * Returns 0 if ok
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)
2776 int ans = -1;
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)
2781 return -1;
2783 fields[0] = rd->t.i.special_hdr;
2784 fields[1] = "received";
2785 fields[2] = NULL;
2786 cookie = 0L;
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);
2792 else if(values[0]){
2793 cookie = strtoul(values[0], (char **)NULL, 10);
2794 if(cookie == rd->cookie) /* all's well */
2795 ans = 0;
2796 else
2797 ans = rd_prompt_about_forged_remote_data(cookie > 1L
2798 ? 100 : cookie,
2799 rd, values[0]);
2801 else
2802 ans = rd_prompt_about_forged_remote_data(-2, rd, NULL);
2804 if(values[0])
2805 fs_give((void **)&values[0]);
2807 if(values[1])
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);
2815 return ans;