]> git.notmuchmail.org Git - notmuch/blob - notmuch-restore.c
debian: start changelog stanza for 0.13~rc1-1
[notmuch] / notmuch-restore.c
1 /* notmuch - Not much of an email program, (just index and search)
2  *
3  * Copyright © 2009 Carl Worth
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see http://www.gnu.org/licenses/ .
17  *
18  * Author: Carl Worth <cworth@cworth.org>
19  */
20
21 #include "notmuch-client.h"
22
23 static int
24 tag_message (notmuch_database_t *notmuch, const char *message_id,
25              char *file_tags, notmuch_bool_t remove_all,
26              notmuch_bool_t synchronize_flags)
27 {
28     notmuch_status_t status;
29     notmuch_tags_t *db_tags;
30     char *db_tags_str;
31     notmuch_message_t *message = NULL;
32     const char *tag;
33     char *next;
34     int ret = 0;
35
36     status = notmuch_database_find_message (notmuch, message_id, &message);
37     if (status || message == NULL) {
38         fprintf (stderr, "Warning: Cannot apply tags to %smessage: %s\n",
39                  message ? "" : "missing ", message_id);
40         if (status)
41             fprintf (stderr, "%s\n", notmuch_status_to_string(status));
42         return 1;
43     }
44
45     /* In order to detect missing messages, this check/optimization is
46      * intentionally done *after* first finding the message. */
47     if (!remove_all && (file_tags == NULL || *file_tags == '\0'))
48         goto DONE;
49
50     db_tags_str = NULL;
51     for (db_tags = notmuch_message_get_tags (message);
52          notmuch_tags_valid (db_tags);
53          notmuch_tags_move_to_next (db_tags)) {
54         tag = notmuch_tags_get (db_tags);
55
56         if (db_tags_str)
57             db_tags_str = talloc_asprintf_append (db_tags_str, " %s", tag);
58         else
59             db_tags_str = talloc_strdup (message, tag);
60     }
61
62     if (((file_tags == NULL || *file_tags == '\0') &&
63          (db_tags_str == NULL || *db_tags_str == '\0')) ||
64         (file_tags && db_tags_str && strcmp (file_tags, db_tags_str) == 0))
65         goto DONE;
66
67     notmuch_message_freeze (message);
68
69     if (remove_all)
70         notmuch_message_remove_all_tags (message);
71
72     next = file_tags;
73     while (next) {
74         tag = strsep (&next, " ");
75         if (*tag == '\0')
76             continue;
77         status = notmuch_message_add_tag (message, tag);
78         if (status) {
79             fprintf (stderr, "Error applying tag %s to message %s:\n",
80                      tag, message_id);
81             fprintf (stderr, "%s\n", notmuch_status_to_string (status));
82             ret = 1;
83         }
84     }
85
86     notmuch_message_thaw (message);
87
88     if (synchronize_flags)
89         notmuch_message_tags_to_maildir_flags (message);
90
91 DONE:
92     if (message)
93         notmuch_message_destroy (message);
94
95     return ret;
96 }
97
98 int
99 notmuch_restore_command (unused (void *ctx), int argc, char *argv[])
100 {
101     notmuch_config_t *config;
102     notmuch_database_t *notmuch;
103     notmuch_bool_t synchronize_flags;
104     notmuch_bool_t accumulate = FALSE;
105     char *input_file_name = NULL;
106     FILE *input = stdin;
107     char *line = NULL;
108     size_t line_size;
109     ssize_t line_len;
110     regex_t regex;
111     int rerr;
112     int opt_index;
113
114     config = notmuch_config_open (ctx, NULL, NULL);
115     if (config == NULL)
116         return 1;
117
118     if (notmuch_database_open (notmuch_config_get_database_path (config),
119                                NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))
120         return 1;
121
122     synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);
123
124     notmuch_opt_desc_t options[] = {
125         { NOTMUCH_OPT_POSITION, &input_file_name, 0, 0, 0 },
126         { NOTMUCH_OPT_BOOLEAN,  &accumulate, "accumulate", 'a', 0 },
127         { 0, 0, 0, 0, 0 }
128     };
129
130     opt_index = parse_arguments (argc, argv, options, 1);
131
132     if (opt_index < 0) {
133         /* diagnostics already printed */
134         return 1;
135     }
136
137     if (input_file_name) {
138         input = fopen (input_file_name, "r");
139         if (input == NULL) {
140             fprintf (stderr, "Error opening %s for reading: %s\n",
141                      input_file_name, strerror (errno));
142             return 1;
143         }
144         optind++;
145     }
146
147     if (opt_index < argc) {
148         fprintf (stderr,
149          "Cannot read dump from more than one file: %s\n",
150                  argv[optind]);
151         return 1;
152     }
153
154     /* Dump output is one line per message. We match a sequence of
155      * non-space characters for the message-id, then one or more
156      * spaces, then a list of space-separated tags as a sequence of
157      * characters within literal '(' and ')'. */
158     if ( xregcomp (&regex,
159                    "^([^ ]+) \\(([^)]*)\\)$",
160                    REG_EXTENDED) )
161         INTERNAL_ERROR("compile time constant regex failed.");
162
163     while ((line_len = getline (&line, &line_size, input)) != -1) {
164         regmatch_t match[3];
165         char *message_id, *file_tags;
166
167         chomp_newline (line);
168
169         rerr = xregexec (&regex, line, 3, match, 0);
170         if (rerr == REG_NOMATCH)
171         {
172             fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",
173                      line);
174             continue;
175         }
176
177         message_id = xstrndup (line + match[1].rm_so,
178                                match[1].rm_eo - match[1].rm_so);
179         file_tags = xstrndup (line + match[2].rm_so,
180                               match[2].rm_eo - match[2].rm_so);
181
182         tag_message (notmuch, message_id, file_tags, !accumulate,
183                      synchronize_flags);
184
185         free (message_id);
186         free (file_tags);
187     }
188
189     regfree (&regex);
190
191     if (line)
192         free (line);
193
194     notmuch_database_destroy (notmuch);
195     if (input != stdin)
196         fclose (input);
197
198     return 0;
199 }