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
.msg_close = comp_msg_close,
.tags_edit = NULL,
.tags_commit = NULL,
+ .path_probe = comp_path_probe,
};
// clang-format on
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
.msg_close = imap_msg_close,
.tags_edit = imap_tags_edit,
.tags_commit = imap_tags_commit,
+ .path_probe = imap_path_probe,
};
// clang-format on
}
/**
- * 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
.msg_close = mh_msg_close,
.tags_edit = NULL,
.tags_commit = NULL,
+ .path_probe = maildir_path_probe,
};
/**
.msg_close = mh_msg_close,
.tags_edit = NULL,
.tags_commit = NULL,
+ .path_probe = mh_path_probe,
};
// clang-format on
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
.msg_close = mbox_msg_close,
.tags_edit = NULL,
.tags_commit = NULL,
+ .path_probe = mbox_path_probe,
};
/**
.msg_close = mbox_msg_close,
.tags_edit = NULL,
.tags_commit = NULL,
+ .path_probe = mbox_path_probe,
};
// clang-format on
{
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;
+}
* @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
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);
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
.msg_close = nntp_msg_close,
.tags_edit = NULL,
.tags_commit = NULL,
+ .path_probe = nntp_path_probe,
};
// clang-format on
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
.msg_close = nm_msg_close,
.tags_edit = nm_tags_edit,
.tags_commit = nm_tags_commit,
+ .path_probe = nm_path_probe,
};
// clang-format on
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
.msg_close = pop_msg_close,
.tags_edit = NULL,
.tags_commit = NULL,
+ .path_probe = pop_path_probe,
};
// clang-format on