Use notmuch_database_destroy instead of notmuch_database_close
[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     notmuch = notmuch_database_open (notmuch_config_get_database_path (config),
119                                      NOTMUCH_DATABASE_MODE_READ_WRITE);
120     if (notmuch == NULL)
121         return 1;
122
123     synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);
124
125     notmuch_opt_desc_t options[] = {
126         { NOTMUCH_OPT_POSITION, &input_file_name, 0, 0, 0 },
127         { NOTMUCH_OPT_BOOLEAN,  &accumulate, "accumulate", 'a', 0 },
128         { 0, 0, 0, 0, 0 }
129     };
130
131     opt_index = parse_arguments (argc, argv, options, 1);
132
133     if (opt_index < 0) {
134         /* diagnostics already printed */
135         return 1;
136     }
137
138     if (input_file_name) {
139         input = fopen (input_file_name, "r");
140         if (input == NULL) {
141             fprintf (stderr, "Error opening %s for reading: %s\n",
142                      input_file_name, strerror (errno));
143             return 1;
144         }
145         optind++;
146     }
147
148     if (opt_index < argc) {
149         fprintf (stderr,
150          "Cannot read dump from more than one file: %s\n",
151                  argv[optind]);
152         return 1;
153     }
154
155     /* Dump output is one line per message. We match a sequence of
156      * non-space characters for the message-id, then one or more
157      * spaces, then a list of space-separated tags as a sequence of
158      * characters within literal '(' and ')'. */
159     if ( xregcomp (&regex,
160                    "^([^ ]+) \\(([^)]*)\\)$",
161                    REG_EXTENDED) )
162         INTERNAL_ERROR("compile time constant regex failed.");
163
164     while ((line_len = getline (&line, &line_size, input)) != -1) {
165         regmatch_t match[3];
166         char *message_id, *file_tags;
167
168         chomp_newline (line);
169
170         rerr = xregexec (&regex, line, 3, match, 0);
171         if (rerr == REG_NOMATCH)
172         {
173             fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",
174                      line);
175             continue;
176         }
177
178         message_id = xstrndup (line + match[1].rm_so,
179                                match[1].rm_eo - match[1].rm_so);
180         file_tags = xstrndup (line + match[2].rm_so,
181                               match[2].rm_eo - match[2].rm_so);
182
183         tag_message (notmuch, message_id, file_tags, !accumulate,
184                      synchronize_flags);
185
186         free (message_id);
187         free (file_tags);
188     }
189
190     regfree (&regex);
191
192     if (line)
193         free (line);
194
195     notmuch_database_destroy (notmuch);
196     if (input != stdin)
197         fclose (input);
198
199     return 0;
200 }