1 /**
2  * @file
3  * Representation of an email
4  *
5  * @authors
6  * Copyright (C) 1996-2009,2012 Michael R. Elkins <me@mutt.org>
7  *
8  * @copyright
9  * This program is free software: you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation, either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /**
24  * @page email_email Email object
25  *
26  * Representation of an email
27  */
28 
29 #include "config.h"
30 #include <stdbool.h>
31 #include <string.h>
32 #include "mutt/lib.h"
33 #include "email.h"
34 #include "body.h"
35 #include "envelope.h"
36 #include "tags.h"
37 
38 void nm_edata_free(void **ptr);
39 
40 /**
41  * email_free - Free an Email
42  * @param[out] ptr Email to free
43  */
email_free(struct Email ** ptr)44 void email_free(struct Email **ptr)
45 {
46   if (!ptr || !*ptr)
47     return;
48 
49   struct Email *e = *ptr;
50 
51   mutt_debug(LL_NOTIFY, "NT_EMAIL_DELETE: %p\n", e);
52   struct EventEmail ev_e = { 1, &e };
53   notify_send(e->notify, NT_EMAIL, NT_EMAIL_DELETE, &ev_e);
54 
55   if (e->edata_free && e->edata)
56     e->edata_free(&e->edata);
57 
58   mutt_env_free(&e->env);
59   mutt_body_free(&e->body);
60   FREE(&e->tree);
61   FREE(&e->path);
62 #ifdef MIXMASTER
63   mutt_list_free(&e->chain);
64 #endif
65 #ifdef USE_NOTMUCH
66   nm_edata_free(&e->nm_edata);
67 #endif
68   driver_tags_free(&e->tags);
69   notify_free(&e->notify);
70 
71   FREE(ptr);
72 }
73 
74 /**
75  * email_new - Create a new Email
76  * @retval ptr Newly created Email
77  */
email_new(void)78 struct Email *email_new(void)
79 {
80   static size_t sequence = 0;
81 
82   struct Email *e = mutt_mem_calloc(1, sizeof(struct Email));
83 #ifdef MIXMASTER
84   STAILQ_INIT(&e->chain);
85 #endif
86   STAILQ_INIT(&e->tags);
87   e->visible = true;
88   e->sequence = sequence++;
89   e->notify = notify_new();
90 
91   return e;
92 }
93 
94 /**
95  * email_cmp_strict - Strictly compare message emails
96  * @param e1 First Email
97  * @param e2 Second Email
98  * @retval true Emails are strictly identical
99  */
email_cmp_strict(const struct Email * e1,const struct Email * e2)100 bool email_cmp_strict(const struct Email *e1, const struct Email *e2)
101 {
102   if (e1 && e2)
103   {
104     if ((e1->received != e2->received) || (e1->date_sent != e2->date_sent) ||
105         (e1->body->length != e2->body->length) || (e1->lines != e2->lines) ||
106         (e1->zhours != e2->zhours) || (e1->zminutes != e2->zminutes) ||
107         (e1->zoccident != e2->zoccident) || (e1->mime != e2->mime) ||
108         !mutt_env_cmp_strict(e1->env, e2->env) || !mutt_body_cmp_strict(e1->body, e2->body))
109     {
110       return false;
111     }
112     return true;
113   }
114   else
115   {
116     return (!e1 && !e2);
117   }
118 }
119 
120 /**
121  * email_size - Compute the size of an email
122  * @param e Email
123  * @retval num Size of the email, in bytes
124  */
email_size(const struct Email * e)125 size_t email_size(const struct Email *e)
126 {
127   if (!e || !e->body)
128     return 0;
129   return e->body->length + e->body->offset - e->body->hdr_offset;
130 }
131 
132 /**
133  * emaillist_clear - Drop a private list of Emails
134  * @param el EmailList to empty
135  *
136  * The Emails are not freed.
137  */
emaillist_clear(struct EmailList * el)138 void emaillist_clear(struct EmailList *el)
139 {
140   if (!el)
141     return;
142 
143   struct EmailNode *en = NULL, *tmp = NULL;
144   STAILQ_FOREACH_SAFE(en, el, entries, tmp)
145   {
146     STAILQ_REMOVE(el, en, EmailNode, entries);
147     FREE(&en);
148   }
149   STAILQ_INIT(el);
150 }
151 
152 /**
153  * emaillist_add_email - Add an Email to a list
154  * @param e  Email to add
155  * @param el EmailList to add to
156  * @retval  0 Success
157  * @retval -1 Error
158  */
emaillist_add_email(struct EmailList * el,struct Email * e)159 int emaillist_add_email(struct EmailList *el, struct Email *e)
160 {
161   if (!el || !e)
162     return -1;
163 
164   struct EmailNode *en = mutt_mem_calloc(1, sizeof(*en));
165   en->email = e;
166   STAILQ_INSERT_TAIL(el, en, entries);
167 
168   return 0;
169 }
170 
171 /**
172  * header_find - Find a header, matching on its field, in a list of headers
173  * @param hdrlist List of headers to search
174  * @param header  The header to search for
175  * @retval node   The node in the list matching the header
176  * @retval NULL   No matching header is found
177  *
178  * The header should either of the form "X-Header:" or "X-Header: value"
179  */
header_find(const struct ListHead * hdrlist,const char * header)180 struct ListNode *header_find(const struct ListHead *hdrlist, const char *header)
181 {
182   const char *key_end = strchr(header, ':');
183   if (!key_end)
184     return NULL;
185 
186   const int keylen = key_end - header + 1;
187 
188   struct ListNode *n = NULL;
189   STAILQ_FOREACH(n, hdrlist, entries)
190   {
191     if (mutt_istrn_equal(n->data, header, keylen))
192       return n;
193   }
194   return n;
195 }
196 
197 /**
198  * header_add - Add a header to a list
199  * @param hdrlist List of headers to search
200  * @param header  String to set as the header
201  * @retval node   The created header
202  */
header_add(struct ListHead * hdrlist,const char * header)203 struct ListNode *header_add(struct ListHead *hdrlist, const char *header)
204 {
205   struct ListNode *n = mutt_list_insert_tail(hdrlist, NULL);
206   n->data = mutt_str_dup(header);
207 
208   return n;
209 }
210 
211 /**
212  * header_update - Update an existing header
213  * @param hdr     The header to update
214  * @param header  String to update the header with
215  * @retval node   The updated header
216  */
header_update(struct ListNode * hdr,const char * header)217 struct ListNode *header_update(struct ListNode *hdr, const char *header)
218 {
219   FREE(&hdr->data);
220   hdr->data = mutt_str_dup(header);
221 
222   return hdr;
223 }
224 
225 /**
226  * header_set - Set a header value in a list
227  * @param hdrlist List of headers to search
228  * @param header  String to set the value of the header to
229  * @retval node   The updated or created header
230  *
231  * If a header exists with the same field, update it, otherwise add a new header.
232  */
header_set(struct ListHead * hdrlist,const char * header)233 struct ListNode *header_set(struct ListHead *hdrlist, const char *header)
234 {
235   struct ListNode *n = header_find(hdrlist, header);
236 
237   return n ? header_update(n, header) : header_add(hdrlist, header);
238 }
239 
240 /**
241  * header_free - Free and remove a header from a header list
242  * @param hdrlist List to free the header from
243  * @param target  The header to free
244  */
header_free(struct ListHead * hdrlist,struct ListNode * target)245 void header_free(struct ListHead *hdrlist, struct ListNode *target)
246 {
247   STAILQ_REMOVE(hdrlist, target, ListNode, entries);
248   FREE(&target->data);
249   FREE(&target);
250 }
251