IMAP: try (hard) to reinstantiate (though as sth. obsolete)
[s-mailx.git] / folder.c
blob43c66ca06e5fe89ef37eb403b5017753be39ff29
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Folder (mailbox) initialization, newmail announcement and related.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE folder
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #include <pwd.h>
44 /* Update mailname (if name != NULL) and displayname, return whether displayname
45 * was large enough to swallow mailname */
46 static bool_t _update_mailname(char const *name);
47 #ifdef HAVE_C90AMEND1 /* TODO unite __narrow_suffix() into one fun! */
48 SINLINE size_t __narrow_suffix(char const *cp, size_t cpl, size_t maxl);
49 #endif
51 /**/
52 static void a_folder_info(void);
54 static bool_t
55 _update_mailname(char const *name) /* TODO 2MUCH work, cache, prop of Object! */
57 char const *foldp;
58 char *mailp, *dispp;
59 size_t i, j, foldlen;
60 bool_t rv;
61 NYD_ENTER;
63 /* Don't realpath(3) if it's only an update request */
64 if(name != NULL){
65 #ifdef HAVE_REALPATH
66 char const *adjname;
67 enum protocol p = which_protocol(name, TRU1, TRU1, &adjname);
69 if(p == PROTO_FILE || p == PROTO_MAILDIR){
70 name = adjname;
71 if (realpath(name, mailname) == NULL && n_err_no != n_ERR_NOENT) {
72 n_err(_("Can't canonicalize %s\n"), n_shexp_quote_cp(name, FAL0));
73 rv = FAL0;
74 goto jdocopy;
76 }else
77 jdocopy:
78 #endif
79 n_strscpy(mailname, name, sizeof(mailname));
82 mailp = mailname;
83 dispp = displayname;
85 /* Don't display an absolute path but "+FOLDER" if under *folder* */
86 if(*(foldp = n_folder_query()) != '\0'){
87 foldlen = strlen(foldp);
88 if(strncmp(foldp, mailp, foldlen))
89 foldlen = 0;
90 }else
91 foldlen = 0;
93 /* We want to see the name of the folder .. on the screen */
94 i = strlen(mailp);
95 if(i < sizeof(displayname) - 3 -1){
96 if(foldlen > 0){
97 *dispp++ = '+';
98 *dispp++ = '[';
99 memcpy(dispp, mailp, foldlen);
100 dispp += foldlen;
101 mailp += foldlen;
102 *dispp++ = ']';
103 memcpy(dispp, mailp, i -= foldlen);
104 dispp[i] = '\0';
105 }else
106 memcpy(dispp, mailp, i +1);
107 rv = TRU1;
108 }else{
109 /* Avoid disrupting multibyte sequences (if possible) */
110 #ifndef HAVE_C90AMEND1
111 j = sizeof(displayname) / 3 - 3;
112 i -= sizeof(displayname) - (1/* + */ + 3) - j;
113 #else
114 j = field_detect_clip(sizeof(displayname) / 3, mailp, i);
115 i = j + __narrow_suffix(mailp + j, i - j,
116 sizeof(displayname) - (1/* + */ + 3 + 1) - j);
117 #endif
118 snprintf(dispp, sizeof(displayname), "%s%.*s...%s",
119 (foldlen > 0 ? "[+]" : ""), (int)j, mailp, mailp + i);
120 rv = FAL0;
123 n_PS_ROOT_BLOCK((ok_vset(mailbox_resolved, mailname),
124 ok_vset(mailbox_display, displayname)));
125 NYD_LEAVE;
126 return rv;
129 #ifdef HAVE_C90AMEND1
130 SINLINE size_t
131 __narrow_suffix(char const *cp, size_t cpl, size_t maxl)
133 int err;
134 size_t i, ok;
135 NYD_ENTER;
137 for (err = ok = i = 0; cpl > maxl || err;) {
138 int ml = mblen(cp, cpl);
139 if (ml < 0) { /* XXX _narrow_suffix(): mblen() error; action? */
140 (void)mblen(NULL, 0);
141 err = 1;
142 ml = 1;
143 } else {
144 if (!err)
145 ok = i;
146 err = 0;
147 if (ml == 0)
148 break;
150 cp += ml;
151 i += ml;
152 cpl -= ml;
154 NYD_LEAVE;
155 return ok;
157 #endif /* HAVE_C90AMEND1 */
159 static void
160 a_folder_info(void){
161 struct message *mp;
162 int u, n, d, s, hidden, moved;
163 NYD2_ENTER;
165 if(mb.mb_type == MB_VOID){
166 fprintf(n_stdout, _("(Currently no active mailbox)"));
167 goto jleave;
170 s = d = hidden = moved = 0;
171 for (mp = message, n = 0, u = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
172 if (mp->m_flag & MNEW)
173 ++n;
174 if ((mp->m_flag & MREAD) == 0)
175 ++u;
176 if ((mp->m_flag & (MDELETED | MSAVED)) == (MDELETED | MSAVED))
177 ++moved;
178 if ((mp->m_flag & (MDELETED | MSAVED)) == MDELETED)
179 ++d;
180 if ((mp->m_flag & (MDELETED | MSAVED)) == MSAVED)
181 ++s;
182 if (mp->m_flag & MHIDDEN)
183 ++hidden;
186 /* If displayname gets truncated the user effectively has no option to see
187 * the full pathname of the mailbox, so print it at least for '? fi' */
188 fprintf(n_stdout, "%s: ", n_shexp_quote_cp(
189 (_update_mailname(NULL) ? displayname : mailname), FAL0));
190 if (msgCount == 1)
191 fprintf(n_stdout, _("1 message"));
192 else
193 fprintf(n_stdout, _("%d messages"), msgCount);
194 if (n > 0)
195 fprintf(n_stdout, _(" %d new"), n);
196 if (u-n > 0)
197 fprintf(n_stdout, _(" %d unread"), u);
198 if (d > 0)
199 fprintf(n_stdout, _(" %d deleted"), d);
200 if (s > 0)
201 fprintf(n_stdout, _(" %d saved"), s);
202 if (moved > 0)
203 fprintf(n_stdout, _(" %d moved"), moved);
204 if (hidden > 0)
205 fprintf(n_stdout, _(" %d hidden"), hidden);
206 if (mb.mb_perm == 0)
207 fprintf(n_stdout, _(" [Read-only]"));
208 #ifdef HAVE_IMAP
209 if (mb.mb_type == MB_CACHE)
210 fprintf(n_stdout, _(" [Disconnected]"));
211 #endif
213 jleave:
214 putc('\n', n_stdout);
215 NYD2_LEAVE;
218 FL int
219 setfile(char const *name, enum fedit_mode fm) /* TODO oh my god */
221 /* TODO This all needs to be converted to URL:: and Mailbox:: */
222 static int shudclob;
224 struct stat stb;
225 size_t offset;
226 char const *who, *orig_name;
227 int rv, omsgCount = 0;
228 FILE *ibuf = NULL, *lckfp = NULL;
229 bool_t isdevnull = FAL0;
230 NYD_ENTER;
232 n_pstate &= ~n_PS_SETFILE_OPENED;
234 /* C99 */{
235 enum fexp_mode fexpm;
237 if((who = shortcut_expand(name)) != NULL){
238 fexpm = FEXP_NSHORTCUT/* XXX | FEXP_NSHELL*/;
239 name = who;
240 }else
241 fexpm = FEXP_FULL/* XXX FEXP_NSHELL*/;
243 if(name[0] == '%'){
244 char const *cp;
246 fm |= FEDIT_SYSBOX; /* TODO fexpand() needs to tell is-valid-user! */
247 if(*(who = &name[1]) == ':')
248 ++who;
249 if((cp = strrchr(who, '/')) != NULL)
250 who = &cp[1];
251 if(*who == '\0')
252 goto jlogname;
253 }else
254 jlogname:
255 who = ok_vlook(LOGNAME);
257 if ((name = fexpand(name, fexpm)) == NULL)
258 goto jem1;
261 /* For at least substdate() users TODO -> eventloop tick */
262 time_current_update(&time_current, FAL0);
264 switch (which_protocol(orig_name = name, TRU1, TRU1, &name)) {
265 case PROTO_FILE:
266 isdevnull = ((n_poption & n_PO_BATCH_FLAG) &&
267 !strcmp(name, n_path_devnull));
268 #ifdef HAVE_REALPATH
269 do { /* TODO we need objects, goes away then */
270 # ifdef HAVE_REALPATH_NULL
271 char *cp;
273 if ((cp = realpath(name, NULL)) != NULL) {
274 name = savestr(cp);
275 (free)(cp);
277 # else
278 char cbuf[PATH_MAX];
280 if (realpath(name, cbuf) != NULL)
281 name = savestr(cbuf);
282 # endif
283 } while (0);
284 #endif
285 rv = 1;
286 break;
287 case PROTO_MAILDIR:
288 shudclob = 1;
289 rv = maildir_setfile(name, fm);
290 goto jleave;
291 #ifdef HAVE_POP3
292 case PROTO_POP3:
293 shudclob = 1;
294 rv = pop3_setfile(orig_name, fm);
295 goto jleave;
296 #endif
297 #ifdef HAVE_IMAP
298 case PROTO_IMAP:
299 shudclob = 1;
300 if((fm & FEDIT_NEWMAIL) && mb.mb_type == MB_CACHE)
301 rv = 1;
302 else
303 rv = imap_setfile(orig_name, fm);
304 goto jleave;
305 #endif
306 default:
307 n_err(_("Cannot handle protocol: %s\n"), orig_name);
308 goto jem1;
311 if ((ibuf = n_fopen_any(savecat("file://", name), "r", NULL)) == NULL) {
312 int e = n_err_no;
314 if ((fm & FEDIT_SYSBOX) && e == n_ERR_NOENT) {
315 if (strcmp(who, ok_vlook(LOGNAME)) && getpwnam(who) == NULL) {
316 n_err(_("%s is not a user of this system\n"),
317 n_shexp_quote_cp(who, FAL0));
318 goto jem2;
320 if (!(n_poption & n_PO_QUICKRUN_MASK) && ok_blook(bsdcompat))
321 n_err(_("No mail for %s\n"), who);
323 if (fm & FEDIT_NEWMAIL)
324 goto jleave;
326 mb.mb_type = MB_VOID;
327 if (ok_blook(emptystart)) {
328 if (!(n_poption & n_PO_QUICKRUN_MASK) && !ok_blook(bsdcompat))
329 n_perr(name, e);
330 /* We must avoid returning -1 and causing program exit */
331 rv = 1;
332 goto jleave;
334 n_perr(name, e);
335 goto jem1;
338 if (fstat(fileno(ibuf), &stb) == -1) {
339 if (fm & FEDIT_NEWMAIL)
340 goto jleave;
341 n_perr(_("fstat"), 0);
342 goto jem1;
345 if (S_ISREG(stb.st_mode) || isdevnull) {
346 /* EMPTY */
347 } else {
348 if (fm & FEDIT_NEWMAIL)
349 goto jleave;
350 n_err_no = S_ISDIR(stb.st_mode) ? n_ERR_ISDIR : n_ERR_INVAL;
351 n_perr(name, 0);
352 goto jem1;
355 if (shudclob && !(fm & FEDIT_NEWMAIL) && !quit(FAL0))
356 goto jem2;
358 hold_sigs();
360 #ifdef HAVE_SOCKETS
361 if (!(fm & FEDIT_NEWMAIL) && mb.mb_sock.s_fd >= 0)
362 sclose(&mb.mb_sock); /* TODO sorry? VMAILFS->close(), thank you */
363 #endif
365 /* TODO There is no intermediate VOID box we've switched to: name may
366 * TODO point to the same box that we just have written, so any updates
367 * TODO we won't see! Reopen again in this case. RACY! Goes with VOID! */
368 /* TODO In addition: in case of compressed/hook boxes we lock a temporary! */
369 /* TODO We may uselessly open twice but quit() doesn't say whether we were
370 * TODO modified so we can't tell: Mailbox::is_modified() :-(( */
371 if (/*shudclob && !(fm & FEDIT_NEWMAIL) &&*/ !strcmp(name, mailname)) {
372 name = mailname;
373 Fclose(ibuf);
375 if ((ibuf = n_fopen_any(name, "r", NULL)) == NULL ||
376 fstat(fileno(ibuf), &stb) == -1 ||
377 (!S_ISREG(stb.st_mode) && !isdevnull)) {
378 n_perr(name, 0);
379 rele_sigs();
380 goto jem2;
384 /* Copy the messages into /tmp and set pointers */
385 if (!(fm & FEDIT_NEWMAIL)) {
386 if (isdevnull) {
387 mb.mb_type = MB_VOID;
388 mb.mb_perm = 0;
389 } else {
390 mb.mb_type = MB_FILE;
391 mb.mb_perm = (((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY) ||
392 access(name, W_OK) < 0) ? 0 : MB_DELE | MB_EDIT);
393 if (shudclob) {
394 if (mb.mb_itf) {
395 fclose(mb.mb_itf);
396 mb.mb_itf = NULL;
398 if (mb.mb_otf) {
399 fclose(mb.mb_otf);
400 mb.mb_otf = NULL;
404 shudclob = 1;
405 if (fm & FEDIT_SYSBOX)
406 n_pstate &= ~n_PS_EDIT;
407 else
408 n_pstate |= n_PS_EDIT;
409 initbox(name);
410 offset = 0;
411 } else {
412 fseek(mb.mb_otf, 0L, SEEK_END);
413 /* TODO Doing this without holding a lock is.. And not err checking.. */
414 fseek(ibuf, mailsize, SEEK_SET);
415 offset = mailsize;
416 omsgCount = msgCount;
419 if (isdevnull)
420 lckfp = (FILE*)-1;
421 else if (!(n_pstate & n_PS_EDIT))
422 lckfp = n_dotlock(name, fileno(ibuf), FLT_READ, offset,0,
423 (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX));
424 else if (n_file_lock(fileno(ibuf), FLT_READ, offset,0,
425 (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX)))
426 lckfp = (FILE*)-1;
428 if (lckfp == NULL) {
429 if (!(fm & FEDIT_NEWMAIL)) {
430 char const *emsg = (n_pstate & n_PS_EDIT)
431 ? N_("Unable to lock mailbox, aborting operation")
432 : N_("Unable to (dot) lock mailbox, aborting operation");
433 n_perr(V_(emsg), 0);
435 rele_sigs();
436 if (!(fm & FEDIT_NEWMAIL))
437 goto jem1;
438 goto jleave;
441 mailsize = fsize(ibuf);
443 /* TODO This is too simple minded? We should regenerate an index file
444 * TODO to be able to truly tell whether *anything* has changed! */
445 if ((fm & FEDIT_NEWMAIL) && UICMP(z, mailsize, <=, offset)) {
446 rele_sigs();
447 goto jleave;
449 setptr(ibuf, offset);
450 setmsize(msgCount);
451 if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted) {
452 mb.mb_threaded = 0;
453 c_sort((void*)-1);
456 Fclose(ibuf);
457 ibuf = NULL;
458 if (lckfp != NULL && lckfp != (FILE*)-1) {
459 Pclose(lckfp, FAL0);
460 /*lckfp = NULL;*/
463 if (!(fm & FEDIT_NEWMAIL)) {
464 n_pstate &= ~n_PS_SAW_COMMAND;
465 n_pstate |= n_PS_SETFILE_OPENED;
468 rele_sigs();
470 rv = (msgCount == 0);
471 if(rv)
472 n_err_no = n_ERR_NODATA;
474 if(n_poption & n_PO_EXISTONLY)
475 goto jleave;
477 if(rv){
478 if(!(n_pstate & n_PS_EDIT) || (fm & FEDIT_NEWMAIL)){
479 if(!(fm & FEDIT_NEWMAIL)){
480 if (!ok_blook(emptystart))
481 n_err(_("No mail for %s\n"), who);
483 goto jleave;
487 if(fm & FEDIT_NEWMAIL)
488 newmailinfo(omsgCount);
489 jleave:
490 if (ibuf != NULL) {
491 Fclose(ibuf);
492 if (lckfp != NULL && lckfp != (FILE*)-1)
493 Pclose(lckfp, FAL0);
495 NYD_LEAVE;
496 return rv;
497 jem2:
498 mb.mb_type = MB_VOID;
499 jem1:
500 n_err_no = n_ERR_NOTOBACCO;
501 rv = -1;
502 goto jleave;
505 FL int
506 newmailinfo(int omsgCount)
508 int mdot, i;
509 NYD_ENTER;
511 for (i = 0; i < omsgCount; ++i)
512 message[i].m_flag &= ~MNEWEST;
514 if (msgCount > omsgCount) {
515 for (i = omsgCount; i < msgCount; ++i)
516 message[i].m_flag |= MNEWEST;
517 fprintf(n_stdout, _("New mail has arrived.\n"));
518 if ((i = msgCount - omsgCount) == 1)
519 fprintf(n_stdout, _("Loaded 1 new message.\n"));
520 else
521 fprintf(n_stdout, _("Loaded %d new messages.\n"), i);
522 } else
523 fprintf(n_stdout, _("Loaded %d messages.\n"), msgCount);
525 temporary_folder_hook_check(TRU1);
527 mdot = getmdot(1);
529 if (ok_blook(header))
530 print_headers(omsgCount + 1, msgCount, FAL0);
531 NYD_LEAVE;
532 return mdot;
535 FL void
536 setmsize(int sz)
538 NYD_ENTER;
539 if (n_msgvec != NULL)
540 free(n_msgvec);
541 n_msgvec = scalloc(sz + 1, sizeof *n_msgvec);
542 NYD_LEAVE;
545 FL void
546 print_header_summary(char const *Larg)
548 size_t bot, top, i, j;
549 NYD_ENTER;
551 if (Larg != NULL) {
552 /* Avoid any messages XXX add a make_mua_silent() and use it? */
553 if ((n_poption & (n_PO_VERB | n_PO_EXISTONLY)) == n_PO_EXISTONLY) {
554 n_stdout = freopen(n_path_devnull, "w", stdout);
555 n_stderr = freopen(n_path_devnull, "w", stderr);
557 assert(n_msgvec != NULL);
558 i = (getmsglist(/*TODO make const */n_UNCONST(Larg), n_msgvec, 0) <= 0);
559 if (n_poption & n_PO_EXISTONLY) {
560 n_exit_status = (int)i;
561 goto jleave;
563 if (i)
564 goto jleave;
565 for (bot = msgCount, top = 0, i = 0; (j = n_msgvec[i]) != 0; ++i) {
566 if (bot > j)
567 bot = j;
568 if (top < j)
569 top = j;
571 } else
572 bot = 1, top = msgCount;
573 print_headers(bot, top, (Larg != NULL)); /* TODO should take iterator!! */
574 jleave:
575 NYD_LEAVE;
578 FL void
579 n_folder_announce(enum n_announce_flags af){
580 int vec[2], mdot;
581 NYD_ENTER;
583 mdot = (mb.mb_type == MB_VOID) ? 1 : getmdot(0);
584 dot = &message[mdot - 1];
586 if(af != n_ANNOUNCE_NONE && ok_blook(header) &&
587 ((af & n_ANNOUNCE_MAIN_CALL) ||
588 ((af & n_ANNOUNCE_CHANGE) && !ok_blook(posix))))
589 af |= n_ANNOUNCE_STATUS | n__ANNOUNCE_HEADER;
591 if(af & n_ANNOUNCE_STATUS){
592 a_folder_info();
593 af |= n__ANNOUNCE_ANY;
596 if(af & n__ANNOUNCE_HEADER){
597 if(!(af & n_ANNOUNCE_MAIN_CALL) && ok_blook(bsdannounce))
598 n_OBSOLETE(_("*bsdannounce* is now default behaviour"));
599 vec[0] = mdot;
600 vec[1] = 0;
601 print_header_group(vec); /* XXX errors? */
602 af |= n__ANNOUNCE_ANY;
605 if(af & n__ANNOUNCE_ANY)
606 fflush(n_stdout);
607 NYD_LEAVE;
610 FL int
611 getmdot(int nmail)
613 struct message *mp;
614 char *cp;
615 int mdot;
616 enum mflag avoid = MHIDDEN | MDELETED;
617 NYD_ENTER;
619 if (!nmail) {
620 if (ok_blook(autothread)) {
621 n_OBSOLETE(_("please use *autosort=thread* instead of *autothread*"));
622 c_thread(NULL);
623 } else if ((cp = ok_vlook(autosort)) != NULL) {
624 if (mb.mb_sorted != NULL)
625 free(mb.mb_sorted);
626 mb.mb_sorted = sstrdup(cp);
627 c_sort(NULL);
630 if (mb.mb_type == MB_VOID) {
631 mdot = 1;
632 goto jleave;
635 if (nmail)
636 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
637 if ((mp->m_flag & (MNEWEST | avoid)) == MNEWEST)
638 break;
640 if (!nmail || PTRCMP(mp, >=, message + msgCount)) {
641 if (mb.mb_threaded) {
642 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
643 if ((mp->m_flag & (MNEW | avoid)) == MNEW)
644 break;
645 } else {
646 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
647 if ((mp->m_flag & (MNEW | avoid)) == MNEW)
648 break;
652 if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
653 if (mb.mb_threaded) {
654 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
655 if (mp->m_flag & MFLAGGED)
656 break;
657 } else {
658 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
659 if (mp->m_flag & MFLAGGED)
660 break;
664 if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
665 if (mb.mb_threaded) {
666 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
667 if (!(mp->m_flag & (MREAD | avoid)))
668 break;
669 } else {
670 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
671 if (!(mp->m_flag & (MREAD | avoid)))
672 break;
676 if (nmail &&
677 (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
678 mdot = (int)PTR2SIZE(mp - message + 1);
679 else if (ok_blook(showlast)) {
680 if (mb.mb_threaded) {
681 for (mp = this_in_thread(threadroot, -1); mp;
682 mp = prev_in_thread(mp))
683 if (!(mp->m_flag & avoid))
684 break;
685 mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
686 } else {
687 for (mp = message + msgCount - 1; mp >= message; --mp)
688 if (!(mp->m_flag & avoid))
689 break;
690 mdot = (mp >= message) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
692 } else if (!nmail &&
693 (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
694 mdot = (int)PTR2SIZE(mp - message + 1);
695 else if (mb.mb_threaded) {
696 for (mp = threadroot; mp; mp = next_in_thread(mp))
697 if (!(mp->m_flag & avoid))
698 break;
699 mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : 1;
700 } else {
701 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
702 if (!(mp->m_flag & avoid))
703 break;
704 mdot = PTRCMP(mp, <, message + msgCount)
705 ? (int)PTR2SIZE(mp - message + 1) : 1;
707 jleave:
708 NYD_LEAVE;
709 return mdot;
712 FL void
713 initbox(char const *name)
715 char *tempMesg;
716 NYD_ENTER;
718 if (mb.mb_type != MB_VOID)
719 n_strscpy(prevfile, mailname, PATH_MAX);
721 /* TODO name always NE mailname (but goes away for objects anyway)
722 * TODO Well, not true no more except that in parens */
723 _update_mailname((name != mailname) ? name : NULL);
725 if ((mb.mb_otf = Ftmp(&tempMesg, "tmpmbox", OF_WRONLY | OF_HOLDSIGS)) ==
726 NULL) {
727 n_perr(_("temporary mail message file"), 0);
728 exit(n_EXIT_ERR);
730 if ((mb.mb_itf = safe_fopen(tempMesg, "r", NULL)) == NULL) {
731 n_perr(_("temporary mail message file"), 0);
732 exit(n_EXIT_ERR);
734 Ftmp_release(&tempMesg);
736 message_reset();
737 mb.mb_active = MB_NONE;
738 mb.mb_threaded = 0;
739 #ifdef HAVE_IMAP
740 mb.mb_flags = MB_NOFLAGS;
741 #endif
742 if (mb.mb_sorted != NULL) {
743 free(mb.mb_sorted);
744 mb.mb_sorted = NULL;
746 dot = prevdot = threadroot = NULL;
747 n_pstate &= ~n_PS_DID_PRINT_DOT;
748 NYD_LEAVE;
751 FL char const *
752 n_folder_query(void){
753 struct n_string s, *sp = &s;
754 enum protocol proto;
755 char *cp;
756 char const *rv, *adjcp;
757 bool_t err;
758 NYD_ENTER;
760 sp = n_string_creat_auto(sp);
762 /* *folder* is linked with *folder_resolved*: we only use the latter */
763 for(err = FAL0;;){
764 if((rv = ok_vlook(folder_resolved)) != NULL)
765 break;
767 /* POSIX says:
768 * If directory does not start with a <slash> ('/'), the contents
769 * of HOME shall be prefixed to it.
770 * And:
771 * If folder is unset or set to null, [.] filenames beginning with
772 * '+' shall refer to files in the current directory.
773 * We may have the result already */
774 rv = n_empty;
775 err = FAL0;
777 if((cp = ok_vlook(folder)) == NULL)
778 goto jset;
780 /* Expand the *folder*; skip %: prefix for simplicity of use */
781 if(cp[0] == '%' && cp[1] == ':')
782 cp += 2;
783 if((err = (cp = fexpand(cp, FEXP_NSPECIAL | FEXP_NFOLDER | FEXP_NSHELL)
784 ) == NULL) || *cp == '\0')
785 goto jset;
786 else{
787 size_t i;
789 for(i = strlen(cp);;){
790 if(--i == 0)
791 goto jset;
792 if(cp[i] != '/'){
793 cp[++i] = '\0';
794 break;
799 switch((proto = which_protocol(cp, FAL0, FAL0, &adjcp))){
800 case PROTO_POP3:
801 n_err(_("*folder* can't be set to a flat, read-only POP3 account\n"));
802 err = TRU1;
803 goto jset;
804 case PROTO_IMAP:
805 #ifdef HAVE_IMAP
806 rv = cp;
807 if(!strcmp(rv, protbase(rv)))
808 rv = savecatsep(rv, '/', n_empty);
809 #else
810 n_err(_("*folder*: IMAP support not compiled in\n"));
811 err = TRU1;
812 #endif
813 goto jset;
814 default:
815 /* Further expansion desired */
816 break;
819 /* Prefix HOME as necessary */
820 if(*adjcp != '/'){ /* XXX path_is_absolute() */
821 size_t l1, l2;
822 char const *home;
824 home = ok_vlook(HOME);
825 l1 = strlen(home);
826 l2 = strlen(cp);
828 sp = n_string_reserve(sp, l1 + 1 + l2 +1);
829 if(cp != adjcp){
830 size_t i;
832 sp = n_string_push_buf(sp, cp, i = PTR2SIZE(adjcp - cp));
833 cp += i;
834 l2 -= i;
836 sp = n_string_push_buf(sp, home, l1);
837 sp = n_string_push_c(sp, '/');
838 sp = n_string_push_buf(sp, cp, l2);
839 cp = n_string_cp(sp);
840 sp = n_string_drop_ownership(sp);
843 rv = cp;
845 /* TODO Since our visual mailname is resolved via realpath(3) if available
846 * TODO to avoid that we loose track of our currently open folder in case
847 * TODO we chdir away, but still checks the leading path portion against
848 * TODO n_folder_query() to be able to abbreviate to the +FOLDER syntax if
849 * TODO possible, we need to realpath(3) the folder, too */
850 #ifdef HAVE_REALPATH
851 assert(sp->s_len == 0 && sp->s_dat == NULL);
852 # ifndef HAVE_REALPATH_NULL
853 sp = n_string_reserve(sp, PATH_MAX +1);
854 # endif
856 if((sp->s_dat = realpath(cp, sp->s_dat)) != NULL){
857 # ifdef HAVE_REALPATH_NULL
858 n_string_cp(sp = n_string_assign_cp(sp, cp = sp->s_dat));
859 (free)(cp);
860 # endif
861 rv = sp->s_dat;
862 }else if(n_err_no == n_ERR_NOENT)
863 rv = cp;
864 else{
865 n_err(_("Can't canonicalize *folder*: %s\n"),
866 n_shexp_quote_cp(cp, FAL0));
867 err = TRU1;
868 rv = n_empty;
870 sp = n_string_drop_ownership(sp);
871 #endif /* HAVE_REALPATH */
873 /* Always append a solidus to our result path upon success */
874 if(!err){
875 size_t i;
877 if(rv[(i = strlen(rv)) - 1] != '/'){
878 sp = n_string_reserve(sp, i + 1 +1);
879 sp = n_string_push_buf(sp, rv, i);
880 sp = n_string_push_c(sp, '/');
881 rv = n_string_cp(sp);
882 sp = n_string_drop_ownership(sp);
886 jset:
887 n_PS_ROOT_BLOCK(ok_vset(folder_resolved, rv));
890 if(err){
891 n_err(_("*folder* is not resolvable, using CWD\n"));
892 assert(rv != NULL && *rv == '\0');
894 NYD_LEAVE;
895 return rv;
898 FL int
899 n_folder_mbox_prepare_append(FILE *fout, struct stat *st_or_null){
900 /* TODO n_folder_mbox_prepare_append -> Mailbox->append() */
901 struct stat stb;
902 char buf[2];
903 bool_t needsep;
904 int rv;
905 NYD2_ENTER;
907 if(fseek(fout, -2L, SEEK_END)){
908 rv = n_err_no;
910 if(st_or_null == NULL){
911 st_or_null = &stb;
912 if(fstat(fileno(fout), st_or_null))
913 goto jerrno;
916 if(st_or_null->st_size >= 2)
917 goto jleave;
918 if(st_or_null->st_size == 0){
919 rv = n_ERR_NONE;
920 goto jleave;
922 if(fseek(fout, -1L, SEEK_END))
923 goto jerrno;
926 rv = n_ERR_NONE;
928 needsep = FAL0;
929 switch(fread(buf, sizeof *buf, 2, fout)){
930 case 2:
931 if(buf[1] != '\n')
932 needsep = TRU1;
933 break;
934 case 1:
935 if(buf[0] != '\n')
936 needsep = TRU1;
937 break;
938 default:
939 if(ferror(fout))
940 goto jerrno;
941 break;
944 if(fflush(fout) || (needsep && putc('\n', fout) == EOF))
945 jerrno:
946 rv = n_err_no;
947 jleave:
948 NYD2_LEAVE;
949 return rv;
952 /* s-it-mode */