1 /* mbtool.c - tool to fiddle mailboxes
2  *
3  * Copyright (c) 1994-2008 Carnegie Mellon University.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. The name "Carnegie Mellon University" must not be used to
18  *    endorse or promote products derived from this software without
19  *    prior written permission. For permission or any legal
20  *    details, please contact
21  *      Carnegie Mellon University
22  *      Center for Technology Transfer and Enterprise Creation
23  *      4615 Forbes Avenue
24  *      Suite 302
25  *      Pittsburgh, PA  15213
26  *      (412) 268-7393, fax: (412) 268-7395
27  *      innovation@andrew.cmu.edu
28  *
29  * 4. Redistributions of any form whatsoever must retain the following
30  *    acknowledgment:
31  *    "This product includes software developed by Computing Services
32  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
33  *
34  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41  */
42 
43 #include <config.h>
44 
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48 #include <stdlib.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <fcntl.h>
52 #include <sysexits.h>
53 #include <syslog.h>
54 #include <sys/types.h>
55 #include <netinet/in.h>
56 #include <sys/stat.h>
57 
58 #if HAVE_DIRENT_H
59 # include <dirent.h>
60 # define NAMLEN(dirent) strlen((dirent)->d_name)
61 #else
62 # define dirent direct
63 # define NAMLEN(dirent) (dirent)->d_namlen
64 # if HAVE_SYS_NDIR_H
65 #  include <sys/ndir.h>
66 # endif
67 # if HAVE_SYS_DIR_H
68 #  include <sys/dir.h>
69 # endif
70 # if HAVE_NDIR_H
71 #  include <ndir.h>
72 # endif
73 #endif
74 
75 #include "assert.h"
76 #include "index.h"
77 #include "global.h"
78 #include "mailbox.h"
79 #include "message.h"
80 #include "message_guid.h"
81 #include "mboxname.h"
82 #include "mboxlist.h"
83 #include "seen.h"
84 #include "times.h"
85 #include "util.h"
86 #include "xmalloc.h"
87 
88 /* generated headers are not necessarily in current directory */
89 #include "imap/imap_err.h"
90 
91 extern int optind;
92 extern char *optarg;
93 
94 /* current namespace */
95 static struct namespace mbtool_namespace;
96 
97 /* forward declarations */
98 static int do_cmd(struct findall_data *data, void *rock);
99 
100 static void usage(void);
101 void shut_down(int code);
102 
103 enum {
104     CMD_TIME = 1,
105     CMD_REID = 2,
106 };
107 
main(int argc,char ** argv)108 int main(int argc, char **argv)
109 {
110     int opt, i, r;
111     int cmd = 0;
112     char *alt_config = NULL;
113 
114     while ((opt = getopt(argc, argv, "C:rt")) != EOF) {
115         switch (opt) {
116         case 'C': /* alt config file */
117             alt_config = optarg;
118             break;
119 
120         case 'r':
121             if (cmd == 0) cmd = CMD_REID;
122             else usage();
123             break;
124         case 't':
125             if (cmd == 0) cmd = CMD_TIME;
126             else usage();
127             break;
128 
129         default:
130             usage();
131         }
132     }
133 
134     /* must provide a command */
135     if (!cmd) usage();
136 
137     /* must provide some mailboxes */
138     if (optind == argc) usage();
139 
140     cyrus_init(alt_config, "mbtool", 0, 0);
141 
142     /* Set namespace -- force standard (internal) */
143     if ((r = mboxname_init_namespace(&mbtool_namespace, 1)) != 0) {
144         syslog(LOG_ERR, "%s", error_message(r));
145         fatal(error_message(r), EX_CONFIG);
146     }
147 
148     signals_set_shutdown(&shut_down);
149     signals_add_handlers(0);
150 
151     for (i = optind; i < argc; i++) {
152         mboxlist_findall(&mbtool_namespace, argv[i], 1, 0, 0, do_cmd, &cmd);
153     }
154 
155     exit(0);
156 }
157 
usage(void)158 static void usage(void)
159 {
160     fprintf(stderr, "Usage:\n");
161     fprintf(stderr, "    mbtool [options] {-r|-t} mailbox...\n");
162     fprintf(stderr, "\nCommands:\n");
163     fprintf(stderr, "    -r    create new unique IDs for specified mailboxes\n");
164     fprintf(stderr, "    -t    normalise internaldates in specified mailboxes\n");
165     fprintf(stderr, "\nOptions:\n");
166     fprintf(stderr, "    -C alt_config  use alternate imapd.conf file\n");
167     exit(EX_USAGE);
168 }
169 
170 /*
171  * mboxlist_findall() callback function to examine a mailbox
172  */
do_timestamp(const mbname_t * mbname)173 static int do_timestamp(const mbname_t *mbname)
174 {
175     int r = 0;
176     struct mailbox *mailbox = NULL;
177     char olddate[RFC5322_DATETIME_MAX+1];
178     char newdate[RFC5322_DATETIME_MAX+1];
179 
180     signals_poll();
181 
182     /* Convert internal name to external */
183     const char *extname = mbname_extname(mbname, &mbtool_namespace, "cyrus");
184     printf("Working on %s...\n", extname);
185 
186     const char *name = mbname_intname(mbname);
187 
188     /* Open/lock header */
189     r = mailbox_open_iwl(name, &mailbox);
190     if (r) return r;
191 
192     struct mailbox_iter *iter = mailbox_iter_init(mailbox, 0, ITER_SKIP_EXPUNGED);
193     const message_t *msg;
194     while ((msg = mailbox_iter_step(iter))) {
195         const struct index_record *record = msg_record(msg);
196         /* 1 day is close enough */
197         if (labs(record->internaldate - record->gmtime) < 86400)
198             continue;
199 
200         struct index_record copyrecord = *record;
201 
202         time_to_rfc5322(copyrecord.internaldate, olddate, sizeof(olddate));
203         time_to_rfc5322(copyrecord.gmtime, newdate, sizeof(newdate));
204         printf("  %u: %s => %s\n", copyrecord.uid, olddate, newdate);
205 
206         /* switch internaldate */
207         copyrecord.internaldate = copyrecord.gmtime;
208 
209         r = mailbox_rewrite_index_record(mailbox, &copyrecord);
210         if (r) goto done;
211     }
212 
213  done:
214     mailbox_iter_done(&iter);
215     mailbox_close(&mailbox);
216 
217     return r;
218 }
219 
do_reid(const mbname_t * mbname)220 static int do_reid(const mbname_t *mbname)
221 {
222     int r = 0;
223     struct mailbox *mailbox = NULL;
224     mbentry_t *mbentry = NULL;
225 
226     /* Convert internal name to external */
227     const char *extname = mbname_extname(mbname, &mbtool_namespace, "cyrus");
228     printf("Working on %s...\n", extname);
229 
230     const char *name = mbname_intname(mbname);
231 
232     r = mailbox_open_iwl(name, &mailbox);
233     if (r) return r;
234 
235     mailbox_make_uniqueid(mailbox);
236 
237     r = mboxlist_lookup(name, &mbentry, NULL);
238     if (r) return r;
239 
240     free(mbentry->uniqueid);
241     mbentry->uniqueid = xstrdup(mailbox->uniqueid);
242 
243     mboxlist_update(mbentry, 0);
244 
245     mailbox_close(&mailbox);
246 
247     /* printf("did reid %s\n", mboxname); */
248     return r;
249 }
250 
251 
do_cmd(struct findall_data * data,void * rock)252 int do_cmd(struct findall_data *data, void *rock)
253 {
254     if (!data) return 0;
255     if (!data->is_exactmatch) return 0;
256 
257     int *valp = (int *)rock;
258 
259     if (*valp == CMD_TIME)
260         return do_timestamp(data->mbname);
261 
262     if (*valp == CMD_REID)
263         return do_reid(data->mbname);
264 
265     return 0;
266 }
267 
268 /*
269  * Cleanly shut down and exit
270  */
271 void shut_down(int code) __attribute__((noreturn));
shut_down(int code)272 void shut_down(int code)
273 {
274     in_shutdown = 1;
275 
276     mboxlist_close();
277     mboxlist_done();
278     exit(code);
279 }
280