1 Return-Path: <bremner@tethera.net>
\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 E6EAE6DE0127
\r
6 for <notmuch@notmuchmail.org>; Sun, 12 Jun 2016 18:06:25 -0700 (PDT)
\r
7 X-Virus-Scanned: Debian amavisd-new at cworth.org
\r
11 X-Spam-Status: No, score=-0.011 tagged_above=-999 required=5
\r
12 tests=[AWL=-0.000, SPF_PASS=-0.001, T_RP_MATCHES_RCVD=-0.01]
\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 BWps4Oxp_7aG for <notmuch@notmuchmail.org>;
\r
17 Sun, 12 Jun 2016 18:06:17 -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 6FC626DE0231
\r
20 for <notmuch@notmuchmail.org>; Sun, 12 Jun 2016 18:06:09 -0700 (PDT)
\r
21 Received: from remotemail by fethera.tethera.net with local (Exim 4.84)
\r
22 (envelope-from <bremner@tethera.net>)
\r
23 id 1bCGKB-0003wx-Ah; Sun, 12 Jun 2016 21:05:55 -0400
\r
24 Received: (nullmailer pid 5670 invoked by uid 1000);
\r
25 Mon, 13 Jun 2016 01:06:04 -0000
\r
26 From: David Bremner <david@tethera.net>
\r
27 To: notmuch@notmuchmail.org
\r
28 Subject: [PATCH 3/8] lib: basic message-property API
\r
29 Date: Sun, 12 Jun 2016 22:05:50 -0300
\r
30 Message-Id: <1465779955-5539-4-git-send-email-david@tethera.net>
\r
31 X-Mailer: git-send-email 2.8.1
\r
32 In-Reply-To: <1465779955-5539-1-git-send-email-david@tethera.net>
\r
33 References: <1465779955-5539-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: Mon, 13 Jun 2016 01:06:26 -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/Makefile.local | 1 +
\r
55 lib/message-private.h | 16 +++++++
\r
56 lib/message-property.cc | 101 ++++++++++++++++++++++++++++++++++++++++++
\r
57 lib/message.cc | 52 +++++++++++++++++++++-
\r
58 lib/notmuch.h | 69 +++++++++++++++++++++++++++++
\r
59 test/T610-message-property.sh | 84 +++++++++++++++++++++++++++++++++++
\r
60 6 files changed, 321 insertions(+), 2 deletions(-)
\r
61 create mode 100644 lib/message-private.h
\r
62 create mode 100644 lib/message-property.cc
\r
63 create mode 100755 test/T610-message-property.sh
\r
65 diff --git a/lib/Makefile.local b/lib/Makefile.local
\r
66 index 9280880..c012ed1 100644
\r
67 --- a/lib/Makefile.local
\r
68 +++ b/lib/Makefile.local
\r
69 @@ -49,6 +49,7 @@ libnotmuch_cxx_srcs = \
\r
70 $(dir)/directory.cc \
\r
73 + $(dir)/message-property.cc \
\r
75 $(dir)/query-fp.cc \
\r
77 diff --git a/lib/message-private.h b/lib/message-private.h
\r
78 new file mode 100644
\r
79 index 0000000..7419925
\r
81 +++ b/lib/message-private.h
\r
83 +#ifndef MESSAGE_PRIVATE_H
\r
84 +#define MESSAGE_PRIVATE_H
\r
86 +notmuch_string_map_t *
\r
87 +_notmuch_message_property_map (notmuch_message_t *message);
\r
90 +_notmuch_message_frozen (notmuch_message_t *message);
\r
93 +_notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix);
\r
96 +_notmuch_message_invalidate_metadata (notmuch_message_t *message, const char *prefix_name);
\r
99 diff --git a/lib/message-property.cc b/lib/message-property.cc
\r
100 new file mode 100644
\r
101 index 0000000..ad2250f
\r
103 +++ b/lib/message-property.cc
\r
105 +/* message-property.cc - Properties are like tags, but (key,value) pairs.
\r
106 + * keys are allowed to repeat.
\r
108 + * This file is part of notmuch.
\r
110 + * Copyright © 2016 David Bremner
\r
112 + * This program is free software: you can redistribute it and/or modify
\r
113 + * it under the terms of the GNU General Public License as published by
\r
114 + * the Free Software Foundation, either version 3 of the License, or
\r
115 + * (at your option) any later version.
\r
117 + * This program is distributed in the hope that it will be useful,
\r
118 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
119 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
120 + * GNU General Public License for more details.
\r
122 + * You should have received a copy of the GNU General Public License
\r
123 + * along with this program. If not, see http://www.gnu.org/licenses/ .
\r
125 + * Author: David Bremner <david@tethera.net>
\r
128 +#include "notmuch-private.h"
\r
129 +#include "database-private.h"
\r
130 +#include "message-private.h"
\r
133 +notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value)
\r
136 + return NOTMUCH_STATUS_NULL_POINTER;
\r
138 + *value = _notmuch_string_map_get (_notmuch_message_property_map (message), key);
\r
140 + return NOTMUCH_STATUS_SUCCESS;
\r
143 +static notmuch_status_t
\r
144 +_notmuch_message_modify_property (notmuch_message_t *message, const char *key, const char *value,
\r
145 + notmuch_bool_t delete_it)
\r
147 + notmuch_private_status_t private_status;
\r
148 + notmuch_status_t status;
\r
149 + char *term = NULL;
\r
151 + status = _notmuch_database_ensure_writable (_notmuch_message_database (message));
\r
155 + if (key == NULL || value == NULL)
\r
156 + return NOTMUCH_STATUS_NULL_POINTER;
\r
158 + if (index (key, '='))
\r
159 + return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
\r
161 + term = talloc_asprintf (message, "%s=%s", key, value);
\r
164 + private_status = _notmuch_message_remove_term (message, "property", term);
\r
166 + private_status = _notmuch_message_add_term (message, "property", term);
\r
168 + if (private_status)
\r
169 + return COERCE_STATUS (private_status,
\r
170 + "Unhandled error modifying message property");
\r
171 + if (! _notmuch_message_frozen (message))
\r
172 + _notmuch_message_sync (message);
\r
175 + talloc_free (term);
\r
177 + return NOTMUCH_STATUS_SUCCESS;
\r
181 +notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value)
\r
183 + return _notmuch_message_modify_property (message, key, value, FALSE);
\r
187 +notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value)
\r
189 + return _notmuch_message_modify_property (message, key, value, TRUE);
\r
193 +notmuch_message_remove_all_properties (notmuch_message_t *message)
\r
195 + notmuch_status_t status;
\r
196 + status = _notmuch_database_ensure_writable (_notmuch_message_database (message));
\r
200 + _notmuch_message_invalidate_metadata (message, "property");
\r
201 + /* XXX better error reporting ? */
\r
202 + _notmuch_message_remove_terms (message, _find_prefix ("property"));
\r
204 + return NOTMUCH_STATUS_SUCCESS;
\r
206 diff --git a/lib/message.cc b/lib/message.cc
\r
207 index 63a8da5..9d3e807 100644
\r
208 --- a/lib/message.cc
\r
209 +++ b/lib/message.cc
\r
212 #include "notmuch-private.h"
\r
213 #include "database-private.h"
\r
214 +#include "message-private.h"
\r
216 #include <stdint.h>
\r
218 @@ -395,7 +396,7 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message)
\r
219 message->in_reply_to = talloc_strdup (message, "");
\r
224 _notmuch_message_invalidate_metadata (notmuch_message_t *message,
\r
225 const char *prefix_name)
\r
227 @@ -552,7 +553,7 @@ notmuch_message_get_replies (notmuch_message_t *message)
\r
228 return _notmuch_messages_create (message->replies);
\r
233 _notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix)
\r
235 Xapian::TermIterator i;
\r
236 @@ -1799,3 +1800,50 @@ _notmuch_message_database (notmuch_message_t *message)
\r
238 return message->notmuch;
\r
242 +_notmuch_message_ensure_property_map (notmuch_message_t *message)
\r
244 + notmuch_string_node_t *node;
\r
246 + if (message->property_map)
\r
249 + if (!message->property_term_list)
\r
250 + _notmuch_message_ensure_metadata (message);
\r
252 + message->property_map = _notmuch_string_map_create (message);
\r
254 + for (node = message->property_term_list->head; node; node = node->next) {
\r
258 + value = index(node->string, '=');
\r
260 + INTERNAL_ERROR ("malformed property term");
\r
264 + key = node->string;
\r
266 + _notmuch_string_map_append (message->property_map, key, value);
\r
270 + talloc_free (message->property_term_list);
\r
271 + message->property_term_list = NULL;
\r
274 +notmuch_string_map_t *
\r
275 +_notmuch_message_property_map (notmuch_message_t *message)
\r
277 + _notmuch_message_ensure_property_map (message);
\r
279 + return message->property_map;
\r
283 +_notmuch_message_frozen (notmuch_message_t *message)
\r
285 + return message->frozen;
\r
287 diff --git a/lib/notmuch.h b/lib/notmuch.h
\r
288 index d4a97cb..afc48c6 100644
\r
289 --- a/lib/notmuch.h
\r
290 +++ b/lib/notmuch.h
\r
291 @@ -180,6 +180,11 @@ typedef enum _notmuch_status {
\r
293 NOTMUCH_STATUS_PATH_ERROR,
\r
295 + * One of the arguments violates the preconditions for the
\r
296 + * function, in a way not covered by a more specific argument.
\r
298 + NOTMUCH_STATUS_ILLEGAL_ARGUMENT,
\r
300 * Not an actual status value. Just a way to find out how many
\r
301 * valid status values there are.
\r
303 @@ -1652,6 +1657,70 @@ void
\r
304 notmuch_message_destroy (notmuch_message_t *message);
\r
307 + * @name Message Properties
\r
309 + * This interface provides the ability to attach arbitrary (key,value)
\r
310 + * string pairs to a message, to remove such pairs, and to iterate
\r
311 + * over them. The caller should take some care as to what keys they
\r
312 + * add or delete values for, as other subsystems or extensions may
\r
313 + * depend on these properties.
\r
318 + * Retrieve the value for a single property key
\r
320 + * *value* is set to a string owned by the message or NULL if there is
\r
321 + * no such key. In the case of multiple values for the given key, the
\r
322 + * first one is retrieved.
\r
325 + * - NOTMUCH_STATUS_NULL_POINTER: *value* may not be NULL.
\r
326 + * - NOTMUCH_STATUS_SUCCESS: No error occured.
\r
330 +notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value);
\r
333 + * Add a (key,value) pair to a message
\r
336 + * - NOTMUCH_STATUS_ILLEGAL_ARGUMENT: *key* may not contain an '=' character.
\r
337 + * - NOTMUCH_STATUS_NULL_POINTER: Neither *key* nor *value* may be NULL.
\r
338 + * - NOTMUCH_STATUS_SUCCESS: No error occured.
\r
341 +notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value);
\r
344 + * Remove a (key,value) pair from a message.
\r
346 + * It is not an error to remove a non-existant (key,value) pair
\r
349 + * - NOTMUCH_STATUS_ILLEGAL_ARGUMENT: *key* may not contain an '=' character.
\r
350 + * - NOTMUCH_STATUS_NULL_POINTER: Neither *key* nor *value* may be NULL.
\r
351 + * - NOTMUCH_STATUS_SUCCESS: No error occured.
\r
354 +notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value);
\r
357 + * Remove all (key,value) pairs from the given message.
\r
360 + * - NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in
\r
361 + * read-only mode so message cannot be modified.
\r
362 + * - NOTMUCH_STATUS_SUCCESS: No error occured.
\r
366 +notmuch_message_remove_all_properties (notmuch_message_t *message);
\r
371 * Is the given 'tags' iterator pointing at a valid tag.
\r
373 * When this function returns TRUE, notmuch_tags_get will return a
\r
374 diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh
\r
375 new file mode 100755
\r
376 index 0000000..0217950
\r
378 +++ b/test/T610-message-property.sh
\r
380 +#!/usr/bin/env bash
\r
381 +test_description="message property API"
\r
383 +. ./test-lib.sh || exit 1
\r
387 +cat <<EOF > c_head
\r
388 +#include <stdio.h>
\r
389 +#include <string.h>
\r
390 +#include <stdlib.h>
\r
391 +#include <notmuch-test.h>
\r
393 +int main (int argc, char** argv)
\r
395 + notmuch_database_t *db;
\r
396 + notmuch_message_t *message = NULL;
\r
398 + notmuch_status_t stat;
\r
400 + EXPECT0(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db));
\r
401 + EXPECT0(notmuch_database_find_message(db, "4EFC743A.3060609@april.org", &message));
\r
402 + if (message == NULL) {
\r
403 + fprintf (stderr, "unable to find message");
\r
408 +cat <<EOF > c_tail
\r
409 + EXPECT0(notmuch_database_destroy(db));
\r
413 +test_begin_subtest "notmuch_message_{add,get,remove}_property"
\r
414 +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
\r
416 + EXPECT0(notmuch_message_add_property (message, "testkey1", "testvalue1"));
\r
417 + EXPECT0(notmuch_message_get_property (message, "testkey1", &val));
\r
418 + printf("testkey1[1] = %s\n", val);
\r
419 + EXPECT0(notmuch_message_add_property (message, "testkey2", "this value has spaces and = sign"));
\r
420 + EXPECT0(notmuch_message_get_property (message, "testkey1", &val));
\r
421 + printf("testkey1[2] = %s\n", val);
\r
422 + EXPECT0(notmuch_message_get_property (message, "testkey1", &val));
\r
424 + EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
\r
425 + printf("testkey2 = %s\n", val);
\r
427 + /* Add second value for key */
\r
428 + EXPECT0(notmuch_message_add_property (message, "testkey2", "zztestvalue3"));
\r
429 + EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
\r
430 + printf("testkey2 = %s\n", val);
\r
432 + /* remove first value for key */
\r
433 + EXPECT0(notmuch_message_remove_property (message, "testkey2", "this value has spaces and = sign"));
\r
434 + EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
\r
435 + printf("testkey2 = %s\n", val);
\r
437 + /* remove non-existant value for key */
\r
438 + EXPECT0(notmuch_message_remove_property (message, "testkey2", "this value has spaces and = sign"));
\r
439 + EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
\r
440 + printf("testkey2 = %s\n", val);
\r
442 + /* remove only value for key */
\r
443 + EXPECT0(notmuch_message_remove_property (message, "testkey2", "zztestvalue3"));
\r
444 + EXPECT0(notmuch_message_get_property (message, "testkey2", &val));
\r
445 + printf("testkey2 = %s\n", val == NULL ? "NULL" : val);
\r
448 +cat <<'EOF' >EXPECTED
\r
450 +testkey1[1] = testvalue1
\r
451 +testkey1[2] = testvalue1
\r
452 +testkey2 = this value has spaces and = sign
\r
453 +testkey2 = this value has spaces and = sign
\r
454 +testkey2 = zztestvalue3
\r
455 +testkey2 = zztestvalue3
\r
459 +test_expect_equal_file EXPECTED OUTPUT
\r