1 Return-Path: <bremner@tesseract.cs.unb.ca>
\r
2 X-Original-To: notmuch@notmuchmail.org
\r
3 Delivered-To: notmuch@notmuchmail.org
\r
4 Received: from localhost (localhost [127.0.0.1])
\r
5 by arlo.cworth.org (Postfix) with ESMTP id C86436DE035F
\r
6 for <notmuch@notmuchmail.org>; Sat, 6 Aug 2016 06:53:01 -0700 (PDT)
\r
7 X-Virus-Scanned: Debian amavisd-new at cworth.org
\r
11 X-Spam-Status: No, score=-0.004 tagged_above=-999 required=5
\r
12 tests=[AWL=-0.005, HEADER_FROM_DIFFERENT_DOMAINS=0.001]
\r
14 Received: from arlo.cworth.org ([127.0.0.1])
\r
15 by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024)
\r
16 with ESMTP id O3vFkxT0BjWZ for <notmuch@notmuchmail.org>;
\r
17 Sat, 6 Aug 2016 06:52:52 -0700 (PDT)
\r
18 Received: from fethera.tethera.net (fethera.tethera.net [198.245.60.197])
\r
19 by arlo.cworth.org (Postfix) with ESMTPS id F2EEB6DE012F
\r
20 for <notmuch@notmuchmail.org>; Sat, 6 Aug 2016 06:52:51 -0700 (PDT)
\r
21 Received: from remotemail by fethera.tethera.net with local (Exim 4.84_2)
\r
22 (envelope-from <bremner@tesseract.cs.unb.ca>)
\r
23 id 1bW22E-0007Dj-UJ; Sat, 06 Aug 2016 09:53:06 -0400
\r
24 Received: (nullmailer pid 4129 invoked by uid 1000);
\r
25 Sat, 06 Aug 2016 13:52:44 -0000
\r
26 From: David Bremner <david@tethera.net>
\r
27 To: notmuch@notmuchmail.org
\r
28 Subject: [PATCH 3/9] lib: basic message-property API
\r
29 Date: Sat, 6 Aug 2016 22:52:33 +0900
\r
30 Message-Id: <1470491559-3946-4-git-send-email-david@tethera.net>
\r
31 X-Mailer: git-send-email 2.8.1
\r
32 In-Reply-To: <1470491559-3946-1-git-send-email-david@tethera.net>
\r
33 References: <1470491559-3946-1-git-send-email-david@tethera.net>
\r
35 Content-Type: text/plain; charset=UTF-8
\r
36 Content-Transfer-Encoding: 8bit
\r
37 X-BeenThere: notmuch@notmuchmail.org
\r
38 X-Mailman-Version: 2.1.20
\r
40 List-Id: "Use and development of the notmuch mail system."
\r
41 <notmuch.notmuchmail.org>
\r
42 List-Unsubscribe: <https://notmuchmail.org/mailman/options/notmuch>,
\r
43 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
44 List-Archive: <http://notmuchmail.org/pipermail/notmuch/>
\r
45 List-Post: <mailto:notmuch@notmuchmail.org>
\r
46 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
47 List-Subscribe: <https://notmuchmail.org/mailman/listinfo/notmuch>,
\r
48 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
49 X-List-Received-Date: Sat, 06 Aug 2016 13:53:01 -0000
\r
51 Initially, support get, set and removal of single key/value pair, as
\r
52 well as removing all properties.
\r
54 lib/message-private.h | 16 +++++++
\r
55 lib/message-property.cc | 108 ++++++++++++++++++++++++++++++++++++++++++
\r
56 lib/message.cc | 52 +++++++++++++++++++-
\r
57 lib/notmuch.h | 72 ++++++++++++++++++++++++++++
\r
58 test/T610-message-property.sh | 84 ++++++++++++++++++++++++++++++++
\r
59 5 files changed, 330 insertions(+), 2 deletions(-)
\r
60 create mode 100644 lib/message-private.h
\r
61 create mode 100644 lib/message-property.cc
\r
62 create mode 100755 test/T610-message-property.sh
\r
64 diff --git a/lib/message-private.h b/lib/message-private.h
\r
65 new file mode 100644
\r
66 index 0000000..7419925
\r
68 +++ b/lib/message-private.h
\r
70 +#ifndef MESSAGE_PRIVATE_H
\r
71 +#define MESSAGE_PRIVATE_H
\r
73 +notmuch_string_map_t *
\r
74 +_notmuch_message_property_map (notmuch_message_t *message);
\r
77 +_notmuch_message_frozen (notmuch_message_t *message);
\r
80 +_notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix);
\r
83 +_notmuch_message_invalidate_metadata (notmuch_message_t *message, const char *prefix_name);
\r
86 diff --git a/lib/message-property.cc b/lib/message-property.cc
\r
87 new file mode 100644
\r
88 index 0000000..1f04a20
\r
90 +++ b/lib/message-property.cc
\r
92 +/* message-property.cc - Properties are like tags, but (key,value) pairs.
\r
93 + * keys are allowed to repeat.
\r
95 + * This file is part of notmuch.
\r
97 + * Copyright © 2016 David Bremner
\r
99 + * This program is free software: you can redistribute it and/or modify
\r
100 + * it under the terms of the GNU General Public License as published by
\r
101 + * the Free Software Foundation, either version 3 of the License, or
\r
102 + * (at your option) any later version.
\r
104 + * This program is distributed in the hope that it will be useful,
\r
105 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
106 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
107 + * GNU General Public License for more details.
\r
109 + * You should have received a copy of the GNU General Public License
\r
110 + * along with this program. If not, see http://www.gnu.org/licenses/ .
\r
112 + * Author: David Bremner <david@tethera.net>
\r
115 +#include "notmuch-private.h"
\r
116 +#include "database-private.h"
\r
117 +#include "message-private.h"
\r
120 +notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value)
\r
123 + return NOTMUCH_STATUS_NULL_POINTER;
\r
125 + *value = _notmuch_string_map_get (_notmuch_message_property_map (message), key);
\r
127 + return NOTMUCH_STATUS_SUCCESS;
\r
130 +static notmuch_status_t
\r
131 +_notmuch_message_modify_property (notmuch_message_t *message, const char *key, const char *value,
\r
132 + notmuch_bool_t delete_it)
\r
134 + notmuch_private_status_t private_status;
\r
135 + notmuch_status_t status;
\r
136 + char *term = NULL;
\r
138 + status = _notmuch_database_ensure_writable (_notmuch_message_database (message));
\r
142 + if (key == NULL || value == NULL)
\r
143 + return NOTMUCH_STATUS_NULL_POINTER;
\r
145 + if (index (key, '='))
\r
146 + return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
\r
148 + term = talloc_asprintf (message, "%s=%s", key, value);
\r
151 + private_status = _notmuch_message_remove_term (message, "property", term);
\r
153 + private_status = _notmuch_message_add_term (message, "property", term);
\r
155 + if (private_status)
\r
156 + return COERCE_STATUS (private_status,
\r
157 + "Unhandled error modifying message property");
\r
158 + if (! _notmuch_message_frozen (message))
\r
159 + _notmuch_message_sync (message);
\r
162 + talloc_free (term);
\r
164 + return NOTMUCH_STATUS_SUCCESS;
\r
168 +notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value)
\r
170 + return _notmuch_message_modify_property (message, key, value, FALSE);
\r
174 +notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value)
\r
176 + return _notmuch_message_modify_property (message, key, value, TRUE);
\r
180 +notmuch_message_remove_all_properties (notmuch_message_t *message, const char *key)
\r
182 + notmuch_status_t status;
\r
183 + const char * term_prefix;
\r
185 + status = _notmuch_database_ensure_writable (_notmuch_message_database (message));
\r
189 + _notmuch_message_invalidate_metadata (message, "property");
\r
191 + term_prefix = talloc_asprintf (message, "%s%s=", _find_prefix ("property"), key);
\r
193 + term_prefix = _find_prefix ("property");
\r
195 + /* XXX better error reporting ? */
\r
196 + _notmuch_message_remove_terms (message, term_prefix);
\r
198 + return NOTMUCH_STATUS_SUCCESS;
\r
200 diff --git a/lib/message.cc b/lib/message.cc
\r
201 index 63a8da5..9d3e807 100644
\r
202 --- a/lib/message.cc
\r
203 +++ b/lib/message.cc
\r
206 #include "notmuch-private.h"
\r
207 #include "database-private.h"
\r
208 +#include "message-private.h"
\r
210 #include <stdint.h>
\r
212 @@ -395,7 +396,7 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message)
\r
213 message->in_reply_to = talloc_strdup (message, "");
\r
218 _notmuch_message_invalidate_metadata (notmuch_message_t *message,
\r
219 const char *prefix_name)
\r
221 @@ -552,7 +553,7 @@ notmuch_message_get_replies (notmuch_message_t *message)
\r
222 return _notmuch_messages_create (message->replies);
\r
227 _notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix)
\r
229 Xapian::TermIterator i;
\r
230 @@ -1799,3 +1800,50 @@ _notmuch_message_database (notmuch_message_t *message)
\r
232 return message->notmuch;
\r
236 +_notmuch_message_ensure_property_map (notmuch_message_t *message)
\r
238 + notmuch_string_node_t *node;
\r
240 + if (message->property_map)
\r
243 + if (!message->property_term_list)
\r
244 + _notmuch_message_ensure_metadata (message);
\r
246 + message->property_map = _notmuch_string_map_create (message);
\r
248 + for (node = message->property_term_list->head; node; node = node->next) {
\r
252 + value = index(node->string, '=');
\r
254 + INTERNAL_ERROR ("malformed property term");
\r
258 + key = node->string;
\r
260 + _notmuch_string_map_append (message->property_map, key, value);
\r
264 + talloc_free (message->property_term_list);
\r
265 + message->property_term_list = NULL;
\r
268 +notmuch_string_map_t *
\r
269 +_notmuch_message_property_map (notmuch_message_t *message)
\r
271 + _notmuch_message_ensure_property_map (message);
\r
273 + return message->property_map;
\r
277 +_notmuch_message_frozen (notmuch_message_t *message)
\r
279 + return message->frozen;
\r
281 diff --git a/lib/notmuch.h b/lib/notmuch.h
\r
282 index 2faa146..b304dca 100644
\r
283 --- a/lib/notmuch.h
\r
284 +++ b/lib/notmuch.h
\r
285 @@ -180,6 +180,11 @@ typedef enum _notmuch_status {
\r
287 NOTMUCH_STATUS_PATH_ERROR,
\r
289 + * One of the arguments violates the preconditions for the
\r
290 + * function, in a way not covered by a more specific argument.
\r
292 + NOTMUCH_STATUS_ILLEGAL_ARGUMENT,
\r
294 * Not an actual status value. Just a way to find out how many
\r
295 * valid status values there are.
\r
297 @@ -1654,6 +1659,73 @@ void
\r
298 notmuch_message_destroy (notmuch_message_t *message);
\r
301 + * @name Message Properties
\r
303 + * This interface provides the ability to attach arbitrary (key,value)
\r
304 + * string pairs to a message, to remove such pairs, and to iterate
\r
305 + * over them. The caller should take some care as to what keys they
\r
306 + * add or delete values for, as other subsystems or extensions may
\r
307 + * depend on these properties.
\r
312 + * Retrieve the value for a single property key
\r
314 + * *value* is set to a string owned by the message or NULL if there is
\r
315 + * no such key. In the case of multiple values for the given key, the
\r
316 + * first one is retrieved.
\r
319 + * - NOTMUCH_STATUS_NULL_POINTER: *value* may not be NULL.
\r
320 + * - NOTMUCH_STATUS_SUCCESS: No error occured.
\r
324 +notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value);
\r
327 + * Add a (key,value) pair to a message
\r
330 + * - NOTMUCH_STATUS_ILLEGAL_ARGUMENT: *key* may not contain an '=' character.
\r
331 + * - NOTMUCH_STATUS_NULL_POINTER: Neither *key* nor *value* may be NULL.
\r
332 + * - NOTMUCH_STATUS_SUCCESS: No error occured.
\r
335 +notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value);
\r
338 + * Remove a (key,value) pair from a message.
\r
340 + * It is not an error to remove a non-existant (key,value) pair
\r
343 + * - NOTMUCH_STATUS_ILLEGAL_ARGUMENT: *key* may not contain an '=' character.
\r
344 + * - NOTMUCH_STATUS_NULL_POINTER: Neither *key* nor *value* may be NULL.
\r
345 + * - NOTMUCH_STATUS_SUCCESS: No error occured.
\r
348 +notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value);
\r
351 + * Remove all (key,value) pairs from the given message.
\r
353 + * @param[in,out] message message to operate on.
\r
354 + * @param[in] key key to delete properties for. If NULL, delete
\r
355 + * properties for all keys
\r
357 + * - NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in
\r
358 + * read-only mode so message cannot be modified.
\r
359 + * - NOTMUCH_STATUS_SUCCESS: No error occured.
\r
363 +notmuch_message_remove_all_properties (notmuch_message_t *message, const char *key);
\r
368 * Is the given 'tags' iterator pointing at a valid tag.
\r
370 * When this function returns TRUE, notmuch_tags_get will return a
\r
371 diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh
\r
372 new file mode 100755
\r
373 index 0000000..0217950
\r
375 +++ b/test/T610-message-property.sh
\r
377 +#!/usr/bin/env bash
\r
378 +test_description="message property API"
\r
380 +. ./test-lib.sh || exit 1
\r
384 +cat <<EOF > c_head
\r
385 +#include <stdio.h>
\r
386 +#include <string.h>
\r
387 +#include <stdlib.h>
\r
388 +#include <notmuch-test.h>
\r
390 +int main (int argc, char** argv)
\r
392 + notmuch_database_t *db;
\r
393 + notmuch_message_t *message = NULL;
\r
395 + notmuch_status_t stat;
\r
397 + EXPECT0(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db));
\r
398 + EXPECT0(notmuch_database_find_message(db, "4EFC743A.3060609@april.org", &message));
\r
399 + if (message == NULL) {
\r
400 + fprintf (stderr, "unable to find message");
\r
405 +cat <<EOF > c_tail
\r
406 + EXPECT0(notmuch_database_destroy(db));
\r
410 +test_begin_subtest "notmuch_message_{add,get,remove}_property"
\r
411 +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
\r
413 + EXPECT0(notmuch_message_add_property (message, "testkey1", "testvalue1"));
\r
414 + EXPECT0(notmuch_message_get_property (message, "testkey1", &val));
\r
415 + printf("testkey1[1] = %s\n", val);
\r
416 + EXPECT0(notmuch_message_add_property (message, "testkey2", "this value has spaces and = sign"));
\r
417 + EXPECT0(notmuch_message_get_property (message, "testkey1", &val));
\r
418 + printf("testkey1[2] = %s\n", val);
\r
419 + EXPECT0(notmuch_message_get_property (message, "testkey1", &val));
\r
421 + EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
\r
422 + printf("testkey2 = %s\n", val);
\r
424 + /* Add second value for key */
\r
425 + EXPECT0(notmuch_message_add_property (message, "testkey2", "zztestvalue3"));
\r
426 + EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
\r
427 + printf("testkey2 = %s\n", val);
\r
429 + /* remove first value for key */
\r
430 + EXPECT0(notmuch_message_remove_property (message, "testkey2", "this value has spaces and = sign"));
\r
431 + EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
\r
432 + printf("testkey2 = %s\n", val);
\r
434 + /* remove non-existant value for key */
\r
435 + EXPECT0(notmuch_message_remove_property (message, "testkey2", "this value has spaces and = sign"));
\r
436 + EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
\r
437 + printf("testkey2 = %s\n", val);
\r
439 + /* remove only value for key */
\r
440 + EXPECT0(notmuch_message_remove_property (message, "testkey2", "zztestvalue3"));
\r
441 + EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
\r
442 + printf("testkey2 = %s\n", val == NULL ? "NULL" : val);
\r
445 +cat <<'EOF' >EXPECTED
\r
447 +testkey1[1] = testvalue1
\r
448 +testkey1[2] = testvalue1
\r
449 +testkey2 = this value has spaces and = sign
\r
450 +testkey2 = this value has spaces and = sign
\r
451 +testkey2 = zztestvalue3
\r
452 +testkey2 = zztestvalue3
\r
456 +test_expect_equal_file EXPECTED OUTPUT
\r