1 Return-Path: <novalazy@gmail.com>
\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 olra.theworths.org (Postfix) with ESMTP id 25FF0431FAF
\r
6 for <notmuch@notmuchmail.org>; Thu, 24 Jan 2013 04:09:18 -0800 (PST)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=-0.799 tagged_above=-999 required=5
\r
12 tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1,
\r
13 FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled
\r
14 Received: from olra.theworths.org ([127.0.0.1])
\r
15 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
16 with ESMTP id TsCRlnDgBiCk for <notmuch@notmuchmail.org>;
\r
17 Thu, 24 Jan 2013 04:09:16 -0800 (PST)
\r
18 Received: from mail-pb0-f43.google.com (mail-pb0-f43.google.com
\r
19 [209.85.160.43]) (using TLSv1 with cipher RC4-SHA (128/128 bits))
\r
20 (No client certificate requested)
\r
21 by olra.theworths.org (Postfix) with ESMTPS id 06190431FAE
\r
22 for <notmuch@notmuchmail.org>; Thu, 24 Jan 2013 04:09:15 -0800 (PST)
\r
23 Received: by mail-pb0-f43.google.com with SMTP id jt11so3997106pbb.2
\r
24 for <notmuch@notmuchmail.org>; Thu, 24 Jan 2013 04:09:15 -0800 (PST)
\r
25 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113;
\r
26 h=x-received:from:to:cc:subject:date:message-id:x-mailer;
\r
27 bh=pq+bVJLMrvoa1umMAPToGStcuLLinxwPlewyyNvRcZg=;
\r
28 b=J9QAYybUCsclcIpETQaEK/WLjlVFqVnIkwRd8263Wsu2gNNJ/FpMpAC6os4fCiUwoo
\r
29 5mzxgrqvFD06ExhIQ8+BPQFTdPhPJY5Zm/n4BGIXuvsdAQEwDHci0N46octc3iUq1qi9
\r
30 FEL1LT/3htG7XNRsdIzcuzaXZ1Z8lEKGR/SNY5BWm+SM1ROkRi5jELa1zIYjrSeHZmCh
\r
31 soU2/kKhGEpw8lWsGgtwhyVUGbg1X6Mseb9/dBswlF5sVcHSK56rEiQTEQ782qKnKIxC
\r
32 LKuF49SqmLaXdFde7IyCHvFhYIz1sHf7NU5cdzBNxhrpI8dImr8RvzGoBfg5+eGXZPtg
\r
34 X-Received: by 10.68.223.35 with SMTP id qr3mr4330939pbc.27.1359029354173;
\r
35 Thu, 24 Jan 2013 04:09:14 -0800 (PST)
\r
36 Received: from localhost (215.42.233.220.static.exetel.com.au.
\r
38 by mx.google.com with ESMTPS id bj9sm2746834pab.22.2013.01.24.04.09.11
\r
39 (version=TLSv1.2 cipher=RC4-SHA bits=128/128);
\r
40 Thu, 24 Jan 2013 04:09:13 -0800 (PST)
\r
41 From: Peter Wang <novalazy@gmail.com>
\r
42 To: notmuch@notmuchmail.org
\r
43 Subject: [PATCH v4 00/12] insert command
\r
44 Date: Thu, 24 Jan 2013 23:07:56 +1100
\r
45 Message-Id: <1359029288-12132-1-git-send-email-novalazy@gmail.com>
\r
46 X-Mailer: git-send-email 1.7.12.1
\r
47 X-BeenThere: notmuch@notmuchmail.org
\r
48 X-Mailman-Version: 2.1.13
\r
50 List-Id: "Use and development of the notmuch mail system."
\r
51 <notmuch.notmuchmail.org>
\r
52 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
53 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
54 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
55 List-Post: <mailto:notmuch@notmuchmail.org>
\r
56 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
57 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
58 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
59 X-List-Received-Date: Thu, 24 Jan 2013 12:09:18 -0000
\r
61 Differences from v3:
\r
63 - squashed patches; take it up with Jani
\r
64 - address some review comments (interdiff follows)
\r
65 - some stylistic things I left for someone who cares
\r
66 (either I tried it and didn't like it, or disagree with the premise)
\r
67 - split doc and test patches so series can be partially applied
\r
68 without --folder or --create-folder options
\r
71 tag-util: move out 'tag' command-line checks
\r
72 tag-util: do not reset list in parse_tag_command_line
\r
73 cli: add insert command
\r
74 man: document 'insert' command
\r
75 man: reference notmuch-insert.1
\r
76 test: add tests for insert
\r
77 insert: add --folder option
\r
78 man: document insert --folder option
\r
79 test: test insert --folder option
\r
80 insert: add --create-folder option
\r
81 man: document insert --create-folder
\r
82 test: test insert --create-folder option
\r
84 Makefile.local | 1 +
\r
85 man/Makefile.local | 1 +
\r
86 man/man1/notmuch-config.1 | 4 +-
\r
87 man/man1/notmuch-count.1 | 4 +-
\r
88 man/man1/notmuch-dump.1 | 4 +-
\r
89 man/man1/notmuch-insert.1 | 63 ++++++
\r
90 man/man1/notmuch-new.1 | 4 +-
\r
91 man/man1/notmuch-reply.1 | 3 +-
\r
92 man/man1/notmuch-restore.1 | 3 +-
\r
93 man/man1/notmuch-search.1 | 3 +-
\r
94 man/man1/notmuch-show.1 | 3 +-
\r
95 man/man1/notmuch-tag.1 | 3 +-
\r
96 man/man1/notmuch.1 | 3 +-
\r
97 man/man5/notmuch-hooks.5 | 4 +-
\r
98 man/man7/notmuch-search-terms.7 | 3 +-
\r
99 notmuch-client.h | 3 +
\r
100 notmuch-insert.c | 484 ++++++++++++++++++++++++++++++++++++++++
\r
101 notmuch-tag.c | 10 +
\r
105 test/insert | 110 +++++++++
\r
106 test/notmuch-test | 1 +
\r
107 23 files changed, 705 insertions(+), 27 deletions(-)
\r
108 create mode 100644 man/man1/notmuch-insert.1
\r
109 create mode 100644 notmuch-insert.c
\r
110 create mode 100755 test/insert
\r
116 diff --git a/man/man1/notmuch-insert.1 b/man/man1/notmuch-insert.1
\r
117 index 4a7cbeb..8ce634e 100644
\r
118 --- a/man/man1/notmuch-insert.1
\r
119 +++ b/man/man1/notmuch-insert.1
\r
120 @@ -24,6 +24,10 @@ configuration option, then by operations specified on the command-line:
\r
121 tags prefixed by '+' are added while
\r
122 those prefixed by '\-' are removed.
\r
124 +If the new message is a duplicate of an existing message in the database
\r
125 +(it has same Message-ID), it will be added to the maildir folder and
\r
126 +notmuch database, but the tags will not be changed.
\r
128 Option arguments must appear before any tag operation arguments.
\r
129 Supported options for
\r
131 diff --git a/notmuch-insert.c b/notmuch-insert.c
\r
132 index 6b3e380..69329ad 100644
\r
133 --- a/notmuch-insert.c
\r
134 +++ b/notmuch-insert.c
\r
135 @@ -44,33 +44,22 @@ handle_sigint (unused (int sig))
\r
138 /* Like gethostname but guarantees that a null-terminated hostname is
\r
139 - * returned, even if it has to make one up.
\r
140 - * Returns true unless hostname contains a slash. */
\r
141 -static notmuch_bool_t
\r
142 + * returned, even if it has to make one up. Invalid characters are
\r
143 + * substituted such that the hostname can be used within a filename.
\r
146 safe_gethostname (char *hostname, size_t len)
\r
150 if (gethostname (hostname, len) == -1) {
\r
151 strncpy (hostname, "unknown", len);
\r
153 hostname[len - 1] = '\0';
\r
155 - return (strchr (hostname, '/') == NULL);
\r
158 -/* Check the specified folder name does not contain a directory
\r
159 - * component ".." to prevent writes outside of the Maildir hierarchy. */
\r
160 -static notmuch_bool_t
\r
161 -check_folder_name (const char *folder)
\r
163 - const char *p = folder;
\r
166 - if ((p[0] == '.') && (p[1] == '.') && (p[2] == '\0' || p[2] == '/'))
\r
168 - p = strchr (p, '/');
\r
172 + for (p = hostname; *p != '\0'; p++) {
\r
173 + if (*p == '/' || *p == ':')
\r
178 @@ -96,6 +85,23 @@ sync_dir (const char *dir)
\r
182 +/* Check the specified folder name does not contain a directory
\r
183 + * component ".." to prevent writes outside of the Maildir hierarchy. */
\r
184 +static notmuch_bool_t
\r
185 +check_folder_name (const char *folder)
\r
187 + const char *p = folder;
\r
190 + if ((p[0] == '.') && (p[1] == '.') && (p[2] == '\0' || p[2] == '/'))
\r
192 + p = strchr (p, '/');
\r
199 /* Make the given directory, succeeding if it already exists. */
\r
200 static notmuch_bool_t
\r
201 make_directory (char *path, int mode)
\r
202 @@ -206,10 +212,7 @@ maildir_open_tmp_file (void *ctx, const char *dir,
\r
204 /* We follow the Dovecot file name generation algorithm. */
\r
206 - if (! safe_gethostname (hostname, sizeof (hostname))) {
\r
207 - fprintf (stderr, "Error: invalid host name.\n");
\r
210 + safe_gethostname (hostname, sizeof (hostname));
\r
212 gettimeofday (&tv, NULL);
\r
213 filename = talloc_asprintf (ctx, "%ld.M%ldP%d.%s",
\r
214 @@ -247,26 +250,6 @@ maildir_open_tmp_file (void *ctx, const char *dir,
\r
218 -/* Atomically move the new message file from the Maildir 'tmp' directory
\r
219 - * to the 'new' directory.
\r
221 - * We follow the Dovecot recommendation to simply use rename()
\r
222 - * instead of link() and unlink(). See also:
\r
223 - * http://wiki.dovecot.org/MailboxFormat/Maildir#Mail_delivery
\r
225 -static notmuch_bool_t
\r
226 -maildir_move_tmp_to_new (const char *tmppath, const char *newpath,
\r
227 - const char *newdir)
\r
229 - if (rename (tmppath, newpath) != 0) {
\r
230 - fprintf (stderr, "Error: rename() failed: %s\n", strerror (errno));
\r
234 - /* Sync the 'new' directory after rename for durability. */
\r
235 - return sync_dir (newdir);
\r
238 /* Copy the contents of standard input (fdin) into fdout. */
\r
239 static notmuch_bool_t
\r
240 copy_stdin (int fdin, int fdout)
\r
241 @@ -291,11 +274,9 @@ copy_stdin (int fdin, int fdout)
\r
244 written = write (fdout, p, remain);
\r
245 - if (written == 0)
\r
247 - if (written < 0) {
\r
248 - if (errno == EINTR)
\r
250 + if (written < 0 && errno == EINTR)
\r
252 + if (written <= 0) {
\r
253 fprintf (stderr, "Error: writing to temporary file: %s",
\r
256 @@ -320,9 +301,7 @@ add_file_to_database (notmuch_database_t *notmuch, const char *path,
\r
257 status = notmuch_database_add_message (notmuch, path, &message);
\r
259 case NOTMUCH_STATUS_SUCCESS:
\r
261 case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:
\r
262 - fprintf (stderr, "Warning: duplicate message.\n");
\r
265 case NOTMUCH_STATUS_FILE_NOT_EMAIL:
\r
266 @@ -340,11 +319,18 @@ add_file_to_database (notmuch_database_t *notmuch, const char *path,
\r
270 - tag_op_list_apply (message, tag_ops, TAG_FLAG_MAILDIR_SYNC);
\r
271 + if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) {
\r
272 + /* Don't change tags of an existing message. */
\r
273 + status = notmuch_message_tags_to_maildir_flags (message);
\r
274 + if (status != NOTMUCH_STATUS_SUCCESS)
\r
275 + fprintf (stderr, "Error: failed to sync tags to maildir flags\n");
\r
277 + status = tag_op_list_apply (message, tag_ops, TAG_FLAG_MAILDIR_SYNC);
\r
280 notmuch_message_destroy (message);
\r
283 + return (status == NOTMUCH_STATUS_SUCCESS) ? TRUE : FALSE;
\r
286 static notmuch_bool_t
\r
287 @@ -355,29 +341,45 @@ insert_message (void *ctx, notmuch_database_t *notmuch, int fdin,
\r
291 - notmuch_bool_t ret;
\r
293 fdout = maildir_open_tmp_file (ctx, dir, &tmppath, &newpath, &newdir);
\r
297 - ret = copy_stdin (fdin, fdout);
\r
298 - if (ret && fsync (fdout) != 0) {
\r
300 + if (! copy_stdin (fdin, fdout)) {
\r
302 + unlink (tmppath);
\r
306 + if (fsync (fdout) != 0) {
\r
307 fprintf (stderr, "Error: fsync failed: %s\n", strerror (errno));
\r
310 + unlink (tmppath);
\r
316 - ret = maildir_move_tmp_to_new (tmppath, newpath, newdir);
\r
320 + /* Atomically move the new message file from the Maildir 'tmp' directory
\r
321 + * to the 'new' directory. We follow the Dovecot recommendation to
\r
322 + * simply use rename() instead of link() and unlink().
\r
323 + * See also: http://wiki.dovecot.org/MailboxFormat/Maildir#Mail_delivery
\r
325 + if (rename (tmppath, newpath) != 0) {
\r
326 + fprintf (stderr, "Error: rename() failed: %s\n", strerror (errno));
\r
331 - ret = add_file_to_database (notmuch, newpath, tag_ops);
\r
333 - /* XXX maybe there should be an option to keep the file in maildir? */
\r
334 + if (! add_file_to_database (notmuch, newpath, tag_ops)) {
\r
335 + /* XXX add an option to keep the file in maildir? */
\r
336 + unlink (newpath);
\r
340 + if (! sync_dir (newdir)) {
\r
344 @@ -398,7 +400,7 @@ notmuch_insert_command (void *ctx, int argc, char *argv[])
\r
345 char *query_string = NULL;
\r
346 const char *folder = NULL;
\r
347 notmuch_bool_t create_folder = FALSE;
\r
349 + const char *maildir;
\r
352 notmuch_bool_t ret;
\r
353 @@ -443,23 +445,23 @@ notmuch_insert_command (void *ctx, int argc, char *argv[])
\r
357 - if (folder != NULL) {
\r
358 + if (folder == NULL) {
\r
359 + maildir = db_path;
\r
361 if (! check_folder_name (folder)) {
\r
362 fprintf (stderr, "Error: bad folder name: %s\n", folder);
\r
365 maildir = talloc_asprintf (ctx, "%s/%s", db_path, folder);
\r
367 - maildir = talloc_asprintf (ctx, "%s", db_path);
\r
370 - fprintf (stderr, "Out of memory\n");
\r
373 - if (create_folder && ! maildir_create_folder (ctx, maildir)) {
\r
374 - fprintf (stderr, "Error: creating maildir %s: %s\n",
\r
375 - maildir, strerror (errno));
\r
378 + fprintf (stderr, "Out of memory\n");
\r
381 + if (create_folder && ! maildir_create_folder (ctx, maildir)) {
\r
382 + fprintf (stderr, "Error: creating maildir %s: %s\n",
\r
383 + maildir, strerror (errno));
\r
388 /* Setup our handler for SIGINT. We do not set SA_RESTART so that copying
\r
389 diff --git a/tag-util.c b/tag-util.c
\r
390 index 41f2c09..b57ee32 100644
\r
393 @@ -188,6 +188,11 @@ parse_tag_command_line (void *ctx, int argc, char **argv,
\r
395 *query_str = query_string_from_args (ctx, argc - i, &argv[i]);
\r
397 + if (*query_str == NULL) {
\r
398 + fprintf (stderr, "Out of memory.\n");
\r
399 + return TAG_PARSE_OUT_OF_MEMORY;
\r
402 return TAG_PARSE_SUCCESS;
\r
405 diff --git a/test/insert b/test/insert
\r
406 index a3b6283..24a61e1 100755
\r
409 @@ -46,10 +46,14 @@ expected='[[[{
\r
410 test_expect_equal_json "$output" "$expected"
\r
412 test_begin_subtest "Insert message, duplicate message"
\r
413 -notmuch insert < "$gen_msg_filename"
\r
414 +notmuch insert +duptag -unread < "$gen_msg_filename"
\r
415 output=$(notmuch search --output=files "subject:insert-subject" | wc -l)
\r
416 test_expect_equal "$output" 2
\r
418 +test_begin_subtest "Insert message, duplicate message does not change tags"
\r
419 +output=$(notmuch search --format=json --output=tags "subject:insert-subject")
\r
420 +test_expect_equal_json "$output" '["inbox", "unread"]'
\r
422 test_begin_subtest "Insert message, add tag"
\r
424 notmuch insert +custom < "$gen_msg_filename"
\r