* NTLM authentication support with the ntlm library, in Unix systems.
[alpine.git] / pith / remote.c
blobac03c9a6fbc7e2f62cfb253be7b9a52295c31eb0
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: remote.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2017 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 /*======================================================================
20 remote.c
21 Implements remote IMAP config files (remote config, remote abook).
22 ====*/
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"
44 * Internal prototypes
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);
60 char *
61 read_remote_pinerc(PINERC_S *prc, ParsePinerc which_vars)
63 int try_cache, no_perm_create_pass = 0;
64 char *file = NULL;
65 unsigned flags;
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){
83 if(prc->rd){
84 prc->rd->flags &= ~DO_REMTRIM;
85 rd_close_remdata(&prc->rd);
88 /* this will cause the remote folder to be created */
89 flags = 0;
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,
98 &flags, _("Error: "),
99 _(" Can't fetch remote configuration."));
100 if(!prc->rd)
101 goto bail_out;
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';
120 else
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;
144 else
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;
162 dprint((1,
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.
169 else{
170 dprint((7,
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){
183 if(try_cache){
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");
188 dprint((2,
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 : "?"));
193 else{
194 prc->rd->flags &= ~DO_REMTRIM;
195 goto bail_out;
199 if(prc->rd->flags & NO_FILE)
200 /* copy text, leave sonofile for later use */
201 file = cpystr((char *)so_text(prc->rd->sonofile));
202 else
203 file = read_file(prc->rd->lf, 0);
205 bail_out:
206 if((which_vars == ParsePers || which_vars == ParsePersPost) &&
207 (!file || !prc->rd || prc->rd->access != ReadWrite)){
208 prc->readonly = 1;
209 if(prc == ps_global->prc)
210 ps_global->readonly_pinerc = 1;
213 return(file);
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.
222 * remote_name --
223 * type_spec -- Type-specific data.
224 * flags --
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.
231 REMDATA_S *
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);
242 if(flags)
243 rd->flags = *flags;
245 switch(rd->type){
246 case RemImap:
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);
251 if(!rd->sonofile)
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;
266 else{
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;
281 break;
283 default:
284 q_status_message(SM_ORDER, 3,5, "rd_create_remote: type not supported");
285 break;
288 return(rd);
292 REMDATA_S *
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));
300 rd->type = type;
301 rd->access = NoExists;
303 if(remote_name)
304 rd->rn = cpystr(remote_name);
306 switch(rd->type){
307 case RemImap:
308 if(type_spec)
309 rd->t.i.special_hdr = cpystr(type_spec);
311 break;
313 default:
314 q_status_message(SM_ORDER, 3,5, "rd_new_remdata: type not supported");
315 break;
318 return(rd);
323 * Closes the remote stream and frees.
325 void
326 rd_free_remdata(REMDATA_S **rd)
328 if(rd && *rd){
329 rd_close_remote(*rd);
331 if((*rd)->rn)
332 fs_give((void **)&(*rd)->rn);
334 if((*rd)->lf)
335 fs_give((void **)&(*rd)->lf);
337 if((*rd)->so){
338 so_give(&(*rd)->so);
339 (*rd)->so = NULL;
342 if((*rd)->sonofile){
343 so_give(&(*rd)->sonofile);
344 (*rd)->sonofile = NULL;
347 switch((*rd)->type){
348 case RemImap:
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);
355 break;
357 default:
358 q_status_message(SM_ORDER, 3, 5,
359 "rd_free_remdata: type not supported");
360 break;
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
371 * and frees the rd.
373 void
374 rd_trim_remdata(REMDATA_S **rd)
376 if(!(rd && *rd))
377 return;
379 switch((*rd)->type){
380 case RemImap:
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
386 * data.
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 */
393 rd_ping_stream(*rd);
394 rd_open_remote(*rd);
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){
400 char sequence[20];
401 int user_deleted = 0;
404 * If user manually deleted some, we'd better not delete
405 * any more.
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);
415 else
416 user_deleted++;
418 mail_expunge((*rd)->t.i.stream);
420 if(!user_deleted)
421 rd_update_metadata(*rd, NULL);
422 /* else
423 * don't update metafile because user is messing with
424 * the remote folder manually. We'd better re-read it next
425 * time. */
429 ps_global->noshow_error = 0;
432 break;
434 default:
435 q_status_message(SM_ORDER, 3,5, "rd_trim_remdata: type not supported");
436 break;
442 * All done with this remote data. Trim the folder, close the
443 * stream, and free.
445 void
446 rd_close_remdata(REMDATA_S **rd)
448 if(!(rd && *rd))
449 return;
451 rd_trim_remdata(rd);
453 if((*rd)->lf && (*rd)->flags & DEL_FILE)
454 our_unlink((*rd)->lf);
456 /* this closes the stream and frees memory */
457 rd_free_remdata(rd);
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.
469 * 0 otherwise.
472 rd_read_metadata(REMDATA_S *rd)
474 REMDATA_META_S *rab = NULL;
475 int try_cache = 0;
477 dprint((7, "rd_read_metadata \"%s\"\n",
478 (rd && rd->rn) ? rd->rn : "?"));
480 if(!rd)
481 return try_cache;
483 if(rd->flags & NO_PERM_CACHE)
484 rab = NULL;
485 else
486 rab = rd_find_our_metadata(rd->rn, &rd->flags);
488 if(!rab){
489 if(!rd->lf){
490 rd->flags |= (NO_META_UPDATE | REM_OUTOFDATE);
491 if(!(rd->flags & NO_FILE)){
492 rd->lf = temp_nam(NULL, "a6");
493 rd->flags |= DEL_FILE;
496 /* display error */
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];
507 char *lc;
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){
517 int to_copy;
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';
524 else{
525 dir[0] = '.';
526 dir[1] = '\0';
529 build_path(path, dir, rab->local_cache_file, sizeof(path));
530 rd->lf = cpystr(path);
532 else{
533 rd->lf = rab->local_cache_file;
534 /* don't free this below, we're using it */
535 rab->local_cache_file = NULL;
538 rd->read_status = rab->read_status;
540 switch(rd->type){
541 case RemImap:
542 rd->t.i.chk_date = rab->date;
543 rab->date = NULL; /* don't free this below, we're using it */
545 dprint((7,
546 "in read_metadata, setting chk_date from metadata to ->%s<-\n",
547 rd->t.i.chk_date ? rd->t.i.chk_date : "?"));
548 rd->t.i.chk_nmsgs = rab->nmsgs;
549 rd->t.i.uidvalidity = rab->uidvalidity;
550 rd->t.i.uidnext = rab->uidnext;
551 rd->t.i.uid = rab->uid;
552 rd->t.i.chk_nmsgs = rab->nmsgs;
553 dprint((7,
554 "setting uid=%lu uidnext=%lu uidval=%lu read_stat=%c nmsgs=%lu\n",
555 rd->t.i.uid, rd->t.i.uidnext, rd->t.i.uidvalidity,
556 rd->read_status ? rd->read_status : '0',
557 rd->t.i.chk_nmsgs));
558 break;
560 default:
561 q_status_message(SM_ORDER, 3, 5,
562 "rd_read_metadata: type not supported");
563 break;
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.
573 else{
575 * The local_cache_file is where we will store the cached local
576 * copy of the remote data.
578 rab->local_cache_file = tempfile_in_same_dir(ps_global->pinerc,
579 meta_prefix, NULL);
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;
585 else
586 rd->lf = temp_nam(NULL, "a7");
589 if(rab){
590 if(rab->local_cache_file)
591 fs_give((void **)&rab->local_cache_file);
592 if(rab->date)
593 fs_give((void **)&rab->date);
594 fs_give((void **)&rab);
597 return(try_cache);
602 * Write out the contents of the metadata file.
604 * Each line should be: folder_name cache_file uidvalidity uidnext uid nmsgs
605 * read_status date
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.
614 void
615 rd_write_metadata(REMDATA_S *rd, int delete_it)
617 char *tempfile;
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];
622 int fd;
624 dprint((7, "rd_write_metadata \"%s\"\n",
625 (rd && rd->rn) ? rd->rn : "?"));
627 if(!rd || rd->flags & NO_META_UPDATE)
628 return;
630 if(rd->type != RemImap){
631 q_status_message(SM_ORDER, 3, 5,
632 "rd_write_metadata: type not supported");
633 return;
636 dprint((9, " - rd_write_metadata: rn=%s lf=%s\n",
637 rd->rn ? rd->rn : "?", rd->lf ? rd->lf : "?"));
639 key = rd->rn;
641 if(!(pith_opt_rd_metadata_name && (metafile = (*pith_opt_rd_metadata_name)())))
642 goto io_err;
644 if(!(tempfile = tempfile_in_same_dir(metafile, "a9", &pinerc_dir)))
645 goto io_err;
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;
652 else
653 rel_filename = rd->lf;
655 if(pinerc_dir)
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)
666 goto io_err;
668 while((p = fgets(line, sizeof(line), fp_old)) != NULL){
670 * Skip the header line and any lines that don't begin
671 * with a "{".
673 if(line[0] != '{')
674 continue;
676 /* skip the old cache line for this key */
677 if(strncmp(line, key, strlen(key)) == 0 && line[strlen(key)] == TAB)
678 continue;
680 /* add this line to new version of file */
681 if(fputs(p, fp_new) == EOF)
682 goto io_err;
686 /* add the cache line for this key */
687 /* Warning: this is type RemImap specific right now! */
688 if(!delete_it){
689 if(!tempfile ||
690 !fp_old ||
691 !fp_new ||
692 p ||
693 fprintf(fp_new, "%s\t%s\t%lu\t%lu\t%lu\t%lu\t%c\t%s\n",
694 key ? key : "",
695 rel_filename ? rel_filename : "",
696 rd->t.i.uidvalidity, rd->t.i.uidnext, rd->t.i.uid,
697 rd->t.i.chk_nmsgs,
698 rd->read_status ? rd->read_status : '?',
699 rd->t.i.chk_date ? rd->t.i.chk_date : "no-match")
700 == EOF)
701 goto io_err;
704 if(fclose(fp_new) == EOF){
705 fp_new = NULL;
706 goto io_err;
709 if(fclose(fp_old) == EOF){
710 fp_old = NULL;
711 goto io_err;
714 if(rename_file(tempfile, metafile) < 0)
715 goto io_err;
717 if(tempfile)
718 fs_give((void **)&tempfile);
720 if(metafile)
721 fs_give((void **)&metafile);
723 return;
725 io_err:
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));
732 if(tempfile){
733 our_unlink(tempfile);
734 fs_give((void **)&tempfile);
736 if(metafile)
737 fs_give((void **)&metafile);
738 if(fp_old)
739 (void)fclose(fp_old);
740 if(fp_new)
741 (void)fclose(fp_new);
745 void
746 rd_update_metadata(REMDATA_S *rd, char *date)
748 if(!rd)
749 return;
751 dprint((9, " - rd_update_metadata: rn=%s lf=%s\n",
752 rd->rn ? rd->rn : "?", rd->lf ? rd->lf : "<none>"));
754 switch(rd->type){
755 case RemImap:
756 if(rd->t.i.stream){
757 ps_global->noshow_error = 1;
758 rd_ping_stream(rd);
759 if(rd->t.i.stream){
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
764 * to see those.
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))){
773 rd_close_remote(rd);
774 rd_open_remote(rd);
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;
785 rd->t.i.uid = 0L;
786 rd->t.i.uidnext = 0L;
788 else{
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)
802 + 1;
806 ps_global->noshow_error = 0;
809 * Save the date so that we can check if it changed next time
810 * we go to write.
812 if(date){
813 if(rd->t.i.chk_date)
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;
824 break;
826 default:
827 q_status_message(SM_ORDER, 3, 5,
828 "rd_update_metadata: type not supported");
829 break;
836 REMDATA_META_S *
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;
842 FILE *fp;
844 dprint((9, "rd_find_our_metadata \"%s\"\n", key ? key : "?"));
846 if(!(key && *key))
847 return rab;
849 if(!(pith_opt_rd_metadata_name && (metafile = (*pith_opt_rd_metadata_name)()) != NULL))
850 return rab;
853 * Open the metadata file and get some information out.
855 fp = our_fopen(metafile, "rb");
856 if(fp == NULL){
857 q_status_message2(SM_ORDER, 3, 5,
858 _("can't open metadata file %s, continuing (%s)"),
859 metafile, error_description(errno));
860 dprint((2,
861 "can't open existing metadata file %s: %s\n",
862 metafile ? metafile : "?", error_description(errno)));
863 if(flags)
864 (*flags) |= NO_META_UPDATE;
866 fs_give((void **)&metafile);
868 return rab;
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)){
885 dprint((2,
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){
891 int fd;
893 (void)fclose(fp);
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));
900 dprint((2,
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);
906 return NULL;
909 (void)close(fd);
911 else
912 (void)fclose(fp);
914 fs_give((void **)&metafile);
915 return(rab);
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)
923 break;
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 */
934 if(*p == TAB){
935 q = ++p; /* q points to cache_file */
936 SKIP_TO_TAB(p); /* skip to TAB following cache_file */
937 if(*p == TAB){
938 *p = '\0';
939 rab->local_cache_file = cpystr(q);
940 q = ++p; /* q points to uidvalidity */
941 SKIP_TO_TAB(p); /* skip to TAB following uidvalidity */
942 if(*p == TAB){
943 *p = '\0';
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 */
947 if(*p == TAB){
948 *p = '\0';
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 */
952 if(*p == TAB){
953 *p = '\0';
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 */
957 if(*p == TAB){
958 *p = '\0';
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 */
962 if(*p == TAB){
963 *p = '\0';
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 */
967 p++;
969 *p = '\0';
970 rab->date = cpystr(q);
980 (void)fclose(fp);
981 return(rab);
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)
999 return -1;
1001 if(fp_file_size(fp) <
1002 (long)(SIZEOF_PMAGIC + SIZEOF_SPACE + SIZEOF_VERSION_NUM))
1003 return -1;
1005 if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0))
1006 return -1;
1008 /* check for magic */
1009 if(fread(buf, sizeof(char), (unsigned)SIZEOF_PMAGIC, fp) != SIZEOF_PMAGIC)
1010 return -1;
1012 buf[SIZEOF_PMAGIC] = '\0';
1013 if(strcmp(buf, PMAGIC) != 0)
1014 return -1;
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))
1024 return -1;
1026 if(fread(buf, sizeof(char), (unsigned)SIZEOF_VERSION_NUM, fp) !=
1027 SIZEOF_VERSION_NUM)
1028 return -1;
1030 buf[SIZEOF_VERSION_NUM] = '\0';
1031 if(strcmp(buf, METAFILE_VERSION_NUM) != 0)
1032 return -1;
1034 /* Position file pointer to second line */
1035 if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0))
1036 return -1;
1038 if(fgets(buf, sizeof(buf), fp) == NULL)
1039 return -1;
1041 return 0;
1046 * Open a data stream to the remote data.
1048 void
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 : "?"));
1056 if(!rd)
1057 return;
1059 if(rd_stream_exists(rd)){
1060 rd->last_use = get_adj_time();
1061 return;
1064 switch(rd->type){
1065 case RemImap:
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.) */
1075 if(!rd->t.i.stream)
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);
1083 if(rd->t.i.stream)
1084 rd->last_use = get_adj_time();
1086 break;
1088 default:
1089 q_status_message(SM_ORDER, 3, 5, "rd_open_remote: type not supported");
1090 break;
1096 * Close a data stream to the remote data.
1098 void
1099 rd_close_remote(REMDATA_S *rd)
1101 if(!rd || !rd_stream_exists(rd))
1102 return;
1104 switch(rd->type){
1105 case RemImap:
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;
1110 break;
1112 default:
1113 q_status_message(SM_ORDER, 3, 5, "rd_close_remote: type not supported");
1114 break;
1120 rd_stream_exists(REMDATA_S *rd)
1122 if(!rd)
1123 return(0);
1125 switch(rd->type){
1126 case RemImap:
1127 return(rd->t.i.stream ? 1 : 0);
1129 default:
1130 q_status_message(SM_ORDER, 3,5, "rd_stream_exists: type not supported");
1131 break;
1134 return(0);
1139 * Ping the stream and return 1 if it is still alive.
1142 rd_ping_stream(REMDATA_S *rd)
1144 int ret = 0;
1146 dprint((7, "rd_ping_stream \"%s\"\n",
1147 (rd && rd->rn) ? rd->rn : "?"));
1149 if(!rd)
1150 return(ret);
1152 if(!rd_stream_exists(rd)){
1153 switch(rd->type){
1154 case RemImap:
1156 * If this stream is already actually open, officially open
1157 * it and use it.
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);
1168 break;
1170 default:
1171 break;
1175 if(!rd_stream_exists(rd))
1176 return(ret);
1178 switch(rd->type){
1179 case RemImap:
1180 ps_global->noshow_error = 1;
1181 if(pine_mail_ping(rd->t.i.stream))
1182 ret = 1;
1183 else
1184 rd->t.i.stream = NULL;
1186 ps_global->noshow_error = 0;
1187 break;
1189 default:
1190 q_status_message(SM_ORDER, 3, 5, "rd_ping_stream: type not supported");
1191 break;
1194 return(ret);
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
1201 * the metadata.
1203 void
1204 rd_check_readonly_access(REMDATA_S *rd)
1206 if(rd && rd->read_status == 'R'){
1207 switch(rd->type){
1208 case RemImap:
1209 rd_open_remote(rd);
1210 if(rd->t.i.stream && !rd->t.i.stream->rdonly){
1211 rd->read_status = 'W';
1212 rd->access = ReadWrite;
1215 break;
1217 default:
1218 q_status_message(SM_ORDER, 3, 5,
1219 "rd_check_readonly_access: type not supported");
1220 break;
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
1230 * data message.
1231 * The remote folder already exists before we get here, though it may be empty.
1233 * Returns -1 on failure
1234 * 0 on success
1237 rd_init_remote(REMDATA_S *rd, int add_only_first_msg)
1239 int err = 0;
1240 char date[200];
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"));
1247 return -1;
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){
1257 dprint((1,
1258 "rd_init_remote: Unexpected error: %s is NULL\n",
1259 !(rd->flags & NO_FILE || rd->lf) ? "localfile" :
1260 !rd->rn ? "remotename" :
1261 !rd->so ? "so" :
1262 !rd->t.i.stream ? "stream" :
1263 !rd->t.i.special_hdr ? "special_hdr" : "?"));
1264 return -1;
1267 /* already init'd */
1268 if(rd->t.i.stream->nmsgs >= 2 ||
1269 (rd->t.i.stream->nmsgs >= 1 && add_only_first_msg))
1270 return err;
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"));
1293 dprint((1,
1294 "Can't initialize remote folder \"%s\"\n",
1295 rd->rn ? rd->rn : "?"));
1296 dprint((1,
1297 " No write permission for that remote folder.\n"));
1298 dprint((1,
1299 " Choose a new unused folder for the remote data.\n"));
1300 err = -1;
1303 if(!err){
1304 if(rd->t.i.stream->nmsgs == 0){
1305 int we_cancel;
1307 we_cancel = busy_cue(_("Initializing remote data"), NULL, 1);
1308 rfc822_date(date);
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
1315 * data folder.
1317 err = rd_add_hdr_msg(rd, date);
1318 if(rd->t.i.stream->nmsgs == 0)
1319 rd_ping_stream(rd);
1320 if(rd->t.i.stream && rd->t.i.stream->nmsgs == 0)
1321 pine_mail_check(rd->t.i.stream);
1323 if(we_cancel)
1324 cancel_busy_cue(-1);
1326 else{
1327 char *eptr = NULL;
1329 err = rd_chk_for_hdr_msg(&(rd->t.i.stream), rd, &eptr);
1330 if(err){
1331 q_status_message1(SM_ORDER | SM_DING, 5, 5,
1332 _("\"%s\" has invalid format, can't initialize"), rd->rn);
1334 dprint((1,
1335 "Can't initialize remote data \"%s\"\n",
1336 rd->rn ? rd->rn : "?"));
1338 if(eptr){
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;
1352 int fd = -1;
1354 if(rd->flags & NO_FILE){
1355 if(so_truncate(rd->sonofile, 0L) == 0)
1356 err = -1;
1358 else{
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)));
1365 err = -1;
1368 if(!err &&
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));
1373 dprint((2,
1374 "init_remote: Error opening temporary file: %s: %s\n",
1375 tempfile ? tempfile : "?", error_description(errno)));
1376 our_unlink(tempfile);
1377 err = -1;
1379 else
1380 (void)close(fd);
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);
1387 err = -1;
1390 if(tempfile)
1391 fs_give((void **)&tempfile);
1394 if(!err){
1395 err = rd_update_remote(rd, date);
1396 if(err){
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");
1407 if(!err){
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];
1429 char *h;
1430 int tried_again = 0;
1431 int ret;
1432 MAILSTREAM *st = NULL;
1434 fields[0] = rd->t.i.special_hdr;
1435 fields[1] = "received";
1436 fields[2] = NULL;
1438 try_again:
1439 ret = -1;
1441 if(!streamp || !*streamp){
1442 dprint((1, "rd_chk_for_hdr_msg: stream is null\n"));
1444 else if((*streamp)->nmsgs == 0){
1445 ret = -2;
1446 dprint((1,
1447 "rd_chk_for_hdr_msg: stream has nmsgs=0, try a ping\n"));
1448 if(!pine_mail_ping(*streamp))
1449 *streamp = NULL;
1451 if(*streamp && (*streamp)->nmsgs == 0){
1452 dprint((1,
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){
1458 dprint((1,
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);
1464 rd_open_remote(rd);
1465 if(rd_stream_exists(rd))
1466 st = rd->t.i.stream;
1469 if(!st)
1470 st = *streamp;
1472 if(st && st->nmsgs == 0){
1473 dprint((1,
1474 "rd_chk_for_hdr_msg: can't see header message\n"));
1477 else
1478 st = *streamp;
1480 if(st && st->nmsgs != 0
1481 && (h=pine_fetchheader_lines(st, 1L, NULL, fields))){
1482 simple_header_parse(h, fields, values);
1483 ret = -3;
1484 if(values[1])
1485 ret = -4;
1486 else if(values[0]){
1487 rd->cookie = strtoul(values[0], (char **)NULL, 10);
1488 if(rd->cookie == 0)
1489 ret = -5;
1490 else if(rd->cookie == 1){
1491 if(rd->flags & COOKIE_ONE_OK || tried_again)
1492 ret = 0;
1493 else
1494 ret = -6;
1496 else if(rd->cookie > 1)
1497 ret = 0;
1500 if(values[0])
1501 fs_give((void **)&values[0]);
1503 if(values[1])
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';
1515 if(ret == -1){
1516 /* null stream */
1517 if(errmsg)
1518 snprintf(*errmsg, 500, _("Can't open remote address book \"%s\""), rd->rn);
1520 else if(ret == -2){
1521 /* no messages in folder */
1522 if(errmsg)
1523 snprintf(*errmsg, 500,
1524 _("Error: no messages in remote address book \"%s\"!"),
1525 rd->rn);
1527 else if(ret == -3){
1528 /* no cookie */
1529 if(errmsg)
1530 snprintf(*errmsg, 500,
1531 "First msg in \"%s\" should have \"%s\" header",
1532 rd->rn, rd->t.i.special_hdr);
1534 else if(ret == -4){
1535 /* Received header */
1536 if(errmsg)
1537 snprintf(*errmsg, 500,
1538 _("Suspicious Received headers in first msg in \"%s\""),
1539 rd->rn);
1541 else if(ret == -5){
1543 /* cookie is 0 */
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 : "?"));
1552 ret = 0;
1554 else if(ret == -6){
1555 dprint((1,
1556 "rd_chk_for_hdr_msg: cookie is 1, try to upgrade it\n"));
1558 if(rd_remote_is_readonly(rd)){
1559 dprint((1,
1560 "rd_chk_for_hdr_msg: can't upgrade, readonly\n"));
1561 ret = 0; /* stick with 1 */
1563 else{
1564 /* cookie is 1, upgrade it */
1565 if(rd_upgrade_cookies(rd, st->nmsgs, 0) == 0){
1566 /* now check again */
1567 if(!tried_again){
1568 tried_again++;
1569 goto try_again;
1574 * This is actually a failure but we've decided that this
1575 * failure is ok.
1577 ret = 0;
1581 if(errmsg && *errmsg)
1582 dprint((1, "rd_chk_for_hdr_msg: %s\n", *errmsg));
1584 return ret;
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
1595 * Result: 0 success
1596 * -1 failure
1599 rd_add_hdr_msg(REMDATA_S *rd, char *date)
1601 int err = 0;
1603 if(!rd|| rd->type != RemImap || !rd->rn || !rd->so || !rd->t.i.special_hdr){
1604 dprint((1,
1605 "rd_add_hdr_msg: Unexpected error: %s is NULL\n",
1606 !rd ? "rd" :
1607 !rd->rn ? "remotename" :
1608 !rd->so ? "so" :
1609 !rd->t.i.special_hdr ? "special_hdr" : "?"));
1610 return -1;
1613 err = rd_store_fake_hdrs(rd, "Header Message for Remote Data",
1614 "plain", date);
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)
1620 err = -1;
1621 if(!err && so_puts(rd->so,
1622 "This message is just an explanatory message.\015\012") == 0)
1623 err = -1;
1624 if(!err && so_puts(rd->so,
1625 "The last message in the folder is the live addressbook data.\015\012") == 0)
1626 err = -1;
1627 if(!err && so_puts(rd->so,
1628 "The rest of the messages contain previous revisions of the addressbook data.\015\012") == 0)
1629 err = -1;
1630 if(!err && so_puts(rd->so,
1631 "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1632 err = -1;
1633 if(!err && so_puts(rd->so,
1634 "which come after it.\015\012") == 0)
1635 err = -1;
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)
1640 err = -1;
1641 if(!err && so_puts(rd->so,
1642 "This message is just an explanatory message.\015\012") == 0)
1643 err = -1;
1644 if(!err && so_puts(rd->so,
1645 "The last message in the folder is the live config data.\015\012") == 0)
1646 err = -1;
1647 if(!err && so_puts(rd->so,
1648 "The rest of the messages contain previous revisions of the data.\015\012") == 0)
1649 err = -1;
1650 if(!err && so_puts(rd->so,
1651 "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1652 err = -1;
1653 if(!err && so_puts(rd->so,
1654 "which come after it.\015\012") == 0)
1655 err = -1;
1657 else{
1658 if(!err && so_puts(rd->so,
1659 "This folder contains remote Alpine data.\015\012") == 0)
1660 err = -1;
1661 if(!err && so_puts(rd->so,
1662 "This message is just an explanatory message.\015\012") == 0)
1663 err = -1;
1664 if(!err && so_puts(rd->so,
1665 "The last message in the folder is the live data.\015\012") == 0)
1666 err = -1;
1667 if(!err && so_puts(rd->so,
1668 "The rest of the messages contain previous revisions of the data.\015\012") == 0)
1669 err = -1;
1670 if(!err && so_puts(rd->so,
1671 "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
1672 err = -1;
1673 if(!err && so_puts(rd->so,
1674 "which come after it.\015\012") == 0)
1675 err = -1;
1678 /* Take the message from "so" to the remote folder */
1679 if(!err){
1680 MAILSTREAM *st;
1682 if((st = rd->t.i.stream) != NULL)
1683 rd->t.i.shouldbe_nmsgs = rd->t.i.stream->nmsgs + 1;
1684 else
1685 st = adrbk_handy_stream(rd->rn);
1687 err = write_fcc(rd->rn, NULL, rd->so, st, "remote data", NULL) ? 0 : -1;
1690 return err;
1695 * Write some fake header lines into storage object rd->so.
1697 * Args: rd
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)
1705 ENVELOPE *fake_env;
1706 BODY *fake_body;
1707 ADDRESS *fake_from;
1708 int err = 0;
1709 char vers[50], *p;
1710 unsigned long r = 0L;
1711 RFC822BUFFER rbuf;
1713 if(!rd|| rd->type != RemImap || !rd->so || !rd->t.i.special_hdr)
1714 return -1;
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");
1733 if(rd->cookie > 0)
1734 r = rd->cookie;
1736 if(!r){
1737 int i;
1739 for(i = 100; i > 0 && r < 1000000; i--)
1740 r = random();
1742 if(r < 1000000)
1743 r = 1712836L + getpid();
1745 rd->cookie = r;
1748 snprintf(vers, sizeof(vers), "%ld", r);
1750 p = tmp_20k_buf;
1751 *p = '\0';
1752 rbuf.f = dummy_soutr;
1753 rbuf.s = NULL;
1754 rbuf.beg = p;
1755 rbuf.cur = p;
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);
1759 *rbuf.cur = '\0';
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)
1766 err = -1;
1768 return err;
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
1776 * next time.
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)
1786 char date[200];
1787 int err = 0;
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.
1795 /* get data */
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;
1805 else{
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 */
1812 rfc822_date(date);
1813 dprint((1, "rd_upgrade_cookies: add new hdr msg to end\n"));
1814 err = rd_add_hdr_msg(rd, date);
1817 if(!err){
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){
1825 char sequence[20];
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.
1835 rd_ping_stream(rd);
1836 rd_open_remote(rd);
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);
1845 return(err);
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
1855 * 0 on success
1858 rd_update_local(REMDATA_S *rd)
1860 char *error;
1861 STORE_S *store;
1862 gf_io_t pc;
1863 int i, we_cancel = 0;
1864 BODY *body = NULL;
1865 ENVELOPE *env;
1866 char *tempfile = NULL;
1869 if(!rd || !(rd->flags & NO_FILE || rd->lf) || !rd->rn){
1870 dprint((1,
1871 "rd_update_local: Unexpected error: %s is NULL\n",
1872 !rd ? "rd" :
1873 !(rd->flags & NO_FILE || rd->lf) ? "localfile" :
1874 !rd->rn ? "remotename" : "?"));
1876 return -1;
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 : "?")));
1883 switch(rd->type){
1884 case RemImap:
1885 if(!rd->so || !rd->t.i.special_hdr){
1886 dprint((1,
1887 "rd_update_local: Unexpected error: %s is NULL\n",
1888 !rd->so ? "so" :
1889 !rd->t.i.special_hdr ? "special_hdr" : "?"));
1890 return -1;
1893 rd_open_remote(rd);
1894 if(!rd_stream_exists(rd)){
1895 if(rd->flags & NO_PERM_CACHE){
1896 dprint((1,
1897 "rd_update_local: backtrack, remote folder does not exist yet\n"));
1899 else{
1900 dprint((1,
1901 "rd_update_local: Unexpected error: stream is NULL\n"));
1904 return -1;
1907 if(rd->t.i.stream){
1908 char ebuf[500];
1909 char *eptr = NULL;
1910 int chk;
1912 /* force ReadOnly */
1913 if(rd->t.i.stream->rdonly){
1914 rd->read_status = 'R';
1915 rd->access = ReadOnly;
1917 else
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);
1926 if(eptr){
1927 q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
1928 dprint((1, "%s\n", eptr));
1929 fs_give((void **)&eptr);
1932 dprint((1,
1933 "Can't initialize remote data \"%s\"\n",
1934 rd->rn ? rd->rn : "?"));
1935 return -1;
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);
1944 else{
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));
1949 dprint((2,
1950 "rd_update_local: Error opening temporary file: %s\n",
1951 error_description(errno)));
1952 return -1;
1955 /* Copy the data into tempfile */
1956 if((store = so_get(FileStar, tempfile, WRITE_ACCESS|OWNER_ONLY))
1957 == NULL){
1958 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1959 _("Error opening temporary file %s: %s"),
1960 tempfile, error_description(errno));
1961 dprint((2,
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);
1967 return -1;
1972 * Copy from the last message in the folder.
1974 if(!pine_mail_fetchstructure(rd->t.i.stream, rd->t.i.stream->nmsgs,
1975 &body)){
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"));
1979 if(tempfile){
1980 our_unlink(tempfile);
1981 fs_give((void **)&tempfile);
1984 if(!(rd->flags & NO_FILE))
1985 so_give(&store);
1987 if(we_cancel)
1988 cancel_busy_cue(-1);
1990 return -1;
1993 if(!body ||
1994 body->type != REMOTE_DATA_TYPE ||
1995 !body->subtype ||
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"));
1999 dprint((2,
2000 "Remote IMAP folder has wrong contents\n"));
2001 if(tempfile){
2002 our_unlink(tempfile);
2003 fs_give((void **)&tempfile);
2006 if(!(rd->flags & NO_FILE))
2007 so_give(&store);
2009 if(we_cancel)
2010 cancel_busy_cue(-1);
2012 return -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"));
2019 dprint((2,
2020 "Can't access check date in remote data\n"));
2021 if(tempfile){
2022 our_unlink(tempfile);
2023 fs_give((void **)&tempfile);
2026 if(!(rd->flags & NO_FILE))
2027 so_give(&store);
2029 if(we_cancel)
2030 cancel_busy_cue(-1);
2032 return -1;
2035 if(rd && rd->flags & USER_SAID_YES)
2036 chk = 0;
2037 else
2038 chk = rd_check_for_suspect_data(rd);
2040 switch(chk){
2041 case -1: /* suspicious data, user says abort */
2042 if(tempfile){
2043 our_unlink(tempfile);
2044 fs_give((void **)&tempfile);
2047 if(!(rd->flags & NO_FILE))
2048 so_give(&store);
2050 if(we_cancel)
2051 cancel_busy_cue(-1);
2053 return -1;
2055 case 1: /* suspicious data, user says go ahead */
2056 if(rd_remote_is_readonly(rd)){
2057 dprint((1,
2058 "rd_update_local: can't upgrade, readonly\n"));
2060 else
2061 /* attempt to upgrade cookie in last message */
2062 (void)rd_upgrade_cookies(rd, 0, 1);
2064 break;
2066 case 0: /* all is ok */
2067 default:
2068 break;
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);
2083 if(we_cancel)
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));
2090 error = ebuf;
2094 if(error){
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 : "?"));
2099 if(tempfile){
2100 our_unlink(tempfile);
2101 fs_give((void **)&tempfile);
2104 return -1;
2107 if(tempfile && (i = rename_file(tempfile, rd->lf)) < 0){
2108 #ifdef _WINDOWS
2109 if(i == -5){
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?"));
2115 dprint((2,
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)));
2121 else
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));
2127 dprint((2,
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);
2137 return -1;
2140 dprint((5,
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;
2148 if(tempfile)
2149 fs_give((void **)&tempfile);
2151 return 0;
2153 else{
2154 q_status_message1(SM_ORDER | SM_DING, 5, 5,
2155 _("Can't open remote IMAP folder \"%s\""), rd->rn);
2156 dprint((1,
2157 "Can't open remote IMAP folder \"%s\"\n",
2158 rd->rn ? rd->rn : "?"));
2159 rd->access = ReadOnly;
2160 return -1;
2163 break;
2165 default:
2166 dprint((1, "rd_update_local: Unsupported type\n"));
2167 return -1;
2173 * Copy local data to remote folder.
2175 * Args lf -- Local file name
2176 * rn -- Remote 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
2182 * 0 on success
2185 rd_update_remote(REMDATA_S *rd, char *returndate)
2187 STORE_S *store;
2188 int err = 0;
2189 long openmode = SP_USEPOOL | SP_TEMPUSE;
2190 MAILSTREAM *st;
2191 char *eptr = NULL;
2193 if(rd && rd->type != RemImap){
2194 dprint((1, "rd_update_remote: type not supported\n"));
2195 return -1;
2198 if(!rd || !(rd->flags & NO_FILE || rd->lf) || !rd->rn ||
2199 !rd->so || !rd->t.i.special_hdr){
2200 dprint((1,
2201 "rd_update_remote: Unexpected error: %s is NULL\n",
2202 !rd ? "rd" :
2203 !(rd->flags & NO_FILE || rd->lf) ? "localfile" :
2204 !rd->rn ? "remotename" :
2205 !rd->so ? "so" :
2206 !rd->t.i.special_hdr ? "special_hdr" : "?"));
2207 return -1;
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);
2218 if(!st)
2219 st = rd->t.i.stream = context_open(NULL, NULL, rd->rn, openmode, NULL);
2221 if(!st){
2222 q_status_message1(SM_ORDER | SM_DING, 5, 5,
2223 _("Can't open \"%s\" for copying"), rd->rn);
2224 dprint((1,
2225 "rd_update_remote: Can't open remote folder \"%s\" for copying\n",
2226 rd->rn ? rd->rn : "?"));
2227 return 1;
2230 rd->last_use = get_adj_time();
2231 err = rd_chk_for_hdr_msg(&st, rd, &eptr);
2232 if(err){
2233 q_status_message1(SM_ORDER | SM_DING, 5, 5,
2234 _("\"%s\" has invalid format"), rd->rn);
2236 if(eptr){
2237 q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
2238 dprint((1, "%s\n", eptr));
2239 fs_give((void **)&eptr);
2242 dprint((1,
2243 "rd_update_remote: \"%s\" has invalid format\n",
2244 rd->rn ? rd->rn : "?"));
2245 return 1;
2248 errno = 0;
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;
2258 if(store)
2259 so_seek(store, 0L, 0); /* rewind */
2261 else
2262 store = so_get(FileStar, rd->lf, READ_ACCESS);
2264 if(store != NULL){
2265 char date[200];
2266 unsigned char c;
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)
2271 err = 1;
2273 rfc822_date(date);
2274 dprint((7,
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))
2279 err = 1;
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)
2295 err = 1;
2297 last_c = 0;
2299 else{
2300 last_c = c;
2301 if(so_writec((int) c, rd->so) == 0)
2302 err = 1;
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.
2311 if(!err){
2312 MAILSTREAM *st;
2314 if((st = rd->t.i.stream) != NULL)
2315 rd->t.i.shouldbe_nmsgs = rd->t.i.stream->nmsgs + 1;
2316 else
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))
2325 so_give(&store);
2327 else
2328 err = -1;
2330 if(err)
2331 dprint((2, "error in rd_update_remote for %s => %s\n",
2332 rd->lf ? rd->lf : "<mem>", rd->rn ? rd->rn : "?"));
2334 return(err);
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
2346 void
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"));
2360 return;
2363 if(!rd || rd->flags & REM_OUTOFDATE || rd->flags & USE_OLD_CACHE)
2364 return;
2366 if(!rd->t.i.chk_date){
2367 dprint((2,
2368 "rd_check_remvalid: remote data %s changed (chk_date)\n",
2369 rd->rn));
2370 rd->flags |= REM_OUTOFDATE;
2371 return;
2374 if(rd->t.i.chk_nmsgs <= 1){
2375 dprint((2,
2376 "rd_check_remvalid: remote data %s changed (chk_nmsgs <= 1)\n",
2377 rd->rn ? rd->rn : "?"));
2378 rd->flags |= REM_OUTOFDATE;
2379 return;
2382 if(do_it_now < 0L){
2383 chk_interval = -1L * do_it_now;
2384 do_it_now = 0L;
2386 else
2387 chk_interval = ps_global->remote_abook_validity * 60L;
2389 /* too soon to check again */
2390 if(!do_it_now &&
2391 (chk_interval == 0L ||
2392 get_adj_time() <= rd->last_valid_chk + chk_interval))
2393 return;
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){
2403 dprint((2,
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;
2408 return;
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.
2417 rd_ping_stream(rd);
2419 try_looking_in_stream:
2422 * Get the current number of messages in the folder to
2423 * compare with our saved number of messages.
2425 current_nmsgs = 0;
2426 if(rd->t.i.stream){
2427 dprint((7, "using open remote data stream\n"));
2428 rd->last_use = get_adj_time();
2429 current_nmsgs = rd->t.i.stream->nmsgs;
2430 got_cmsgs++;
2432 else{
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.
2449 if(stat_stream){
2450 if(!LEVELSTATUS(stat_stream)){
2451 rd->flags |= NO_STATUSCMD;
2452 dprint((2,
2453 "rd_check_remvalid: remote data %s: server doesn't support status\n",
2454 rd->rn ? rd->rn : "?"));
2456 else{
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,
2466 * too bad.
2468 if(same_stream_and_mailbox(rd->rn, stat_stream)){
2469 dprint((7,
2470 "rd_check_remvalid: faking status\n"));
2471 mm_status_result.flags = SA_MESSAGES | SA_UIDVALIDITY
2472 | SA_UIDNEXT;
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;
2477 else{
2479 dprint((7,
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;
2486 dprint((2,
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){
2499 dprint((7,
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;
2504 got_cmsgs++;
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;
2516 dprint((2,
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));
2519 return;
2523 * Get the current uidvalidity and uidnext values from the
2524 * folder to compare with our saved values.
2526 if(rd->t.i.stream){
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;
2535 dprint((2,
2536 "rd_check_remvalid: remote data %s changed (uid)\n",
2537 rd->rn ? rd->rn : "?"));
2539 else{
2540 dprint((7,"rd_check_remvalid: uids match\n"));
2543 return;
2545 else{
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;
2551 dprint((7,
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){
2562 * Uids are valid.
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"));
2570 return;
2572 else{ /* uidnext changed, folder _may_ have changed */
2574 dprint((7,
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,
2587 NULL)) != NULL){
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();
2597 dprint((7,
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;
2602 dprint((2,
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.
2616 dprint((2,
2617 "rd_check_remvalid: remote data %s false change: adjusting uidnext from %lu to %lu\n",
2618 rd->rn ? rd->rn : "?",
2619 rd->t.i.uidnext,
2620 mm_status_result.uidnext));
2621 rd->t.i.uidnext = mm_status_result.uidnext;
2622 rd_write_metadata(rd, 0);
2625 if(we_cancel)
2626 cancel_busy_cue(-1);
2628 return;
2630 else{
2631 ps_global->noshow_error = 0;
2632 dprint((2,
2633 "rd_check_remvalid: couldn't open %s\n",
2634 rd->rn ? rd->rn : "?"));
2638 else{
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.
2651 if(stat_stream){
2652 rd->flags |= NO_STATUSCMD;
2653 dprint((7,
2654 "rd_check_remvalid: remote data %s don't use status\n",
2655 rd->rn ? rd->rn : "?"));
2658 dprint((7,
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,
2663 NULL);
2664 ps_global->noshow_error = 0;
2665 if(we_cancel)
2666 cancel_busy_cue(-1);
2668 we_cancel = 0;
2669 if(rd->t.i.stream)
2670 goto try_looking_in_stream;
2671 else{
2672 dprint((7,
2673 "rd_check_remvalid: cannot open remote mailbox\n"));
2674 return;
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.
2689 dprint((7,
2690 "rd_check_remvalid: falling back to Date check\n"));
2693 * Fall back to looking in the folder at the Date header.
2696 if(!rd->t.i.stream)
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;
2709 dprint((2,
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;
2719 dprint((2,
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();
2726 if(env)
2727 dprint((7,
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;
2735 if(we_cancel)
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))
2753 return(1);
2755 switch(rd->type){
2756 case RemImap:
2757 if(rd->t.i.stream->rdonly)
2758 return(2);
2760 break;
2762 default:
2763 q_status_message(SM_ORDER, 3, 5,
2764 "rd_remote_is_readonly: type not supported");
2765 break;
2768 return(0);
2773 * Returns 0 if ok
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)
2780 int ans = -1;
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)
2785 return -1;
2787 fields[0] = rd->t.i.special_hdr;
2788 fields[1] = "received";
2789 fields[2] = NULL;
2790 cookie = 0L;
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);
2796 else if(values[0]){
2797 cookie = strtoul(values[0], (char **)NULL, 10);
2798 if(cookie == rd->cookie) /* all's well */
2799 ans = 0;
2800 else
2801 ans = rd_prompt_about_forged_remote_data(cookie > 1L
2802 ? 100 : cookie,
2803 rd, values[0]);
2805 else
2806 ans = rd_prompt_about_forged_remote_data(-2, rd, NULL);
2808 if(values[0])
2809 fs_give((void **)&values[0]);
2811 if(values[1])
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);
2819 return ans;