sendout.c:_sendbundle_setup_creds(): fix !HAVE_SMTP (CONFIG=NULL tests)
[s-mailx.git] / folder.c
blob4625f1e0fefcaf3432259f5a2be7e6c9d8f16525
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 #ifdef HAVE_C90AMEND1
52 SINLINE size_t
53 __narrow_suffix(char const *cp, size_t cpl, size_t maxl)
55 int err;
56 size_t i, ok;
57 NYD_ENTER;
59 for (err = ok = i = 0; cpl > maxl || err;) {
60 int ml = mblen(cp, cpl);
61 if (ml < 0) { /* XXX _narrow_suffix(): mblen() error; action? */
62 (void)mblen(NULL, 0);
63 err = 1;
64 ml = 1;
65 } else {
66 if (!err)
67 ok = i;
68 err = 0;
69 if (ml == 0)
70 break;
72 cp += ml;
73 i += ml;
74 cpl -= ml;
76 NYD_LEAVE;
77 return ok;
79 #endif /* HAVE_C90AMEND1 */
81 static bool_t
82 _update_mailname(char const *name)
84 char *mailp, *dispp;
85 size_t i, j;
86 bool_t rv = TRU1;
87 NYD_ENTER;
89 /* Don't realpath(3) if it's only an update request */
90 if (name != NULL) {
91 #ifdef HAVE_REALPATH
92 enum protocol p = which_protocol(name);
94 if (p == PROTO_FILE || p == PROTO_MAILDIR) {
95 if (realpath(name, mailname) == NULL && n_err_no != n_ERR_NOENT) {
96 n_err(_("Can't canonicalize %s\n"), n_shexp_quote_cp(name, FAL0));
97 rv = FAL0;
98 goto jdocopy;
100 } else
101 jdocopy:
102 #endif
103 n_strscpy(mailname, name, sizeof(mailname));
106 mailp = mailname;
107 dispp = displayname;
109 /* Don't display an absolute path but "+FOLDER" if under *folder* */
110 /* C99 */{
111 char const *folderp;
113 if(*(folderp = folder_query()) != '\0'){
114 i = strlen(folderp);
115 if(!strncmp(folderp, mailp, i)){
116 mailp += i;
117 *dispp++ = '+';
122 /* We want to see the name of the folder .. on the screen */
123 i = strlen(mailp);
124 if (i < sizeof(displayname) -1)
125 memcpy(dispp, mailp, i +1);
126 else {
127 rv = FAL0;
128 /* Avoid disrupting multibyte sequences (if possible) */
129 #ifndef HAVE_C90AMEND1
130 j = sizeof(displayname) / 3 - 1;
131 i -= sizeof(displayname) - (1/* + */ + 3) - j;
132 #else
133 j = field_detect_clip(sizeof(displayname) / 3, mailp, i);
134 i = j + __narrow_suffix(mailp + j, i - j,
135 sizeof(displayname) - (1/* + */ + 3 + 1) - j);
136 #endif
137 snprintf(dispp, sizeof(displayname), "%.*s...%s",
138 (int)j, mailp, mailp + i);
141 n_PS_ROOT_BLOCK((ok_vset(mailbox_resolved, mailname),
142 ok_vset(mailbox_display, displayname)));
143 NYD_LEAVE;
144 return rv;
147 FL int
148 setfile(char const *name, enum fedit_mode fm) /* TODO oh my god */
150 static int shudclob;
152 struct stat stb;
153 size_t offset;
154 char const *who;
155 int rv, omsgCount = 0;
156 FILE *ibuf = NULL, *lckfp = NULL;
157 bool_t isdevnull = FAL0;
158 NYD_ENTER;
160 n_pstate &= ~n_PS_SETFILE_OPENED;
162 /* C99 */{
163 enum fexp_mode fexpm;
165 if((who = shortcut_expand(name)) != NULL){
166 fexpm = FEXP_NSHORTCUT/* XXX | FEXP_NSHELL*/;
167 name = who;
168 }else
169 fexpm = FEXP_FULL/* XXX FEXP_NSHELL*/;
171 if(name[0] == '%'){
172 fm |= FEDIT_SYSBOX; /* TODO fexpand() needs to tell is-valid-user! */
173 if(*(who = &name[1]) == ':')
174 ++who;
175 if(*who == '\0')
176 goto jlogname;
177 }else
178 jlogname:
179 who = ok_vlook(LOGNAME);
181 if ((name = fexpand(name, fexpm)) == NULL)
182 goto jem1;
185 /* For at least substdate() users */
186 time_current_update(&time_current, FAL0);
188 switch (which_protocol(name)) {
189 case PROTO_FILE:
190 if (temporary_protocol_ext != NULL)
191 name = savecat(name, temporary_protocol_ext);
192 isdevnull = ((n_poption & n_PO_BATCH_FLAG) && !strcmp(name, "/dev/null"));
193 #ifdef HAVE_REALPATH
194 do { /* TODO we need objects, goes away then */
195 # ifdef HAVE_REALPATH_NULL
196 char *cp;
198 if ((cp = realpath(name, NULL)) != NULL) {
199 name = savestr(cp);
200 (free)(cp);
202 # else
203 char cbuf[PATH_MAX];
205 if (realpath(name, cbuf) != NULL)
206 name = savestr(cbuf);
207 # endif
208 } while (0);
209 #endif
210 rv = 1;
211 break;
212 case PROTO_MAILDIR:
213 shudclob = 1;
214 rv = maildir_setfile(name, fm);
215 goto jleave;
216 #ifdef HAVE_POP3
217 case PROTO_POP3:
218 shudclob = 1;
219 rv = pop3_setfile(name, fm);
220 goto jleave;
221 #endif
222 default:
223 n_err(_("Cannot handle protocol: %s\n"), name);
224 goto jem1;
227 if ((ibuf = Zopen(name, "r")) == NULL) {
228 int e = n_err_no;
230 if ((fm & FEDIT_SYSBOX) && e == n_ERR_NOENT) {
231 if (strcmp(who, ok_vlook(LOGNAME)) && getpwnam(who) == NULL) {
232 n_err(_("%s is not a user of this system\n"),
233 n_shexp_quote_cp(who, FAL0));
234 goto jem2;
236 if (!(n_poption & n_PO_QUICKRUN_MASK) && ok_blook(bsdcompat))
237 n_err(_("No mail for %s\n"), who);
239 if (fm & FEDIT_NEWMAIL)
240 goto jleave;
242 mb.mb_type = MB_VOID;
243 if (ok_blook(emptystart)) {
244 if (!(n_poption & n_PO_QUICKRUN_MASK) && !ok_blook(bsdcompat))
245 n_perr(name, e);
246 /* We must avoid returning -1 and causing program exit */
247 rv = 1;
248 goto jleave;
250 n_perr(name, e);
251 goto jem1;
254 if (fstat(fileno(ibuf), &stb) == -1) {
255 if (fm & FEDIT_NEWMAIL)
256 goto jleave;
257 n_perr(_("fstat"), 0);
258 goto jem1;
261 if (S_ISREG(stb.st_mode) || isdevnull) {
262 /* EMPTY */
263 } else {
264 if (fm & FEDIT_NEWMAIL)
265 goto jleave;
266 n_err_no = S_ISDIR(stb.st_mode) ? n_ERR_ISDIR : n_ERR_INVAL;
267 n_perr(name, 0);
268 goto jem1;
271 if (shudclob && !(fm & FEDIT_NEWMAIL) && !quit(FAL0))
272 goto jem2;
274 hold_sigs();
276 #ifdef HAVE_SOCKETS
277 if (!(fm & FEDIT_NEWMAIL) && mb.mb_sock.s_fd >= 0)
278 sclose(&mb.mb_sock); /* TODO sorry? VMAILFS->close(), thank you */
279 #endif
281 /* TODO There is no intermediate VOID box we've switched to: name may
282 * TODO point to the same box that we just have written, so any updates
283 * TODO we won't see! Reopen again in this case. RACY! Goes with VOID! */
284 /* TODO In addition: in case of compressed/hook boxes we lock a temporary! */
285 /* TODO We may uselessly open twice but quit() doesn't say whether we were
286 * TODO modified so we can't tell: Mailbox::is_modified() :-(( */
287 if (/*shudclob && !(fm & FEDIT_NEWMAIL) &&*/ !strcmp(name, mailname)) {
288 name = mailname;
289 Fclose(ibuf);
291 if ((ibuf = Zopen(name, "r")) == NULL ||
292 fstat(fileno(ibuf), &stb) == -1 ||
293 (!S_ISREG(stb.st_mode) && !isdevnull)) {
294 n_perr(name, 0);
295 rele_sigs();
296 goto jem2;
300 /* Copy the messages into /tmp and set pointers */
301 if (!(fm & FEDIT_NEWMAIL)) {
302 if (isdevnull) {
303 mb.mb_type = MB_VOID;
304 mb.mb_perm = 0;
305 } else {
306 mb.mb_type = MB_FILE;
307 mb.mb_perm = (((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY) ||
308 access(name, W_OK) < 0) ? 0 : MB_DELE | MB_EDIT);
309 if (shudclob) {
310 if (mb.mb_itf) {
311 fclose(mb.mb_itf);
312 mb.mb_itf = NULL;
314 if (mb.mb_otf) {
315 fclose(mb.mb_otf);
316 mb.mb_otf = NULL;
320 shudclob = 1;
321 if (fm & FEDIT_SYSBOX)
322 n_pstate &= ~n_PS_EDIT;
323 else
324 n_pstate |= n_PS_EDIT;
325 initbox(name);
326 offset = 0;
327 } else {
328 fseek(mb.mb_otf, 0L, SEEK_END);
329 /* TODO Doing this without holding a lock is.. And not err checking.. */
330 fseek(ibuf, mailsize, SEEK_SET);
331 offset = mailsize;
332 omsgCount = msgCount;
335 if (isdevnull)
336 lckfp = (FILE*)-1;
337 else if (!(n_pstate & n_PS_EDIT))
338 lckfp = n_dotlock(name, fileno(ibuf), FLT_READ, offset,0,
339 (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX));
340 else if (n_file_lock(fileno(ibuf), FLT_READ, offset,0,
341 (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX)))
342 lckfp = (FILE*)-1;
344 if (lckfp == NULL) {
345 if (!(fm & FEDIT_NEWMAIL)) {
346 char const *emsg = (n_pstate & n_PS_EDIT)
347 ? N_("Unable to lock mailbox, aborting operation")
348 : N_("Unable to (dot) lock mailbox, aborting operation");
349 n_perr(V_(emsg), 0);
351 rele_sigs();
352 if (!(fm & FEDIT_NEWMAIL))
353 goto jem1;
354 goto jleave;
357 mailsize = fsize(ibuf);
359 /* TODO This is too simple minded? We should regenerate an index file
360 * TODO to be able to truly tell whether *anything* has changed! */
361 if ((fm & FEDIT_NEWMAIL) && UICMP(z, mailsize, <=, offset)) {
362 rele_sigs();
363 goto jleave;
365 setptr(ibuf, offset);
366 setmsize(msgCount);
367 if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted) {
368 mb.mb_threaded = 0;
369 c_sort((void*)-1);
372 Fclose(ibuf);
373 ibuf = NULL;
374 if (lckfp != NULL && lckfp != (FILE*)-1) {
375 Pclose(lckfp, FAL0);
376 /*lckfp = NULL;*/
379 if (!(fm & FEDIT_NEWMAIL)) {
380 n_pstate &= ~n_PS_SAW_COMMAND;
381 n_pstate |= n_PS_SETFILE_OPENED;
384 rele_sigs();
386 if ((n_poption & n_PO_EXISTONLY) && !(n_poption & n_PO_HEADERLIST)) {
387 rv = (msgCount == 0);
388 goto jleave;
391 if ((!(n_pstate & n_PS_EDIT) || (fm & FEDIT_NEWMAIL)) && msgCount == 0) {
392 if (!(fm & FEDIT_NEWMAIL)) {
393 if (!ok_blook(emptystart))
394 n_err(_("No mail for %s\n"), who);
396 goto jleave;
399 if (fm & FEDIT_NEWMAIL)
400 newmailinfo(omsgCount);
402 rv = 0;
403 jleave:
404 if (ibuf != NULL) {
405 Fclose(ibuf);
406 if (lckfp != NULL && lckfp != (FILE*)-1)
407 Pclose(lckfp, FAL0);
409 NYD_LEAVE;
410 return rv;
411 jem2:
412 mb.mb_type = MB_VOID;
413 jem1:
414 rv = -1;
415 goto jleave;
418 FL int
419 newmailinfo(int omsgCount)
421 int mdot, i;
422 NYD_ENTER;
424 for (i = 0; i < omsgCount; ++i)
425 message[i].m_flag &= ~MNEWEST;
427 if (msgCount > omsgCount) {
428 for (i = omsgCount; i < msgCount; ++i)
429 message[i].m_flag |= MNEWEST;
430 fprintf(n_stdout, _("New mail has arrived.\n"));
431 if ((i = msgCount - omsgCount) == 1)
432 fprintf(n_stdout, _("Loaded 1 new message.\n"));
433 else
434 fprintf(n_stdout, _("Loaded %d new messages.\n"), i);
435 } else
436 fprintf(n_stdout, _("Loaded %d messages.\n"), msgCount);
438 temporary_folder_hook_check(TRU1);
440 mdot = getmdot(1);
442 if (ok_blook(header))
443 print_headers(omsgCount + 1, msgCount, FAL0);
444 NYD_LEAVE;
445 return mdot;
448 FL void
449 setmsize(int sz)
451 NYD_ENTER;
452 if (n_msgvec != NULL)
453 free(n_msgvec);
454 n_msgvec = scalloc(sz + 1, sizeof *n_msgvec);
455 NYD_LEAVE;
458 FL void
459 print_header_summary(char const *Larg)
461 size_t bot, top, i, j;
462 NYD_ENTER;
464 if (Larg != NULL) {
465 /* Avoid any messages XXX add a make_mua_silent() and use it? */
466 if ((n_poption & (n_PO_VERB | n_PO_EXISTONLY)) == n_PO_EXISTONLY) {
467 n_stdout = freopen("/dev/null", "w", stdout);
468 n_stderr = freopen("/dev/null", "w", stderr);
470 assert(n_msgvec != NULL);
471 i = (getmsglist(/*TODO make const */n_UNCONST(Larg), n_msgvec, 0) <= 0);
472 if (n_poption & n_PO_EXISTONLY) {
473 n_exit_status = (int)i;
474 goto jleave;
476 if (i)
477 goto jleave;
478 for (bot = msgCount, top = 0, i = 0; (j = n_msgvec[i]) != 0; ++i) {
479 if (bot > j)
480 bot = j;
481 if (top < j)
482 top = j;
484 } else
485 bot = 1, top = msgCount;
486 print_headers(bot, top, (Larg != NULL)); /* TODO should take iterator!! */
487 jleave:
488 NYD_LEAVE;
491 FL void
492 announce(int printheaders)
494 int vec[2], mdot;
495 NYD_ENTER;
497 mdot = newfileinfo();
498 vec[0] = mdot;
499 vec[1] = 0;
500 dot = message + mdot - 1;
501 if (printheaders && msgCount > 0 && ok_blook(header)) {
502 print_header_group(vec); /* XXX errors? */
504 NYD_LEAVE;
507 FL int
508 newfileinfo(void)
510 struct message *mp;
511 int u, n, mdot, d, s, hidden, moved;
512 NYD_ENTER;
514 if (mb.mb_type == MB_VOID) {
515 mdot = 1;
516 goto jleave;
519 mdot = getmdot(0);
520 s = d = hidden = moved =0;
521 for (mp = message, n = 0, u = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
522 if (mp->m_flag & MNEW)
523 ++n;
524 if ((mp->m_flag & MREAD) == 0)
525 ++u;
526 if ((mp->m_flag & (MDELETED | MSAVED)) == (MDELETED | MSAVED))
527 ++moved;
528 if ((mp->m_flag & (MDELETED | MSAVED)) == MDELETED)
529 ++d;
530 if ((mp->m_flag & (MDELETED | MSAVED)) == MSAVED)
531 ++s;
532 if (mp->m_flag & MHIDDEN)
533 ++hidden;
536 /* If displayname gets truncated the user effectively has no option to see
537 * the full pathname of the mailbox, so print it at least for '? fi' */
538 fprintf(n_stdout, _("%s: "), n_shexp_quote_cp(
539 (_update_mailname(NULL) ? displayname : mailname), FAL0));
540 if (msgCount == 1)
541 fprintf(n_stdout, _("1 message"));
542 else
543 fprintf(n_stdout, _("%d messages"), msgCount);
544 if (n > 0)
545 fprintf(n_stdout, _(" %d new"), n);
546 if (u-n > 0)
547 fprintf(n_stdout, _(" %d unread"), u);
548 if (d > 0)
549 fprintf(n_stdout, _(" %d deleted"), d);
550 if (s > 0)
551 fprintf(n_stdout, _(" %d saved"), s);
552 if (moved > 0)
553 fprintf(n_stdout, _(" %d moved"), moved);
554 if (hidden > 0)
555 fprintf(n_stdout, _(" %d hidden"), hidden);
556 else if (mb.mb_perm == 0)
557 fprintf(n_stdout, _(" [Read-only]"));
558 putc('\n', n_stdout);
559 jleave:
560 NYD_LEAVE;
561 return mdot;
564 FL int
565 getmdot(int nmail)
567 struct message *mp;
568 char *cp;
569 int mdot;
570 enum mflag avoid = MHIDDEN | MDELETED;
571 NYD_ENTER;
573 if (!nmail) {
574 if (ok_blook(autothread)) {
575 n_OBSOLETE(_("please use *autosort=thread* instead of *autothread*"));
576 c_thread(NULL);
577 } else if ((cp = ok_vlook(autosort)) != NULL) {
578 if (mb.mb_sorted != NULL)
579 free(mb.mb_sorted);
580 mb.mb_sorted = sstrdup(cp);
581 c_sort(NULL);
584 if (mb.mb_type == MB_VOID) {
585 mdot = 1;
586 goto jleave;
589 if (nmail)
590 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
591 if ((mp->m_flag & (MNEWEST | avoid)) == MNEWEST)
592 break;
594 if (!nmail || PTRCMP(mp, >=, message + msgCount)) {
595 if (mb.mb_threaded) {
596 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
597 if ((mp->m_flag & (MNEW | avoid)) == MNEW)
598 break;
599 } else {
600 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
601 if ((mp->m_flag & (MNEW | avoid)) == MNEW)
602 break;
606 if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
607 if (mb.mb_threaded) {
608 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
609 if (mp->m_flag & MFLAGGED)
610 break;
611 } else {
612 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
613 if (mp->m_flag & MFLAGGED)
614 break;
618 if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
619 if (mb.mb_threaded) {
620 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
621 if (!(mp->m_flag & (MREAD | avoid)))
622 break;
623 } else {
624 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
625 if (!(mp->m_flag & (MREAD | avoid)))
626 break;
630 if (nmail &&
631 (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
632 mdot = (int)PTR2SIZE(mp - message + 1);
633 else if (ok_blook(showlast)) {
634 if (mb.mb_threaded) {
635 for (mp = this_in_thread(threadroot, -1); mp;
636 mp = prev_in_thread(mp))
637 if (!(mp->m_flag & avoid))
638 break;
639 mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
640 } else {
641 for (mp = message + msgCount - 1; mp >= message; --mp)
642 if (!(mp->m_flag & avoid))
643 break;
644 mdot = (mp >= message) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
646 } else if (!nmail &&
647 (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
648 mdot = (int)PTR2SIZE(mp - message + 1);
649 else if (mb.mb_threaded) {
650 for (mp = threadroot; mp; mp = next_in_thread(mp))
651 if (!(mp->m_flag & avoid))
652 break;
653 mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : 1;
654 } else {
655 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
656 if (!(mp->m_flag & avoid))
657 break;
658 mdot = PTRCMP(mp, <, message + msgCount)
659 ? (int)PTR2SIZE(mp - message + 1) : 1;
661 jleave:
662 NYD_LEAVE;
663 return mdot;
666 FL void
667 initbox(char const *name)
669 char *tempMesg;
670 NYD_ENTER;
672 if (mb.mb_type != MB_VOID)
673 n_strscpy(prevfile, mailname, PATH_MAX);
675 /* TODO name always NE mailname (but goes away for objects anyway)
676 * TODO Well, not true no more except that in parens */
677 _update_mailname((name != mailname) ? name : NULL);
679 if ((mb.mb_otf = Ftmp(&tempMesg, "tmpmbox", OF_WRONLY | OF_HOLDSIGS)) ==
680 NULL) {
681 n_perr(_("temporary mail message file"), 0);
682 exit(n_EXIT_ERR);
684 if ((mb.mb_itf = safe_fopen(tempMesg, "r", NULL)) == NULL) {
685 n_perr(_("temporary mail message file"), 0);
686 exit(n_EXIT_ERR);
688 Ftmp_release(&tempMesg);
690 message_reset();
691 mb.mb_threaded = 0;
692 if (mb.mb_sorted != NULL) {
693 free(mb.mb_sorted);
694 mb.mb_sorted = NULL;
696 dot = prevdot = threadroot = NULL;
697 n_pstate &= ~n_PS_DID_PRINT_DOT;
698 NYD_LEAVE;
701 FL char const *
702 folder_query(void){
703 struct n_string s, *sp = &s;
704 char *cp;
705 char const *rv;
706 bool_t err;
707 NYD_ENTER;
709 sp = n_string_creat_auto(sp);
711 /* *folder* is linked with *folder_resolved*: we only use the latter */
712 for(err = FAL0;;){
713 if((rv = ok_vlook(folder_resolved)) != NULL)
714 break;
716 /* POSIX says:
717 * If directory does not start with a <slash> ('/'), the contents
718 * of HOME shall be prefixed to it.
719 * And:
720 * If folder is unset or set to null, [.] filenames beginning with
721 * '+' shall refer to files in the current directory.
722 * We may have the result already */
723 rv = n_empty;
724 err = FAL0;
726 if((cp = ok_vlook(folder)) == NULL)
727 goto jset;
729 /* Expand the *folder*; skip %: prefix for simplicity of use */
730 if(cp[0] == '%' && cp[1] == ':')
731 cp += 2;
732 if((err = (cp = fexpand(cp, FEXP_NSPECIAL | FEXP_NFOLDER | FEXP_NSHELL)
733 ) == NULL) || *cp == '\0')
734 goto jset;
736 switch(which_protocol(cp)){
737 case PROTO_POP3:
738 n_err(_("*folder* can't be set to a flat, read-only POP3 account\n"));
739 err = TRU1;
740 goto jset;
741 default:
742 /* Further expansion desired */
743 break;
746 /* Prefix HOME as necessary */
747 if(*cp != '/'){ /* XXX path_is_absolute() */
748 size_t l1, l2;
749 char const *home;
751 home = ok_vlook(HOME);
752 l1 = strlen(home);
753 l2 = strlen(cp);
755 sp = n_string_reserve(sp, l1 + 1 + l2 +1);
756 sp = n_string_push_buf(sp, home, l1);
757 sp = n_string_push_c(sp, '/');
758 sp = n_string_push_buf(sp, cp, l2);
759 cp = n_string_cp(sp);
760 sp = n_string_drop_ownership(sp);
763 rv = cp;
765 /* TODO Since our visual mailname is resolved via realpath(3) if available
766 * TODO to avoid that we loose track of our currently open folder in case
767 * TODO we chdir away, but still checks the leading path portion against
768 * TODO folder_query() to be able to abbreviate to the +FOLDER syntax if
769 * TODO possible, we need to realpath(3) the folder, too */
770 #ifdef HAVE_REALPATH
771 assert(sp->s_len == 0 && sp->s_dat == NULL);
772 # ifndef HAVE_REALPATH_NULL
773 sp = n_string_reserve(sp, PATH_MAX +1);
774 # endif
776 if((sp->s_dat = realpath(cp, sp->s_dat)) != NULL){
777 # ifdef HAVE_REALPATH_NULL
778 n_string_cp(sp = n_string_assign_cp(sp, cp = sp->s_dat));
779 (free)(cp);
780 # endif
781 rv = sp->s_dat;
782 }else if(n_err_no == n_ERR_NOENT)
783 rv = cp;
784 else{
785 n_err(_("Can't canonicalize *folder*: %s\n"),
786 n_shexp_quote_cp(cp, FAL0));
787 err = TRU1;
788 rv = n_empty;
790 sp = n_string_drop_ownership(sp);
791 #endif /* HAVE_REALPATH */
793 /* Always append a solidus to our result path upon success */
794 if(!err){
795 size_t i;
797 if(rv[(i = strlen(rv)) - 1] != '/'){
798 sp = n_string_reserve(sp, i + 1 +1);
799 sp = n_string_push_buf(sp, rv, i);
800 sp = n_string_push_c(sp, '/');
801 rv = n_string_cp(sp);
802 sp = n_string_drop_ownership(sp);
806 jset:
807 n_PS_ROOT_BLOCK(ok_vset(folder_resolved, rv));
810 if(err){
811 n_err(_("*folder* is not resolvable, using CWD\n"));
812 assert(rv != NULL && *rv == '\0');
814 NYD_LEAVE;
815 return rv;
818 /* s-it-mode */