make-config.in: install: ignore chown error..
[s-mailx.git] / folder.c
blobf78dbba4843f2289769fb353e61b9256e897c545
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 goto jdocopy;
75 }else
76 jdocopy:
77 #endif
78 n_strscpy(mailname, name, sizeof(mailname));
81 mailp = mailname;
82 dispp = displayname;
84 /* Don't display an absolute path but "+FOLDER" if under *folder* */
85 if(*(foldp = n_folder_query()) != '\0'){
86 foldlen = strlen(foldp);
87 if(strncmp(foldp, mailp, foldlen))
88 foldlen = 0;
89 }else
90 foldlen = 0;
92 /* We want to see the name of the folder .. on the screen */
93 i = strlen(mailp);
94 if(i < sizeof(displayname) - 3 -1){
95 if(foldlen > 0){
96 *dispp++ = '+';
97 *dispp++ = '[';
98 memcpy(dispp, mailp, foldlen);
99 dispp += foldlen;
100 mailp += foldlen;
101 *dispp++ = ']';
102 memcpy(dispp, mailp, i -= foldlen);
103 dispp[i] = '\0';
104 }else
105 memcpy(dispp, mailp, i +1);
106 rv = TRU1;
107 }else{
108 /* Avoid disrupting multibyte sequences (if possible) */
109 #ifndef HAVE_C90AMEND1
110 j = sizeof(displayname) / 3 - 3;
111 i -= sizeof(displayname) - (1/* + */ + 3) - j;
112 #else
113 j = field_detect_clip(sizeof(displayname) / 3, mailp, i);
114 i = j + __narrow_suffix(mailp + j, i - j,
115 sizeof(displayname) - (1/* + */ + 3 + 1) - j);
116 #endif
117 snprintf(dispp, sizeof(displayname), "%s%.*s...%s",
118 (foldlen > 0 ? "[+]" : ""), (int)j, mailp, mailp + i);
119 rv = FAL0;
122 n_PS_ROOT_BLOCK((ok_vset(mailbox_resolved, mailname),
123 ok_vset(mailbox_display, displayname)));
124 NYD_LEAVE;
125 return rv;
128 #ifdef HAVE_C90AMEND1
129 SINLINE size_t
130 __narrow_suffix(char const *cp, size_t cpl, size_t maxl)
132 int err;
133 size_t i, ok;
134 NYD_ENTER;
136 for (err = ok = i = 0; cpl > maxl || err;) {
137 int ml = mblen(cp, cpl);
138 if (ml < 0) { /* XXX _narrow_suffix(): mblen() error; action? */
139 (void)mblen(NULL, 0);
140 err = 1;
141 ml = 1;
142 } else {
143 if (!err)
144 ok = i;
145 err = 0;
146 if (ml == 0)
147 break;
149 cp += ml;
150 i += ml;
151 cpl -= ml;
153 NYD_LEAVE;
154 return ok;
156 #endif /* HAVE_C90AMEND1 */
158 static void
159 a_folder_info(void){
160 struct message *mp;
161 int u, n, d, s, hidden, moved;
162 NYD2_ENTER;
164 if(mb.mb_type == MB_VOID){
165 fprintf(n_stdout, _("(Currently no active mailbox)"));
166 goto jleave;
169 s = d = hidden = moved = 0;
170 for (mp = message, n = 0, u = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
171 if (mp->m_flag & MNEW)
172 ++n;
173 if ((mp->m_flag & MREAD) == 0)
174 ++u;
175 if ((mp->m_flag & (MDELETED | MSAVED)) == (MDELETED | MSAVED))
176 ++moved;
177 if ((mp->m_flag & (MDELETED | MSAVED)) == MDELETED)
178 ++d;
179 if ((mp->m_flag & (MDELETED | MSAVED)) == MSAVED)
180 ++s;
181 if (mp->m_flag & MHIDDEN)
182 ++hidden;
185 /* If displayname gets truncated the user effectively has no option to see
186 * the full pathname of the mailbox, so print it at least for '? fi' */
187 fprintf(n_stdout, "%s: ", n_shexp_quote_cp(
188 (_update_mailname(NULL) ? displayname : mailname), FAL0));
189 if (msgCount == 1)
190 fprintf(n_stdout, _("1 message"));
191 else
192 fprintf(n_stdout, _("%d messages"), msgCount);
193 if (n > 0)
194 fprintf(n_stdout, _(" %d new"), n);
195 if (u-n > 0)
196 fprintf(n_stdout, _(" %d unread"), u);
197 if (d > 0)
198 fprintf(n_stdout, _(" %d deleted"), d);
199 if (s > 0)
200 fprintf(n_stdout, _(" %d saved"), s);
201 if (moved > 0)
202 fprintf(n_stdout, _(" %d moved"), moved);
203 if (hidden > 0)
204 fprintf(n_stdout, _(" %d hidden"), hidden);
205 if (mb.mb_perm == 0)
206 fprintf(n_stdout, _(" [Read-only]"));
207 #ifdef HAVE_IMAP
208 if (mb.mb_type == MB_CACHE)
209 fprintf(n_stdout, _(" [Disconnected]"));
210 #endif
212 jleave:
213 putc('\n', n_stdout);
214 NYD2_LEAVE;
217 FL int
218 setfile(char const *name, enum fedit_mode fm) /* TODO oh my god */
220 /* TODO This all needs to be converted to URL:: and Mailbox:: */
221 static int shudclob;
223 struct stat stb;
224 size_t offset;
225 char const *who, *orig_name;
226 int rv, omsgCount = 0;
227 FILE *ibuf = NULL, *lckfp = NULL;
228 bool_t isdevnull = FAL0;
229 NYD_ENTER;
231 n_pstate &= ~n_PS_SETFILE_OPENED;
233 /* C99 */{
234 enum fexp_mode fexpm;
236 if((who = shortcut_expand(name)) != NULL){
237 fexpm = FEXP_NSHORTCUT/* XXX | FEXP_NSHELL*/;
238 name = who;
239 }else
240 fexpm = FEXP_FULL/* XXX FEXP_NSHELL*/;
242 if(name[0] == '%'){
243 char const *cp;
245 fm |= FEDIT_SYSBOX; /* TODO fexpand() needs to tell is-valid-user! */
246 if(*(who = &name[1]) == ':')
247 ++who;
248 if((cp = strrchr(who, '/')) != NULL)
249 who = &cp[1];
250 if(*who == '\0')
251 goto jlogname;
252 }else
253 jlogname:
254 who = ok_vlook(LOGNAME);
256 if ((name = fexpand(name, fexpm)) == NULL)
257 goto jem1;
260 /* For at least substdate() users TODO -> eventloop tick */
261 time_current_update(&time_current, FAL0);
263 switch (which_protocol(orig_name = name, TRU1, TRU1, &name)) {
264 case PROTO_FILE:
265 isdevnull = ((n_poption & n_PO_BATCH_FLAG) &&
266 !strcmp(name, n_path_devnull));
267 #ifdef HAVE_REALPATH
268 do { /* TODO we need objects, goes away then */
269 # ifdef HAVE_REALPATH_NULL
270 char *cp;
272 if ((cp = realpath(name, NULL)) != NULL) {
273 name = savestr(cp);
274 (free)(cp);
276 # else
277 char cbuf[PATH_MAX];
279 if (realpath(name, cbuf) != NULL)
280 name = savestr(cbuf);
281 # endif
282 } while (0);
283 #endif
284 rv = 1;
285 break;
286 case PROTO_MAILDIR:
287 shudclob = 1;
288 rv = maildir_setfile(name, fm);
289 goto jleave;
290 #ifdef HAVE_POP3
291 case PROTO_POP3:
292 shudclob = 1;
293 rv = pop3_setfile(orig_name, fm);
294 goto jleave;
295 #endif
296 #ifdef HAVE_IMAP
297 case PROTO_IMAP:
298 shudclob = 1;
299 if((fm & FEDIT_NEWMAIL) && mb.mb_type == MB_CACHE)
300 rv = 1;
301 else
302 rv = imap_setfile(orig_name, fm);
303 goto jleave;
304 #endif
305 default:
306 n_err(_("Cannot handle protocol: %s\n"), orig_name);
307 goto jem1;
310 if ((ibuf = n_fopen_any(savecat("file://", name), "r", NULL)) == NULL) {
311 int e = n_err_no;
313 if ((fm & FEDIT_SYSBOX) && e == n_ERR_NOENT) {
314 if (strcmp(who, ok_vlook(LOGNAME)) && getpwnam(who) == NULL) {
315 n_err(_("%s is not a user of this system\n"),
316 n_shexp_quote_cp(who, FAL0));
317 goto jem2;
319 if (!(n_poption & n_PO_QUICKRUN_MASK) && ok_blook(bsdcompat))
320 n_err(_("No mail for %s\n"), who);
322 if (fm & FEDIT_NEWMAIL)
323 goto jleave;
325 mb.mb_type = MB_VOID;
326 if (ok_blook(emptystart)) {
327 if (!(n_poption & n_PO_QUICKRUN_MASK) && !ok_blook(bsdcompat))
328 n_perr(name, e);
329 /* We must avoid returning -1 and causing program exit */
330 rv = 1;
331 goto jleave;
333 n_perr(name, e);
334 goto jem1;
337 if (fstat(fileno(ibuf), &stb) == -1) {
338 if (fm & FEDIT_NEWMAIL)
339 goto jleave;
340 n_perr(_("fstat"), 0);
341 goto jem1;
344 if (S_ISREG(stb.st_mode) || isdevnull) {
345 /* EMPTY */
346 } else {
347 if (fm & FEDIT_NEWMAIL)
348 goto jleave;
349 n_err_no = S_ISDIR(stb.st_mode) ? n_ERR_ISDIR : n_ERR_INVAL;
350 n_perr(name, 0);
351 goto jem1;
354 if (shudclob && !(fm & FEDIT_NEWMAIL) && !quit(FAL0))
355 goto jem2;
357 hold_sigs();
359 #ifdef HAVE_SOCKETS
360 if (!(fm & FEDIT_NEWMAIL) && mb.mb_sock.s_fd >= 0)
361 sclose(&mb.mb_sock); /* TODO sorry? VMAILFS->close(), thank you */
362 #endif
364 /* TODO There is no intermediate VOID box we've switched to: name may
365 * TODO point to the same box that we just have written, so any updates
366 * TODO we won't see! Reopen again in this case. RACY! Goes with VOID! */
367 /* TODO In addition: in case of compressed/hook boxes we lock a temporary! */
368 /* TODO We may uselessly open twice but quit() doesn't say whether we were
369 * TODO modified so we can't tell: Mailbox::is_modified() :-(( */
370 if (/*shudclob && !(fm & FEDIT_NEWMAIL) &&*/ !strcmp(name, mailname)) {
371 name = mailname;
372 Fclose(ibuf);
374 if ((ibuf = n_fopen_any(name, "r", NULL)) == NULL ||
375 fstat(fileno(ibuf), &stb) == -1 ||
376 (!S_ISREG(stb.st_mode) && !isdevnull)) {
377 n_perr(name, 0);
378 rele_sigs();
379 goto jem2;
383 /* Copy the messages into /tmp and set pointers */
384 if (!(fm & FEDIT_NEWMAIL)) {
385 if (isdevnull) {
386 mb.mb_type = MB_VOID;
387 mb.mb_perm = 0;
388 } else {
389 mb.mb_type = MB_FILE;
390 mb.mb_perm = (((n_poption & n_PO_R_FLAG) || (fm & FEDIT_RDONLY) ||
391 access(name, W_OK) < 0) ? 0 : MB_DELE | MB_EDIT);
392 if (shudclob) {
393 if (mb.mb_itf) {
394 fclose(mb.mb_itf);
395 mb.mb_itf = NULL;
397 if (mb.mb_otf) {
398 fclose(mb.mb_otf);
399 mb.mb_otf = NULL;
403 shudclob = 1;
404 if (fm & FEDIT_SYSBOX)
405 n_pstate &= ~n_PS_EDIT;
406 else
407 n_pstate |= n_PS_EDIT;
408 initbox(name);
409 offset = 0;
410 } else {
411 fseek(mb.mb_otf, 0L, SEEK_END);
412 /* TODO Doing this without holding a lock is.. And not err checking.. */
413 fseek(ibuf, mailsize, SEEK_SET);
414 offset = mailsize;
415 omsgCount = msgCount;
418 if (isdevnull)
419 lckfp = (FILE*)-1;
420 else if (!(n_pstate & n_PS_EDIT))
421 lckfp = n_dotlock(name, fileno(ibuf), FLT_READ, offset,0,
422 (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX));
423 else if (n_file_lock(fileno(ibuf), FLT_READ, offset,0,
424 (fm & FEDIT_NEWMAIL ? 0 : UIZ_MAX)))
425 lckfp = (FILE*)-1;
427 if (lckfp == NULL) {
428 if (!(fm & FEDIT_NEWMAIL)) {
429 char const *emsg = (n_pstate & n_PS_EDIT)
430 ? N_("Unable to lock mailbox, aborting operation")
431 : N_("Unable to (dot) lock mailbox, aborting operation");
432 n_perr(V_(emsg), 0);
434 rele_sigs();
435 if (!(fm & FEDIT_NEWMAIL))
436 goto jem1;
437 goto jleave;
440 mailsize = fsize(ibuf);
442 /* TODO This is too simple minded? We should regenerate an index file
443 * TODO to be able to truly tell whether *anything* has changed! */
444 if ((fm & FEDIT_NEWMAIL) && UICMP(z, mailsize, <=, offset)) {
445 rele_sigs();
446 goto jleave;
448 setptr(ibuf, offset);
449 setmsize(msgCount);
450 if ((fm & FEDIT_NEWMAIL) && mb.mb_sorted) {
451 mb.mb_threaded = 0;
452 c_sort((void*)-1);
455 Fclose(ibuf);
456 ibuf = NULL;
457 if (lckfp != NULL && lckfp != (FILE*)-1) {
458 Pclose(lckfp, FAL0);
459 /*lckfp = NULL;*/
462 if (!(fm & FEDIT_NEWMAIL)) {
463 n_pstate &= ~n_PS_SAW_COMMAND;
464 n_pstate |= n_PS_SETFILE_OPENED;
467 rele_sigs();
469 rv = (msgCount == 0);
470 if(rv)
471 n_err_no = n_ERR_NODATA;
473 if(n_poption & n_PO_EXISTONLY)
474 goto jleave;
476 if(rv){
477 if(!(n_pstate & n_PS_EDIT) || (fm & FEDIT_NEWMAIL)){
478 if(!(fm & FEDIT_NEWMAIL)){
479 if (!ok_blook(emptystart))
480 n_err(_("No mail for %s\n"), who);
482 goto jleave;
486 if(fm & FEDIT_NEWMAIL)
487 newmailinfo(omsgCount);
488 jleave:
489 if (ibuf != NULL) {
490 Fclose(ibuf);
491 if (lckfp != NULL && lckfp != (FILE*)-1)
492 Pclose(lckfp, FAL0);
494 NYD_LEAVE;
495 return rv;
496 jem2:
497 mb.mb_type = MB_VOID;
498 jem1:
499 n_err_no = n_ERR_NOTOBACCO;
500 rv = -1;
501 goto jleave;
504 FL int
505 newmailinfo(int omsgCount)
507 int mdot, i;
508 NYD_ENTER;
510 for (i = 0; i < omsgCount; ++i)
511 message[i].m_flag &= ~MNEWEST;
513 if (msgCount > omsgCount) {
514 for (i = omsgCount; i < msgCount; ++i)
515 message[i].m_flag |= MNEWEST;
516 fprintf(n_stdout, _("New mail has arrived.\n"));
517 if ((i = msgCount - omsgCount) == 1)
518 fprintf(n_stdout, _("Loaded 1 new message.\n"));
519 else
520 fprintf(n_stdout, _("Loaded %d new messages.\n"), i);
521 } else
522 fprintf(n_stdout, _("Loaded %d messages.\n"), msgCount);
524 temporary_folder_hook_check(TRU1);
526 mdot = getmdot(1);
528 if (ok_blook(header))
529 print_headers(omsgCount + 1, msgCount, FAL0);
530 NYD_LEAVE;
531 return mdot;
534 FL void
535 setmsize(int sz)
537 NYD_ENTER;
538 if (n_msgvec != NULL)
539 free(n_msgvec);
540 n_msgvec = scalloc(sz + 1, sizeof *n_msgvec);
541 NYD_LEAVE;
544 FL void
545 print_header_summary(char const *Larg)
547 size_t bot, top, i, j;
548 NYD_ENTER;
550 if (Larg != NULL) {
551 /* Avoid any messages XXX add a make_mua_silent() and use it? */
552 if ((n_poption & (n_PO_VERB | n_PO_EXISTONLY)) == n_PO_EXISTONLY) {
553 n_stdout = freopen(n_path_devnull, "w", stdout);
554 n_stderr = freopen(n_path_devnull, "w", stderr);
556 assert(n_msgvec != NULL);
557 i = (getmsglist(/*TODO make const */n_UNCONST(Larg), n_msgvec, 0) <= 0);
558 if (n_poption & n_PO_EXISTONLY) {
559 n_exit_status = (int)i;
560 goto jleave;
562 if (i)
563 goto jleave;
564 for (bot = msgCount, top = 0, i = 0; (j = n_msgvec[i]) != 0; ++i) {
565 if (bot > j)
566 bot = j;
567 if (top < j)
568 top = j;
570 } else
571 bot = 1, top = msgCount;
572 print_headers(bot, top, (Larg != NULL)); /* TODO should take iterator!! */
573 jleave:
574 NYD_LEAVE;
577 FL void
578 n_folder_announce(enum n_announce_flags af){
579 int vec[2], mdot;
580 NYD_ENTER;
582 mdot = (mb.mb_type == MB_VOID) ? 1 : getmdot(0);
583 dot = &message[mdot - 1];
585 if(af != n_ANNOUNCE_NONE && ok_blook(header) &&
586 ((af & n_ANNOUNCE_MAIN_CALL) ||
587 ((af & n_ANNOUNCE_CHANGE) && !ok_blook(posix))))
588 af |= n_ANNOUNCE_STATUS | n__ANNOUNCE_HEADER;
590 if(af & n_ANNOUNCE_STATUS){
591 a_folder_info();
592 af |= n__ANNOUNCE_ANY;
595 if(af & n__ANNOUNCE_HEADER){
596 if(!(af & n_ANNOUNCE_MAIN_CALL) && ok_blook(bsdannounce))
597 n_OBSOLETE(_("*bsdannounce* is now default behaviour"));
598 vec[0] = mdot;
599 vec[1] = 0;
600 print_header_group(vec); /* XXX errors? */
601 af |= n__ANNOUNCE_ANY;
604 if(af & n__ANNOUNCE_ANY)
605 fflush(n_stdout);
606 NYD_LEAVE;
609 FL int
610 getmdot(int nmail)
612 struct message *mp;
613 char *cp;
614 int mdot;
615 enum mflag avoid = MHIDDEN | MDELETED;
616 NYD_ENTER;
618 if (!nmail) {
619 if (ok_blook(autothread)) {
620 n_OBSOLETE(_("please use *autosort=thread* instead of *autothread*"));
621 c_thread(NULL);
622 } else if ((cp = ok_vlook(autosort)) != NULL) {
623 if (mb.mb_sorted != NULL)
624 free(mb.mb_sorted);
625 mb.mb_sorted = sstrdup(cp);
626 c_sort(NULL);
629 if (mb.mb_type == MB_VOID) {
630 mdot = 1;
631 goto jleave;
634 if (nmail)
635 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
636 if ((mp->m_flag & (MNEWEST | avoid)) == MNEWEST)
637 break;
639 if (!nmail || PTRCMP(mp, >=, message + msgCount)) {
640 if (mb.mb_threaded) {
641 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
642 if ((mp->m_flag & (MNEW | avoid)) == MNEW)
643 break;
644 } else {
645 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
646 if ((mp->m_flag & (MNEW | avoid)) == MNEW)
647 break;
651 if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
652 if (mb.mb_threaded) {
653 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
654 if (mp->m_flag & MFLAGGED)
655 break;
656 } else {
657 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
658 if (mp->m_flag & MFLAGGED)
659 break;
663 if ((mb.mb_threaded ? (mp == NULL) : PTRCMP(mp, >=, message + msgCount))) {
664 if (mb.mb_threaded) {
665 for (mp = threadroot; mp != NULL; mp = next_in_thread(mp))
666 if (!(mp->m_flag & (MREAD | avoid)))
667 break;
668 } else {
669 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
670 if (!(mp->m_flag & (MREAD | avoid)))
671 break;
675 if (nmail &&
676 (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
677 mdot = (int)PTR2SIZE(mp - message + 1);
678 else if (ok_blook(showlast)) {
679 if (mb.mb_threaded) {
680 for (mp = this_in_thread(threadroot, -1); mp;
681 mp = prev_in_thread(mp))
682 if (!(mp->m_flag & avoid))
683 break;
684 mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
685 } else {
686 for (mp = message + msgCount - 1; mp >= message; --mp)
687 if (!(mp->m_flag & avoid))
688 break;
689 mdot = (mp >= message) ? (int)PTR2SIZE(mp - message + 1) : msgCount;
691 } else if (!nmail &&
692 (mb.mb_threaded ? (mp != NULL) : PTRCMP(mp, <, message + msgCount)))
693 mdot = (int)PTR2SIZE(mp - message + 1);
694 else if (mb.mb_threaded) {
695 for (mp = threadroot; mp; mp = next_in_thread(mp))
696 if (!(mp->m_flag & avoid))
697 break;
698 mdot = (mp != NULL) ? (int)PTR2SIZE(mp - message + 1) : 1;
699 } else {
700 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
701 if (!(mp->m_flag & avoid))
702 break;
703 mdot = PTRCMP(mp, <, message + msgCount)
704 ? (int)PTR2SIZE(mp - message + 1) : 1;
706 jleave:
707 NYD_LEAVE;
708 return mdot;
711 FL void
712 initbox(char const *name)
714 char *tempMesg;
715 NYD_ENTER;
717 if (mb.mb_type != MB_VOID)
718 n_strscpy(prevfile, mailname, PATH_MAX);
720 /* TODO name always NE mailname (but goes away for objects anyway)
721 * TODO Well, not true no more except that in parens */
722 _update_mailname((name != mailname) ? name : NULL);
724 if ((mb.mb_otf = Ftmp(&tempMesg, "tmpmbox", OF_WRONLY | OF_HOLDSIGS)) ==
725 NULL) {
726 n_perr(_("temporary mail message file"), 0);
727 exit(n_EXIT_ERR);
729 if ((mb.mb_itf = safe_fopen(tempMesg, "r", NULL)) == NULL) {
730 n_perr(_("temporary mail message file"), 0);
731 exit(n_EXIT_ERR);
733 Ftmp_release(&tempMesg);
735 message_reset();
736 mb.mb_active = MB_NONE;
737 mb.mb_threaded = 0;
738 #ifdef HAVE_IMAP
739 mb.mb_flags = MB_NOFLAGS;
740 #endif
741 if (mb.mb_sorted != NULL) {
742 free(mb.mb_sorted);
743 mb.mb_sorted = NULL;
745 dot = prevdot = threadroot = NULL;
746 n_pstate &= ~n_PS_DID_PRINT_DOT;
747 NYD_LEAVE;
750 FL char const *
751 n_folder_query(void){
752 struct n_string s, *sp = &s;
753 enum protocol proto;
754 char *cp;
755 char const *rv, *adjcp;
756 bool_t err;
757 NYD_ENTER;
759 sp = n_string_creat_auto(sp);
761 /* *folder* is linked with *folder_resolved*: we only use the latter */
762 for(err = FAL0;;){
763 if((rv = ok_vlook(folder_resolved)) != NULL)
764 break;
766 /* POSIX says:
767 * If directory does not start with a <slash> ('/'), the contents
768 * of HOME shall be prefixed to it.
769 * And:
770 * If folder is unset or set to null, [.] filenames beginning with
771 * '+' shall refer to files in the current directory.
772 * We may have the result already */
773 rv = n_empty;
774 err = FAL0;
776 if((cp = ok_vlook(folder)) == NULL)
777 goto jset;
779 /* Expand the *folder*; skip %: prefix for simplicity of use */
780 if(cp[0] == '%' && cp[1] == ':')
781 cp += 2;
782 if((err = (cp = fexpand(cp, FEXP_NSPECIAL | FEXP_NFOLDER | FEXP_NSHELL)
783 ) == NULL) || *cp == '\0')
784 goto jset;
785 else{
786 size_t i;
788 for(i = strlen(cp);;){
789 if(--i == 0)
790 goto jset;
791 if(cp[i] != '/'){
792 cp[++i] = '\0';
793 break;
798 switch((proto = which_protocol(cp, FAL0, FAL0, &adjcp))){
799 case PROTO_POP3:
800 n_err(_("*folder* can't be set to a flat, read-only POP3 account\n"));
801 err = TRU1;
802 goto jset;
803 case PROTO_IMAP:
804 #ifdef HAVE_IMAP
805 rv = cp;
806 if(!strcmp(rv, protbase(rv)))
807 rv = savecatsep(rv, '/', n_empty);
808 #else
809 n_err(_("*folder*: IMAP support not compiled in\n"));
810 err = TRU1;
811 #endif
812 goto jset;
813 default:
814 /* Further expansion desired */
815 break;
818 /* Prefix HOME as necessary */
819 if(*adjcp != '/'){ /* XXX path_is_absolute() */
820 size_t l1, l2;
821 char const *home;
823 home = ok_vlook(HOME);
824 l1 = strlen(home);
825 l2 = strlen(cp);
827 sp = n_string_reserve(sp, l1 + 1 + l2 +1);
828 if(cp != adjcp){
829 size_t i;
831 sp = n_string_push_buf(sp, cp, i = PTR2SIZE(adjcp - cp));
832 cp += i;
833 l2 -= i;
835 sp = n_string_push_buf(sp, home, l1);
836 sp = n_string_push_c(sp, '/');
837 sp = n_string_push_buf(sp, cp, l2);
838 cp = n_string_cp(sp);
839 sp = n_string_drop_ownership(sp);
842 /* TODO Since our visual mailname is resolved via realpath(3) if available
843 * TODO to avoid that we loose track of our currently open folder in case
844 * TODO we chdir away, but still checks the leading path portion against
845 * TODO n_folder_query() to be able to abbreviate to the +FOLDER syntax if
846 * TODO possible, we need to realpath(3) the folder, too */
847 #ifndef HAVE_REALPATH
848 rv = cp;
849 #else
850 assert(sp->s_len == 0 && sp->s_dat == NULL);
851 # ifndef HAVE_REALPATH_NULL
852 sp = n_string_reserve(sp, PATH_MAX +1);
853 # endif
855 if((sp->s_dat = realpath(cp, sp->s_dat)) != NULL){
856 # ifdef HAVE_REALPATH_NULL
857 n_string_cp(sp = n_string_assign_cp(sp, cp = sp->s_dat));
858 (free)(cp);
859 # endif
860 rv = sp->s_dat;
861 }else if(n_err_no == n_ERR_NOENT)
862 rv = cp;
863 else{
864 n_err(_("Can't canonicalize *folder*: %s\n"),
865 n_shexp_quote_cp(cp, FAL0));
866 err = TRU1;
867 rv = n_empty;
869 sp = n_string_drop_ownership(sp);
870 #endif /* HAVE_REALPATH */
872 /* Always append a solidus to our result path upon success */
873 if(!err){
874 size_t i;
876 if(rv[(i = strlen(rv)) - 1] != '/'){
877 sp = n_string_reserve(sp, i + 1 +1);
878 sp = n_string_push_buf(sp, rv, i);
879 sp = n_string_push_c(sp, '/');
880 rv = n_string_cp(sp);
881 sp = n_string_drop_ownership(sp);
885 jset:
886 n_PS_ROOT_BLOCK(ok_vset(folder_resolved, rv));
889 if(err){
890 n_err(_("*folder* is not resolvable, using CWD\n"));
891 assert(rv != NULL && *rv == '\0');
893 NYD_LEAVE;
894 return rv;
897 FL int
898 n_folder_mbox_prepare_append(FILE *fout, struct stat *st_or_null){
899 /* TODO n_folder_mbox_prepare_append -> Mailbox->append() */
900 struct stat stb;
901 char buf[2];
902 bool_t needsep;
903 int rv;
904 NYD2_ENTER;
906 if(fseek(fout, -2L, SEEK_END)){
907 rv = n_err_no;
909 if(st_or_null == NULL){
910 st_or_null = &stb;
911 if(fstat(fileno(fout), st_or_null))
912 goto jerrno;
915 if(st_or_null->st_size >= 2)
916 goto jleave;
917 if(st_or_null->st_size == 0){
918 rv = n_ERR_NONE;
919 goto jleave;
921 if(fseek(fout, -1L, SEEK_END))
922 goto jerrno;
925 rv = n_ERR_NONE;
927 needsep = FAL0;
928 switch(fread(buf, sizeof *buf, 2, fout)){
929 case 2:
930 if(buf[1] != '\n')
931 needsep = TRU1;
932 break;
933 case 1:
934 if(buf[0] != '\n')
935 needsep = TRU1;
936 break;
937 default:
938 if(ferror(fout))
939 goto jerrno;
940 break;
943 if(fflush(fout) || (needsep && putc('\n', fout) == EOF))
944 jerrno:
945 rv = n_err_no;
946 jleave:
947 NYD2_LEAVE;
948 return rv;
951 /* s-it-mode */