notmuch: Add a configuration system.
[notmuch] / notmuch-setup.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 notmuch_status_t
24 add_all_files (notmuch_database_t *notmuch,
25                const char *mail_directory,
26                int num_files)
27 {
28     add_files_state_t add_files_state;
29     double elapsed;
30     struct timeval tv_now;
31     notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
32
33     add_files_state.ignore_read_only_directories = FALSE;
34     add_files_state.saw_read_only_directory = FALSE;
35     add_files_state.total_files = num_files;
36     add_files_state.processed_files = 0;
37     add_files_state.added_messages = 0;
38     add_files_state.callback = NULL;
39     gettimeofday (&add_files_state.tv_start, NULL);
40
41     ret = add_files (notmuch, mail_directory, &add_files_state);
42
43     gettimeofday (&tv_now, NULL);
44     elapsed = notmuch_time_elapsed (add_files_state.tv_start,
45                                     tv_now);
46     printf ("Processed %d %s in ", add_files_state.processed_files,
47             add_files_state.processed_files == 1 ?
48             "file" : "total files");
49     notmuch_time_print_formatted_seconds (elapsed);
50     if (elapsed > 1) {
51         printf (" (%d files/sec.).                 \n",
52                 (int) (add_files_state.processed_files / elapsed));
53     } else {
54         printf (".                    \n");
55     }
56     if (add_files_state.added_messages) {
57         printf ("Added %d %s to the database.\n\n",
58                 add_files_state.added_messages,
59                 add_files_state.added_messages == 1 ?
60                 "message" : "unique messages");
61     }
62
63     return ret;
64 }
65
66
67 /* XXX: This should be merged with the existing add_files function in
68  * add-files.c. */
69 /* Recursively count all regular files in path and all sub-direcotries
70  * of path.  The result is added to *count (which should be
71  * initialized to zero by the top-level caller before calling
72  * count_files). */
73 static void
74 count_files (const char *path, int *count)
75 {
76     DIR *dir;
77     struct dirent *e, *entry = NULL;
78     int entry_length;
79     int err;
80     char *next;
81     struct stat st;
82
83     dir = opendir (path);
84
85     if (dir == NULL) {
86         fprintf (stderr, "Warning: failed to open directory %s: %s\n",
87                  path, strerror (errno));
88         goto DONE;
89     }
90
91     entry_length = offsetof (struct dirent, d_name) +
92         pathconf (path, _PC_NAME_MAX) + 1;
93     entry = malloc (entry_length);
94
95     while (1) {
96         err = readdir_r (dir, entry, &e);
97         if (err) {
98             fprintf (stderr, "Error reading directory: %s\n",
99                      strerror (errno));
100             free (entry);
101             goto DONE;
102         }
103
104         if (e == NULL)
105             break;
106
107         /* Ignore special directories to avoid infinite recursion.
108          * Also ignore the .notmuch directory.
109          */
110         /* XXX: Eventually we'll want more sophistication to let the
111          * user specify files to be ignored. */
112         if (strcmp (entry->d_name, ".") == 0 ||
113             strcmp (entry->d_name, "..") == 0 ||
114             strcmp (entry->d_name, ".notmuch") == 0)
115         {
116             continue;
117         }
118
119         if (asprintf (&next, "%s/%s", path, entry->d_name) == -1) {
120             next = NULL;
121             fprintf (stderr, "Error descending from %s to %s: Out of memory\n",
122                      path, entry->d_name);
123             continue;
124         }
125
126         stat (next, &st);
127
128         if (S_ISREG (st.st_mode)) {
129             *count = *count + 1;
130             if (*count % 1000 == 0) {
131                 printf ("Found %d files so far.\r", *count);
132                 fflush (stdout);
133             }
134         } else if (S_ISDIR (st.st_mode)) {
135             count_files (next, count);
136         }
137
138         free (next);
139     }
140
141   DONE:
142     if (entry)
143         free (entry);
144
145     closedir (dir);
146 }
147
148 static const char *
149 make_path_absolute (void *ctx, const char *path)
150 {
151     char *cwd;
152
153     if (*path == '/')
154         return path;
155
156     cwd = getcwd (NULL, 0);
157     if (cwd == NULL) {
158         fprintf (stderr, "Out of memory.\n");
159         return NULL;
160     }
161
162     path = talloc_asprintf (ctx, "%s/%s", cwd, path);
163     if (path == NULL)
164         fprintf (stderr, "Out of memory.\n");
165
166     free (cwd);
167
168     return path;
169 }
170
171 int
172 notmuch_setup_command (unused (void *ctx),
173                        unused (int argc), unused (char *argv[]))
174 {
175     char *response = NULL;
176     size_t response_size;
177     notmuch_config_t *config;
178     char **old_other_emails;
179     size_t old_other_emails_len;
180     GPtrArray *other_emails;
181     unsigned int i;
182
183 #define prompt(format, ...)                             \
184     do {                                                \
185         printf (format, ##__VA_ARGS__);                 \
186         fflush (stdout);                                \
187         getline (&response, &response_size, stdin);     \
188         chomp_newline (response);                       \
189     } while (0)
190
191     config = notmuch_config_open (ctx, NULL);
192
193     prompt ("Your full name [%s]: ", notmuch_config_get_user_name (config));
194     if (strlen (response))
195         notmuch_config_set_user_name (config, response);
196
197     prompt ("Your primary email address [%s]: ",
198             notmuch_config_get_user_primary_email (config));
199     if (strlen (response))
200         notmuch_config_set_user_primary_email (config, response);
201
202     other_emails = g_ptr_array_new ();
203
204     old_other_emails = notmuch_config_get_user_other_email (config,
205                                              &old_other_emails_len);
206     for (i = 0; i < old_other_emails_len; i++) {
207         prompt ("Additional email address [%s]: ", old_other_emails[i]);
208         if (strlen (response))
209             g_ptr_array_add (other_emails, talloc_strdup (ctx, response));
210         else
211             g_ptr_array_add (other_emails, talloc_strdup (ctx,
212                                                          old_other_emails[i]));
213     }
214
215     do {
216         prompt ("Additional email address [Press 'Enter' if none]: ");
217         if (strlen (response))
218             g_ptr_array_add (other_emails, talloc_strdup (ctx, response));
219     } while (strlen (response));
220     if (other_emails->len)
221         notmuch_config_set_user_other_email (config,
222                                              (const char **)
223                                              other_emails->pdata,
224                                              other_emails->len);
225     g_ptr_array_free (other_emails, TRUE);
226
227     prompt ("Top-level directory of your email archive [%s]: ",
228             notmuch_config_get_database_path (config));
229     if (strlen (response)) {
230         const char *absolute_path;
231
232         absolute_path = make_path_absolute (ctx, response);
233         notmuch_config_set_database_path (config, absolute_path);
234     }
235
236     notmuch_config_save (config);
237
238     return 0;
239 }