1 /* message-property.cc - Properties are like tags, but (key,value) pairs.
2  * keys are allowed to repeat.
3  *
4  * This file is part of notmuch.
5  *
6  * Copyright © 2016 David Bremner
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see https://www.gnu.org/licenses/ .
20  *
21  * Author: David Bremner <david@tethera.net>
22  */
23 
24 #include "notmuch-private.h"
25 #include "database-private.h"
26 #include "message-private.h"
27 
28 notmuch_status_t
notmuch_message_get_property(notmuch_message_t * message,const char * key,const char ** value)29 notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value)
30 {
31     if (! value)
32 	return NOTMUCH_STATUS_NULL_POINTER;
33 
34     *value = _notmuch_string_map_get (_notmuch_message_property_map (message), key);
35 
36     return NOTMUCH_STATUS_SUCCESS;
37 }
38 
39 notmuch_status_t
notmuch_message_count_properties(notmuch_message_t * message,const char * key,unsigned int * count)40 notmuch_message_count_properties (notmuch_message_t *message, const char *key, unsigned int *count)
41 {
42     if (! count || ! key || ! message)
43 	return NOTMUCH_STATUS_NULL_POINTER;
44 
45     notmuch_string_map_t *map;
46 
47     map = _notmuch_message_property_map (message);
48     if (! map)
49 	return NOTMUCH_STATUS_NULL_POINTER;
50 
51     notmuch_string_map_iterator_t *matcher = _notmuch_string_map_iterator_create (map, key, true);
52 
53     if (! matcher)
54 	return NOTMUCH_STATUS_OUT_OF_MEMORY;
55 
56     *count = 0;
57     while (_notmuch_string_map_iterator_valid (matcher)) {
58 	(*count)++;
59 	_notmuch_string_map_iterator_move_to_next (matcher);
60     }
61 
62     _notmuch_string_map_iterator_destroy (matcher);
63     return NOTMUCH_STATUS_SUCCESS;
64 }
65 
66 static notmuch_status_t
_notmuch_message_modify_property(notmuch_message_t * message,const char * key,const char * value,bool delete_it)67 _notmuch_message_modify_property (notmuch_message_t *message, const char *key, const char *value,
68 				  bool delete_it)
69 {
70     notmuch_private_status_t private_status;
71     notmuch_status_t status;
72     char *term = NULL;
73 
74     status = _notmuch_database_ensure_writable (notmuch_message_get_database (message));
75     if (status)
76 	return status;
77 
78     if (key == NULL || value == NULL)
79 	return NOTMUCH_STATUS_NULL_POINTER;
80 
81     if (strchr (key, '='))
82 	return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
83 
84     term = talloc_asprintf (message, "%s=%s", key, value);
85 
86     if (delete_it)
87 	private_status = _notmuch_message_remove_term (message, "property", term);
88     else
89 	private_status = _notmuch_message_add_term (message, "property", term);
90 
91     if (private_status)
92 	return COERCE_STATUS (private_status,
93 			      "Unhandled error modifying message property");
94     if (! _notmuch_message_frozen (message))
95 	_notmuch_message_sync (message);
96 
97     if (term)
98 	talloc_free (term);
99 
100     return NOTMUCH_STATUS_SUCCESS;
101 }
102 
103 notmuch_status_t
notmuch_message_add_property(notmuch_message_t * message,const char * key,const char * value)104 notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value)
105 {
106     return _notmuch_message_modify_property (message, key, value, false);
107 }
108 
109 notmuch_status_t
notmuch_message_remove_property(notmuch_message_t * message,const char * key,const char * value)110 notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value)
111 {
112     return _notmuch_message_modify_property (message, key, value, true);
113 }
114 
115 static
116 notmuch_status_t
_notmuch_message_remove_all_properties(notmuch_message_t * message,const char * key,bool prefix)117 _notmuch_message_remove_all_properties (notmuch_message_t *message, const char *key, bool prefix)
118 {
119     notmuch_status_t status;
120     const char *term_prefix;
121 
122     status = _notmuch_database_ensure_writable (notmuch_message_get_database (message));
123     if (status)
124 	return status;
125 
126     _notmuch_message_invalidate_metadata (message, "property");
127     if (key)
128 	term_prefix = talloc_asprintf (message, "%s%s%s", _find_prefix ("property"), key,
129 				       prefix ? "" : "=");
130     else
131 	term_prefix = _find_prefix ("property");
132 
133     /* XXX better error reporting ? */
134     _notmuch_message_remove_terms (message, term_prefix);
135 
136     return NOTMUCH_STATUS_SUCCESS;
137 }
138 
139 notmuch_status_t
notmuch_message_remove_all_properties(notmuch_message_t * message,const char * key)140 notmuch_message_remove_all_properties (notmuch_message_t *message, const char *key)
141 {
142     return _notmuch_message_remove_all_properties (message, key, false);
143 }
144 
145 notmuch_status_t
notmuch_message_remove_all_properties_with_prefix(notmuch_message_t * message,const char * prefix)146 notmuch_message_remove_all_properties_with_prefix (notmuch_message_t *message, const char *prefix)
147 {
148     return _notmuch_message_remove_all_properties (message, prefix, true);
149 }
150 
151 notmuch_message_properties_t *
notmuch_message_get_properties(notmuch_message_t * message,const char * key,notmuch_bool_t exact)152 notmuch_message_get_properties (notmuch_message_t *message, const char *key, notmuch_bool_t exact)
153 {
154     notmuch_string_map_t *map;
155 
156     map = _notmuch_message_property_map (message);
157     return _notmuch_string_map_iterator_create (map, key, exact);
158 }
159 
160 notmuch_bool_t
notmuch_message_properties_valid(notmuch_message_properties_t * properties)161 notmuch_message_properties_valid (notmuch_message_properties_t *properties)
162 {
163     return _notmuch_string_map_iterator_valid (properties);
164 }
165 
166 void
notmuch_message_properties_move_to_next(notmuch_message_properties_t * properties)167 notmuch_message_properties_move_to_next (notmuch_message_properties_t *properties)
168 {
169     return _notmuch_string_map_iterator_move_to_next (properties);
170 }
171 
172 const char *
notmuch_message_properties_key(notmuch_message_properties_t * properties)173 notmuch_message_properties_key (notmuch_message_properties_t *properties)
174 {
175     return _notmuch_string_map_iterator_key (properties);
176 }
177 
178 const char *
notmuch_message_properties_value(notmuch_message_properties_t * properties)179 notmuch_message_properties_value (notmuch_message_properties_t *properties)
180 {
181     return _notmuch_string_map_iterator_value (properties);
182 }
183 
184 void
notmuch_message_properties_destroy(notmuch_message_properties_t * properties)185 notmuch_message_properties_destroy (notmuch_message_properties_t *properties)
186 {
187     _notmuch_string_map_iterator_destroy (properties);
188 }
189