NeoMutt  2023-03-22
Teaching an old dog new tricks
DOXYGEN
lib.h File Reference

Maildir local mailbox type. More...

#include <stdbool.h>
#include <stdio.h>
#include "core/lib.h"
+ Include dependency graph for lib.h:
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

int maildir_check_empty (const char *path)
 Is the mailbox empty. More...
 
void maildir_gen_flags (char *dest, size_t destlen, struct Email *e)
 Generate the Maildir flags for an email. More...
 
bool maildir_msg_open_new (struct Mailbox *m, struct Message *msg, const struct Email *e)
 Open a new message in a Mailbox - Implements MxOps::msg_open_new() -. More...
 
FILE * maildir_open_find_message (const char *folder, const char *msg, char **newname)
 Find a message by name. More...
 
void maildir_parse_flags (struct Email *e, const char *path)
 Parse Maildir file flags. More...
 
struct Emailmaildir_parse_message (enum MailboxType type, const char *fname, bool is_old, struct Email *e)
 Actually parse a maildir message. More...
 
struct Emailmaildir_parse_stream (enum MailboxType type, FILE *fp, const char *fname, bool is_old, struct Email *e)
 Parse a Maildir message. More...
 
bool maildir_sync_mailbox_message (struct Mailbox *m, int msgno, struct HeaderCache *hc)
 Save changes to the mailbox. More...
 
bool maildir_update_flags (struct Mailbox *m, struct Email *e_old, struct Email *e_new)
 Update the mailbox flags. More...
 
int mh_check_empty (const char *path)
 Is mailbox empty. More...
 
int mh_sync_mailbox_message (struct Mailbox *m, int msgno, struct HeaderCache *hc)
 Save changes to the mailbox. More...
 

Variables

struct MxOps MxMaildirOps
 Maildir Mailbox - Implements MxOps -. More...
 
struct MxOps MxMhOps
 MH Mailbox - Implements MxOps -. More...
 

Detailed Description

Maildir local mailbox type.

Authors
  • Richard Russon

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file lib.h.

Function Documentation

◆ maildir_check_empty()

int maildir_check_empty ( const char *  path)

Is the mailbox empty.

Parameters
pathMailbox to check
Return values
1Mailbox is empty
0Mailbox contains mail
-1Error

Definition at line 1043 of file maildir.c.

1044{
1045 DIR *dir = NULL;
1046 struct dirent *de = NULL;
1047 int rc = 1; /* assume empty until we find a message */
1048 char realpath[PATH_MAX] = { 0 };
1049 int iter = 0;
1050
1051 /* Strategy here is to look for any file not beginning with a period */
1052
1053 do
1054 {
1055 /* we do "cur" on the first iteration since it's more likely that we'll
1056 * find old messages without having to scan both subdirs */
1057 snprintf(realpath, sizeof(realpath), "%s/%s", path, (iter == 0) ? "cur" : "new");
1058 dir = mutt_file_opendir(realpath, MUTT_OPENDIR_CREATE);
1059 if (!dir)
1060 return -1;
1061 while ((de = readdir(dir)))
1062 {
1063 if (*de->d_name != '.')
1064 {
1065 rc = 0;
1066 break;
1067 }
1068 }
1069 closedir(dir);
1070 iter++;
1071 } while (rc && iter < 2);
1072
1073 return rc;
1074}
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition: file.c:614
@ MUTT_OPENDIR_CREATE
Create the directory if it doesn't exist.
Definition: file.h:74
#define PATH_MAX
Definition: mutt.h:41
+ Here is the call graph for this function:

◆ maildir_gen_flags()

void maildir_gen_flags ( char *  dest,
size_t  destlen,
struct Email e 
)

Generate the Maildir flags for an email.

Parameters
destBuffer for the result
destlenLength of buffer
eEmail

Definition at line 186 of file maildir.c.

187{
188 *dest = '\0';
189
190 const char *flags = NULL;
191
193 if (edata)
194 flags = edata->maildir_flags;
195
196 /* The maildir specification requires that all files in the cur
197 * subdirectory have the :unique string appended, regardless of whether
198 * or not there are any flags. If .old is set, we know that this message
199 * will end up in the cur directory, so we include it in the following
200 * test even though there is no associated flag. */
201
202 if (e->flagged || e->replied || e->read || e->deleted || e->old || flags)
203 {
204 char tmp[1024] = { 0 };
205 snprintf(tmp, sizeof(tmp), "%s%s%s%s%s", e->flagged ? "F" : "", e->replied ? "R" : "",
206 e->read ? "S" : "", e->deleted ? "T" : "", NONULL(flags));
207 if (flags)
208 qsort(tmp, strlen(tmp), 1, ch_compare);
209 snprintf(dest, destlen, ":2,%s", tmp);
210 }
211}
struct MaildirEmailData * maildir_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:64
static int ch_compare(const void *a, const void *b)
qsort() callback to sort characters
Definition: maildir.c:175
#define NONULL(x)
Definition: string2.h:37
bool read
Email is read.
Definition: email.h:48
void * edata
Driver-specific data.
Definition: email.h:72
bool old
Email is seen, but unread.
Definition: email.h:47
bool flagged
Marked important?
Definition: email.h:45
bool replied
Email has been replied to.
Definition: email.h:49
bool deleted
Email is deleted.
Definition: email.h:76
Maildir-specific Email data -.
Definition: edata.h:34
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_open_find_message()

FILE * maildir_open_find_message ( const char *  folder,
const char *  msg,
char **  newname 
)

Find a message by name.

Parameters
[in]folderMaildir path
[in]msgEmail path
[out]newnameNew name, if it has moved
Return values
ptrFile handle

Definition at line 996 of file maildir.c.

997{
998 static unsigned int new_hits = 0, cur_hits = 0; /* simple dynamic optimization */
999
1000 struct Buffer *unique = mutt_buffer_pool_get();
1001 maildir_canon_filename(unique, msg);
1002
1003 FILE *fp = maildir_open_find_message_dir(folder, mutt_buffer_string(unique),
1004 (new_hits > cur_hits) ? "new" : "cur", newname);
1005 if (fp || (errno != ENOENT))
1006 {
1007 if ((new_hits < UINT_MAX) && (cur_hits < UINT_MAX))
1008 {
1009 new_hits += ((new_hits > cur_hits) ? 1 : 0);
1010 cur_hits += ((new_hits > cur_hits) ? 0 : 1);
1011 }
1012
1013 goto cleanup;
1014 }
1016 (new_hits > cur_hits) ? "cur" : "new", newname);
1017 if (fp || (errno != ENOENT))
1018 {
1019 if ((new_hits < UINT_MAX) && (cur_hits < UINT_MAX))
1020 {
1021 new_hits += ((new_hits > cur_hits) ? 0 : 1);
1022 cur_hits += ((new_hits > cur_hits) ? 1 : 0);
1023 }
1024
1025 goto cleanup;
1026 }
1027
1028 fp = NULL;
1029
1030cleanup:
1031 mutt_buffer_pool_release(&unique);
1032
1033 return fp;
1034}
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:78
static void maildir_canon_filename(struct Buffer *dest, const char *src)
Generate the canonical filename for a Maildir folder.
Definition: maildir.c:726
static FILE * maildir_open_find_message_dir(const char *folder, const char *unique, const char *subfolder, char **newname)
Find a message in a maildir folder.
Definition: maildir.c:756
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
String manipulation buffer.
Definition: buffer.h:34
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_parse_flags()

void maildir_parse_flags ( struct Email e,
const char *  path 
)

Parse Maildir file flags.

Parameters
eEmail
pathPath to email file

Definition at line 810 of file maildir.c.

811{
812 char *q = NULL;
813
814 e->flagged = false;
815 e->read = false;
816 e->replied = false;
817
819
820 char *p = strrchr(path, ':');
821 if (p && mutt_str_startswith(p + 1, "2,"))
822 {
823 p += 3;
824
825 mutt_str_replace(&edata->maildir_flags, p);
826 q = edata->maildir_flags;
827
828 while (*p)
829 {
830 switch (*p)
831 {
832 case 'F':
833 e->flagged = true;
834 break;
835
836 case 'R': /* replied */
837 e->replied = true;
838 break;
839
840 case 'S': /* seen */
841 e->read = true;
842 break;
843
844 case 'T': /* trashed */
845 {
846 const bool c_flag_safe = cs_subset_bool(NeoMutt->sub, "flag_safe");
847 if (!e->flagged || !c_flag_safe)
848 {
849 e->trash = true;
850 e->deleted = true;
851 }
852 break;
853 }
854
855 default:
856 *q++ = *p;
857 break;
858 }
859 p++;
860 }
861 }
862
863 if (q == edata->maildir_flags)
864 FREE(&edata->maildir_flags);
865 else if (q)
866 *q = '\0';
867}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define FREE(x)
Definition: memory.h:43
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
char * path
Path of Email (for local Mailboxes)
Definition: email.h:68
bool trash
Message is marked as trashed on disk (used by the maildir_trash option)
Definition: email.h:51
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_parse_message()

struct Email * maildir_parse_message ( enum MailboxType  type,
const char *  fname,
bool  is_old,
struct Email e 
)

Actually parse a maildir message.

Parameters
typeMailbox type, e.g. MUTT_MAILDIR
fnameMessage filename
is_oldtrue, if the email is old (read)
eEmail to populate (OPTIONAL)
Return values
ptrPopulated Email

This may also be used to fill out a fake header structure generated by lazy maildir parsing.

Definition at line 929 of file maildir.c.

931{
932 FILE *fp = fopen(fname, "r");
933 if (!fp)
934 return NULL;
935
936 struct Email *e_res = maildir_parse_stream(type, fp, fname, is_old, e);
937 mutt_file_fclose(&fp);
938 return e_res;
939}
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:151
struct Email * maildir_parse_stream(enum MailboxType type, FILE *fp, const char *fname, bool is_old, struct Email *e)
Parse a Maildir message.
Definition: maildir.c:882
The envelope/body of an email.
Definition: email.h:37
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_parse_stream()

struct Email * maildir_parse_stream ( enum MailboxType  type,
FILE *  fp,
const char *  fname,
bool  is_old,
struct Email e 
)

Parse a Maildir message.

Parameters
typeMailbox type, e.g. MUTT_MAILDIR
fpMessage file handle
fnameMessage filename
is_oldtrue, if the email is old (read)
eEmail
Return values
ptrPopulated Email
NULLon error

Actually parse a maildir message. This may also be used to fill out a fake header structure generated by lazy maildir parsing.

Definition at line 882 of file maildir.c.

884{
885 const long size = mutt_file_get_size_fp(fp);
886 if (size == 0)
887 {
888 return NULL;
889 }
890
891 if (!e)
892 {
893 e = email_new();
896 }
897 e->env = mutt_rfc822_read_header(fp, e, false, false);
898
899 if (e->received == 0)
900 e->received = e->date_sent;
901
902 /* always update the length since we have fresh information available. */
903 e->body->length = size - e->body->offset;
904
905 e->index = -1;
906
907 if (type == MUTT_MAILDIR)
908 {
909 /* maildir stores its flags in the filename, so ignore the
910 * flags in the header of the message */
911
912 e->old = is_old;
913 maildir_parse_flags(e, fname);
914 }
915 return e;
916}
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1585
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition: mailbox.h:48
void maildir_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:38
struct MaildirEmailData * maildir_edata_new(void)
Create a new MaildirEmailData object.
Definition: edata.c:53
void maildir_parse_flags(struct Email *e, const char *path)
Parse Maildir file flags.
Definition: maildir.c:810
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1157
LOFF_T offset
offset where the actual data begins
Definition: body.h:52
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
struct Envelope * env
Envelope information.
Definition: email.h:66
struct Body * body
List of MIME parts.
Definition: email.h:67
void(* edata_free)(void **ptr)
Free the private data attached to the Email.
Definition: email.h:86
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:58
int index
The absolute (unsorted) message number.
Definition: email.h:109
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:59
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_sync_mailbox_message()

bool maildir_sync_mailbox_message ( struct Mailbox m,
int  msgno,
struct HeaderCache hc 
)

Save changes to the mailbox.

Parameters
mMailbox
msgnoIndex number
hcHeader cache handle
Return values
trueSuccess
falseError

Definition at line 949 of file maildir.c.

950{
951 struct Email *e = m->emails[msgno];
952 if (!e)
953 return false;
954
955 const bool c_maildir_trash = cs_subset_bool(NeoMutt->sub, "maildir_trash");
956 if (e->deleted && !c_maildir_trash)
957 {
958 char path[PATH_MAX] = { 0 };
959 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
960#ifdef USE_HCACHE
961 if (hc)
962 {
963 const char *key = e->path + 3;
964 size_t keylen = maildir_hcache_keylen(key);
965 mutt_hcache_delete_record(hc, key, keylen);
966 }
967#endif
968 unlink(path);
969 }
970 else if (e->changed || e->attach_del ||
971 ((c_maildir_trash || e->trash) && (e->deleted != e->trash)))
972 {
973 if (maildir_sync_message(m, msgno) == -1)
974 return false;
975 }
976
977#ifdef USE_HCACHE
978 if (hc && e->changed)
979 {
980 const char *key = e->path + 3;
981 size_t keylen = maildir_hcache_keylen(key);
982 mutt_hcache_store(hc, key, keylen, e, 0);
983 }
984#endif
985
986 return true;
987}
int mutt_hcache_store(struct HeaderCache *hc, const char *key, size_t keylen, struct Email *e, uint32_t uidvalidity)
Multiplexor for StoreOps::store.
Definition: hcache.c:610
int mutt_hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:689
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:209
static size_t maildir_hcache_keylen(const char *fn)
Calculate the length of the Maildir path.
Definition: maildir.c:585
static int maildir_sync_message(struct Mailbox *m, int msgno)
Sync an email to a Maildir folder.
Definition: maildir.c:387
bool changed
Email has been edited.
Definition: email.h:75
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:98
int msgno
Number displayed to the user.
Definition: email.h:110
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ maildir_update_flags()

bool maildir_update_flags ( struct Mailbox m,
struct Email e_old,
struct Email e_new 
)

Update the mailbox flags.

Parameters
mMailbox
e_oldOld Email
e_newNew Email
Return values
trueThe flags changed
falseOtherwise

Definition at line 122 of file shared.c.

123{
124 if (!m)
125 return false;
126
127 /* save the global state here so we can reset it at the
128 * end of list block if required. */
129 bool context_changed = m->changed;
130
131 /* user didn't modify this message. alter the flags to match the
132 * current state on disk. This may not actually do
133 * anything. mutt_set_flag() will just ignore the call if the status
134 * bits are already properly set, but it is still faster not to pass
135 * through it */
136 if (e_old->flagged != e_new->flagged)
137 mutt_set_flag(m, e_old, MUTT_FLAG, e_new->flagged);
138 if (e_old->replied != e_new->replied)
139 mutt_set_flag(m, e_old, MUTT_REPLIED, e_new->replied);
140 if (e_old->read != e_new->read)
141 mutt_set_flag(m, e_old, MUTT_READ, e_new->read);
142 if (e_old->old != e_new->old)
143 mutt_set_flag(m, e_old, MUTT_OLD, e_new->old);
144
145 /* mutt_set_flag() will set this, but we don't need to
146 * sync the changes we made because we just updated the
147 * context to match the current on-disk state of the
148 * message. */
149 bool header_changed = e_old->changed;
150 e_old->changed = false;
151
152 /* if the mailbox was not modified before we made these
153 * changes, unset the changed flag since nothing needs to
154 * be synchronized. */
155 if (!context_changed)
156 m->changed = false;
157
158 return header_changed;
159}
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:81
@ MUTT_OLD
Old messages.
Definition: mutt.h:79
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:87
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:80
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:63
bool changed
Mailbox has been modified.
Definition: mailbox.h:110
+ Here is the caller graph for this function:

◆ mh_check_empty()

int mh_check_empty ( const char *  path)

Is mailbox empty.

Parameters
pathMailbox to check
Return values
1Mailbox is empty
0Mailbox contains mail
-1Error

Definition at line 166 of file mh.c.

167{
168 struct dirent *de = NULL;
169 int rc = 1; /* assume empty until we find a message */
170
171 DIR *dir = mutt_file_opendir(path, MUTT_OPENDIR_NONE);
172 if (!dir)
173 return -1;
174 while ((de = readdir(dir)))
175 {
176 if (mh_valid_message(de->d_name))
177 {
178 rc = 0;
179 break;
180 }
181 }
182 closedir(dir);
183
184 return rc;
185}
@ MUTT_OPENDIR_NONE
Plain opendir()
Definition: file.h:73
static bool mh_valid_message(const char *s)
Is this a valid MH message filename.
Definition: mh.c:149
+ Here is the call graph for this function:

◆ mh_sync_mailbox_message()

int mh_sync_mailbox_message ( struct Mailbox m,
int  msgno,
struct HeaderCache hc 
)

Save changes to the mailbox.

Parameters
mMailbox
msgnoIndex number
hcHeader cache handle
Return values
0Success
-1Error

Definition at line 757 of file mh.c.

758{
759 if (!m || !m->emails || (msgno >= m->msg_count))
760 return -1;
761
762 struct Email *e = m->emails[msgno];
763 if (!e)
764 return -1;
765
766 if (e->deleted)
767 {
768 char path[PATH_MAX] = { 0 };
769 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
770 const bool c_mh_purge = cs_subset_bool(NeoMutt->sub, "mh_purge");
771 if (c_mh_purge)
772 {
773#ifdef USE_HCACHE
774 if (hc)
775 {
776 const char *key = e->path;
777 size_t keylen = strlen(key);
778 mutt_hcache_delete_record(hc, key, keylen);
779 }
780#endif
781 unlink(path);
782 }
783 else
784 {
785 /* MH just moves files out of the way when you delete them */
786 if (*e->path != ',')
787 {
788 char tmp[PATH_MAX] = { 0 };
789 snprintf(tmp, sizeof(tmp), "%s/,%s", mailbox_path(m), e->path);
790 unlink(tmp);
791 if (rename(path, tmp) != 0)
792 {
793 return -1;
794 }
795 }
796 }
797 }
798 else if (e->changed || e->attach_del)
799 {
800 if (mh_sync_message(m, msgno) == -1)
801 return -1;
802 }
803
804#ifdef USE_HCACHE
805 if (hc && e->changed)
806 {
807 const char *key = e->path;
808 size_t keylen = strlen(key);
809 mutt_hcache_store(hc, key, keylen, e, 0);
810 }
811#endif
812
813 return 0;
814}
static int mh_sync_message(struct Mailbox *m, int msgno)
Sync an email to an MH folder.
Definition: mh.c:451
int msg_count
Total number of messages.
Definition: mailbox.h:88
+ Here is the call graph for this function:
+ Here is the caller graph for this function: