From: Richard Russon Date: Thu, 23 Aug 2018 11:23:32 +0000 (+0100) Subject: add MxOps:path_probe() X-Git-Tag: 2019-10-25~682^2~16 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=01b33d1d167e5bf5e463f33035e3edcea3d6211e;p=neomutt add MxOps:path_probe() --- diff --git a/compress.c b/compress.c index b2ecff4f9..6f9c302a7 100644 --- a/compress.c +++ b/compress.c @@ -892,6 +892,23 @@ int mutt_comp_valid_command(const char *cmd) return strstr(cmd, "%f") && strstr(cmd, "%t"); } +/** + * comp_path_probe - Is this a compressed mailbox? - Implements MxOps::path_probe + */ +int comp_path_probe(const char *path, const struct stat *st) +{ + if (!path) + return MUTT_UNKNOWN; + + if (!st || !S_ISREG(st->st_mode)) + return MUTT_UNKNOWN; + + if (mutt_comp_can_read(path)) + return MUTT_COMPRESSED; + + return MUTT_UNKNOWN; +} + // clang-format off /** * struct mx_comp_ops - Mailbox callback functions for compressed mailboxes @@ -913,5 +930,6 @@ struct MxOps mx_comp_ops = { .msg_close = comp_msg_close, .tags_edit = NULL, .tags_commit = NULL, + .path_probe = comp_path_probe, }; // clang-format on diff --git a/imap/imap.c b/imap/imap.c index 4c921b050..ce98e12a4 100644 --- a/imap/imap.c +++ b/imap/imap.c @@ -2719,6 +2719,21 @@ static int imap_tags_commit(struct Context *ctx, struct Header *hdr, char *buf) return 0; } +/** + * imap_path_probe - Is this an IMAP mailbox? - Implements MxOps::path_probe + */ +int imap_path_probe(const char *path, const struct stat *st) +{ + if (!path) + return MUTT_UNKNOWN; + + enum UrlScheme scheme = url_check_scheme(path); + if ((scheme == U_IMAP) || (scheme == U_IMAPS)) + return MUTT_IMAP; + + return MUTT_UNKNOWN; +} + // clang-format off /** * struct mx_imap_ops - Mailbox callback functions for IMAP mailboxes @@ -2737,5 +2752,6 @@ struct MxOps mx_imap_ops = { .msg_close = imap_msg_close, .tags_edit = imap_tags_edit, .tags_commit = imap_tags_commit, + .path_probe = imap_path_probe, }; // clang-format on diff --git a/maildir/mh.c b/maildir/mh.c index 131ae479e..0053d3f32 100644 --- a/maildir/mh.c +++ b/maildir/mh.c @@ -2868,59 +2868,87 @@ int mh_check_empty(const char *path) } /** - * mx_is_maildir - Is this a Maildir folder? - * @param path Path to examine - * @retval true If it is + * maildir_path_probe - Is this a Maildir mailbox? - Implements MxOps::path_probe */ -bool mx_is_maildir(const char *path) +int maildir_path_probe(const char *path, const struct stat *st) { - char tmp[PATH_MAX]; - struct stat st; + if (!path) + return MUTT_UNKNOWN; - snprintf(tmp, sizeof(tmp), "%s/cur", path); - if (stat(tmp, &st) == 0 && S_ISDIR(st.st_mode)) - return true; - return false; + if (!st || !S_ISDIR(st->st_mode)) + return MUTT_UNKNOWN; + + char cur[PATH_MAX]; + snprintf(cur, sizeof(cur), "%s/cur", path); + + struct stat stc; + if ((stat(cur, &stc) == 0) && S_ISDIR(stc.st_mode)) + return MUTT_MAILDIR; + + return MUTT_UNKNOWN; } /** - * mx_is_mh - Is this an MH folder? - * @param path Path to examine + * mx_is_maildir - Is this a Maildir mailbox? + * @param path Path to test * @retval true If it is */ -bool mx_is_mh(const char *path) +bool mx_is_maildir(const char *path) +{ + return (maildir_path_probe(path, NULL) == MUTT_MAILDIR); +} + +/** + * mh_path_probe - Is this an mh mailbox? - Implements MxOps::path_probe + */ +int mh_path_probe(const char *path, const struct stat *st) { + if (!path) + return MUTT_UNKNOWN; + + if (!st || !S_ISDIR(st->st_mode)) + return MUTT_UNKNOWN; + char tmp[PATH_MAX]; snprintf(tmp, sizeof(tmp), "%s/.mh_sequences", path); if (access(tmp, F_OK) == 0) - return true; + return MUTT_MH; snprintf(tmp, sizeof(tmp), "%s/.xmhcache", path); if (access(tmp, F_OK) == 0) - return true; + return MUTT_MH; snprintf(tmp, sizeof(tmp), "%s/.mew_cache", path); if (access(tmp, F_OK) == 0) - return true; + return MUTT_MH; snprintf(tmp, sizeof(tmp), "%s/.mew-cache", path); if (access(tmp, F_OK) == 0) - return true; + return MUTT_MH; snprintf(tmp, sizeof(tmp), "%s/.sylpheed_cache", path); if (access(tmp, F_OK) == 0) - return true; + return MUTT_MH; /* ok, this isn't an mh folder, but mh mode can be used to read - * Usenet news from the spool. ;-) - */ + * Usenet news from the spool. */ snprintf(tmp, sizeof(tmp), "%s/.overview", path); if (access(tmp, F_OK) == 0) - return true; + return MUTT_MH; - return false; + return MUTT_UNKNOWN; +} + +/** + * mx_is_mh - Is this an mh mailbox? + * @param path Path to test + * @retval true If it is + */ +bool mx_is_mh(const char *path) +{ + return (mh_path_probe(path, NULL) == MUTT_MH); } // clang-format off @@ -2941,6 +2969,7 @@ struct MxOps mx_maildir_ops = { .msg_close = mh_msg_close, .tags_edit = NULL, .tags_commit = NULL, + .path_probe = maildir_path_probe, }; /** @@ -2960,5 +2989,6 @@ struct MxOps mx_mh_ops = { .msg_close = mh_msg_close, .tags_edit = NULL, .tags_commit = NULL, + .path_probe = mh_path_probe, }; // clang-format on diff --git a/mbox/mbox.c b/mbox/mbox.c index 2607effae..cf9729326 100644 --- a/mbox/mbox.c +++ b/mbox/mbox.c @@ -1322,6 +1322,60 @@ bail: /* Come here in case of disaster */ return rc; } +/** + * mbox_path_probe - Is this an mbox mailbox? - Implements MxOps::path_probe + */ +int mbox_path_probe(const char *path, const struct stat *st) +{ + if (!path) + return MUTT_UNKNOWN; + + if (!st || !S_ISREG(st->st_mode)) + return MUTT_UNKNOWN; + + FILE *fp = fopen(path, "r"); + if (!fp) + return MUTT_UNKNOWN; + + int ch; + while ((ch = fgetc(fp)) != EOF) + { + /* Some mailbox creation tools erroneously append a blank line to + * a file before appending a mail message. This allows neomutt to + * detect magic for and thus open those files. */ + if ((ch != '\n') && (ch != '\r')) + { + ungetc(ch, fp); + break; + } + } + + int magic = MUTT_UNKNOWN; + char tmp[STRING]; + if (fgets(tmp, sizeof(tmp), fp)) + { + if (mutt_str_strncmp("From ", tmp, 5) == 0) + magic = MUTT_MBOX; + else if (mutt_str_strcmp(MMDF_SEP, tmp) == 0) + magic = MUTT_MMDF; + } + mutt_file_fclose(&fp); + + if (!CheckMboxSize) + { + /* need to restore the times here, the file was not really accessed, + * only the type was accessed. This is important, because detection + * of "new mail" depends on those times set correctly. + */ + struct utimbuf times; + times.actime = st->st_atime; + times.modtime = st->st_mtime; + utime(path, ×); + } + + return magic; +} + // clang-format off /** * struct mx_mbox_ops - Mailbox callback functions for mbox mailboxes @@ -1340,6 +1394,7 @@ struct MxOps mx_mbox_ops = { .msg_close = mbox_msg_close, .tags_edit = NULL, .tags_commit = NULL, + .path_probe = mbox_path_probe, }; /** @@ -1359,5 +1414,6 @@ struct MxOps mx_mmdf_ops = { .msg_close = mbox_msg_close, .tags_edit = NULL, .tags_commit = NULL, + .path_probe = mbox_path_probe, }; // clang-format on diff --git a/mx.c b/mx.c index 0e4de572b..3be2051c6 100644 --- a/mx.c +++ b/mx.c @@ -1497,3 +1497,66 @@ bool mx_tags_is_supported(struct Context *ctx) { return ctx->mx_ops->tags_commit && ctx->mx_ops->tags_edit; } + +/** + * mx_path_probe - Find a mailbox that understands a path + * @param path Path to examine + * @param st stat buffer (for local filesystems) + * @retval num Type, e.g. #MUTT_IMAP + */ +int mx_path_probe(const char *path, const struct stat *st) +{ + if (!path) + return MUTT_UNKNOWN; + + static const struct MxOps *no_stat[] = { +#ifdef USE_IMAP + &mx_imap_ops, +#endif +#ifdef USE_NOTMUCH + &mx_notmuch_ops, +#endif +#ifdef USE_POP + &mx_pop_ops, +#endif +#ifdef USE_NNTP + &mx_nntp_ops, +#endif + }; + + static const struct MxOps *with_stat[] = { + &mx_maildir_ops, &mx_mbox_ops, &mx_mh_ops, &mx_mmdf_ops, +#ifdef USE_COMPRESSED + &mx_comp_ops, +#endif + }; + + int rc; + + for (size_t i = 0; i < mutt_array_size(no_stat); i++) + { + rc = no_stat[i]->path_probe(path, NULL); + if (rc != MUTT_UNKNOWN) + return rc; + } + + struct stat st2 = { 0 }; + if (!st) + { + st = &st2; + if (stat(path, &st2) != 0) + { + mutt_debug(1, "unable to stat %s: %s (errno %d).\n", path, strerror(errno), errno); + return MUTT_UNKNOWN; + } + } + + for (size_t i = 0; i < mutt_array_size(with_stat); i++) + { + rc = with_stat[i]->path_probe(path, st); + if (rc != MUTT_UNKNOWN) + return rc; + } + + return rc; +} diff --git a/mx.h b/mx.h index 5ef407807..deb6ba4a8 100644 --- a/mx.h +++ b/mx.h @@ -145,6 +145,13 @@ struct MxOps * @retval 1 Buf set */ int (*tags_commit) (struct Context *ctx, struct Header *hdr, char *buf); + /** + * path_probe - Does this mailbox type recognise this path? + * @param path Path to examine + * @param st stat buffer (for local filesystems) + * @retval num Type, e.g. #MUTT_IMAP + */ + int (*path_probe) (const char *path, const struct stat *st); }; #ifdef USE_NOTMUCH @@ -219,6 +226,7 @@ int mx_msg_commit (struct Context *ctx, struct Message *msg); int mx_msg_close (struct Context *ctx, struct Message **msg); int mx_tags_edit (struct Context *ctx, const char *tags, char *buf, size_t buflen); int mx_tags_commit (struct Context *ctx, struct Header *hdr, char *tags); +int mx_path_probe (const char *path, const struct stat *st); void mx_fastclose_mailbox(struct Context *ctx); diff --git a/nntp/nntp.c b/nntp/nntp.c index abc7f5d25..f9059ba85 100644 --- a/nntp/nntp.c +++ b/nntp/nntp.c @@ -2625,6 +2625,21 @@ int nntp_compare_order(const void *a, const void *b) return SORTCODE(result); } +/** + * nntp_path_probe - Is this an NNTP mailbox? - Implements MxOps::path_probe + */ +int nntp_path_probe(const char *path, const struct stat *st) +{ + if (!path) + return MUTT_UNKNOWN; + + enum UrlScheme scheme = url_check_scheme(path); + if ((scheme == U_NNTP) || (scheme == U_NNTPS)) + return MUTT_NNTP; + + return MUTT_UNKNOWN; +} + // clang-format off /** * struct mx_nntp_ops - Mailbox callback functions for NNTP mailboxes @@ -2643,5 +2658,6 @@ struct MxOps mx_nntp_ops = { .msg_close = nntp_msg_close, .tags_edit = NULL, .tags_commit = NULL, + .path_probe = nntp_path_probe, }; // clang-format on diff --git a/notmuch/mutt_notmuch.c b/notmuch/mutt_notmuch.c index f2ed83ecc..623aaea64 100644 --- a/notmuch/mutt_notmuch.c +++ b/notmuch/mutt_notmuch.c @@ -2760,6 +2760,21 @@ static int nm_msg_commit(struct Context *ctx, struct Message *msg) return -1; } +/** + * nm_path_probe - Is this a Notmuch mailbox? - Implements MxOps::path_probe + */ +int nm_path_probe(const char *path, const struct stat *st) +{ + if (!path) + return MUTT_UNKNOWN; + + enum UrlScheme scheme = url_check_scheme(path); + if (scheme == U_NOTMUCH) + return MUTT_NOTMUCH; + + return MUTT_UNKNOWN; +} + // clang-format off /** * struct mx_notmuch_ops - Mailbox callback functions for Notmuch mailboxes @@ -2778,5 +2793,6 @@ struct MxOps mx_notmuch_ops = { .msg_close = nm_msg_close, .tags_edit = nm_tags_edit, .tags_commit = nm_tags_commit, + .path_probe = nm_path_probe, }; // clang-format on diff --git a/pop/pop.c b/pop/pop.c index e3a80dfd2..9e9959c29 100644 --- a/pop/pop.c +++ b/pop/pop.c @@ -1056,6 +1056,21 @@ fail: FREE(&pop_data); } +/** + * pop_path_probe - Is this a POP mailbox? - Implements MxOps::path_probe + */ +int pop_path_probe(const char *path, const struct stat *st) +{ + if (!path) + return MUTT_UNKNOWN; + + enum UrlScheme scheme = url_check_scheme(path); + if ((scheme == U_POP) || (scheme == U_POPS)) + return MUTT_POP; + + return MUTT_UNKNOWN; +} + // clang-format off /** * mx_pop_ops - Mailbox callback functions for POP mailboxes @@ -1074,5 +1089,6 @@ struct MxOps mx_pop_ops = { .msg_close = pop_msg_close, .tags_edit = NULL, .tags_commit = NULL, + .path_probe = pop_path_probe, }; // clang-format on