]> granicus.if.org Git - neomutt/commitdiff
add MxOps:path_probe()
authorRichard Russon <rich@flatcap.org>
Thu, 23 Aug 2018 11:23:32 +0000 (12:23 +0100)
committerRichard Russon <rich@flatcap.org>
Thu, 23 Aug 2018 11:23:32 +0000 (12:23 +0100)
compress.c
imap/imap.c
maildir/mh.c
mbox/mbox.c
mx.c
mx.h
nntp/nntp.c
notmuch/mutt_notmuch.c
pop/pop.c

index b2ecff4f937f6918a33c1ee13266102ebf53a26c..6f9c302a7cf5d19e580ba651b7892b99b2c4ecdf 100644 (file)
@@ -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
index 4c921b0507f13defa689a658205ff455e255f170..ce98e12a4e5894378b8b24ed535bacf642357232 100644 (file)
@@ -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
index 131ae479e2ea97e3b46c57f7a2df6f0e3192fb24..0053d3f329adca5d0daf262ff58c0e7e7156a1d3 100644 (file)
@@ -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
index 2607effaeff46f9d3683b16dd533202e49d552f5..cf9729326fc09416144d14177d0b98e1c41b7621 100644 (file)
@@ -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, &times);
+  }
+
+  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 0e4de572bbe5f532825799e00a4e6a9a0ec749a1..3be2051c6559531797279b7d1aa61d5f36d97fad 100644 (file)
--- 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 5ef40780711d9cc257b23232a7e8c970067cf315..deb6ba4a897a3e854ff71abe955ecc08c0b99961 100644 (file)
--- 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);
 
index abc7f5d25526292e9cce656b426984fb87f3cde4..f9059ba85868bb96bf3679d404514751e8ad072f 100644 (file)
@@ -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
index f2ed83ecc6d6cc6e5bca69b9917f1d8a0500c153..623aaea648acbc6e2c92a7fdcec958d9da4713ab 100644 (file)
@@ -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
index e3a80dfd203a007a453509f5092f2a4493376567..9e9959c29a590673574a673c67a5f05dbcbe5bdb 100644 (file)
--- 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