Instant expunge defaults to ON on new installations
[citadel.git] / citadel / modules / upgrade / serv_upgrade.c
1 /*
2  * Transparently handle the upgrading of server data formats.
3  *
4  * Copyright (c) 1987-2012 by the citadel.org team
5  *
6  * This program is open source software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include "sysdep.h"
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <stdio.h>
19 #include <fcntl.h>
20 #include <signal.h>
21 #include <pwd.h>
22 #include <errno.h>
23 #include <sys/types.h>
24
25 #if TIME_WITH_SYS_TIME
26 # include <sys/time.h>
27 # include <time.h>
28 #else
29 # if HAVE_SYS_TIME_H
30 #  include <sys/time.h>
31 # else
32 #  include <time.h>
33 # endif
34 #endif
35
36 #include <sys/wait.h>
37 #include <string.h>
38 #include <limits.h>
39 #include <libcitadel.h>
40 #include "citadel.h"
41 #include "server.h"
42 #include "citserver.h"
43 #include "support.h"
44 #include "config.h"
45 #include "control.h"
46 #include "database.h"
47 #include "user_ops.h"
48 #include "msgbase.h"
49 #include "serv_upgrade.h"
50 #include "euidindex.h"
51
52
53 #include "ctdl_module.h"
54
55
56
57 /*
58  * Fix up the name for Citadel user 0 and try to remove any extra users with number 0
59  */
60 void fix_sys_user_name(void)
61 {
62         struct ctdluser usbuf;
63         char usernamekey[USERNAME_SIZE];
64
65         /** If we have a user called Citadel rename them to SYS_Citadel */
66         if (CtdlGetUser(&usbuf, "Citadel") == 0)
67         {
68                 rename_user("Citadel", "SYS_Citadel");
69         }
70
71         while (CtdlGetUserByNumber(&usbuf, 0) == 0)
72         {
73                 /* delete user with number 0 and no name */
74                 if (IsEmptyStr(usbuf.fullname)) {
75                         cdb_delete(CDB_USERS, "", 0);
76                 }
77                 else {
78                         /* temporarily set this user to -1 */
79                         usbuf.usernum = -1;
80                         CtdlPutUser(&usbuf);
81                 }
82         }
83
84         /* Make sure user SYS_* is user 0 */
85         while (CtdlGetUserByNumber(&usbuf, -1) == 0)
86         {
87                 if (strncmp(usbuf.fullname, "SYS_", 4))
88                 {       /* Delete any user 0 that doesn't start with SYS_ */
89                         makeuserkey(usernamekey, usbuf.fullname, cutuserkey(usbuf.fullname));
90                         cdb_delete(CDB_USERS, usernamekey, strlen(usernamekey));
91                 }
92                 else {
93                         usbuf.usernum = 0;
94                         CtdlPutUser(&usbuf);
95                 }
96         }
97 }
98
99
100 /* 
101  * Back end processing function for cmd_bmbx
102  */
103 void cmd_bmbx_backend(struct ctdlroom *qrbuf, void *data) {
104         static struct RoomProcList *rplist = NULL;
105         struct RoomProcList *ptr;
106         struct ctdlroom qr;
107
108         /* Lazy programming here.  Call this function as a CtdlForEachRoom backend
109          * in order to queue up the room names, or call it with a null room
110          * to make it do the processing.
111          */
112         if (qrbuf != NULL) {
113                 ptr = (struct RoomProcList *) malloc(sizeof (struct RoomProcList));
114                 if (ptr == NULL) return;
115
116                 safestrncpy(ptr->name, qrbuf->QRname, sizeof ptr->name);
117                 ptr->next = rplist;
118                 rplist = ptr;
119                 return;
120         }
121
122         while (rplist != NULL) {
123
124                 if (CtdlGetRoomLock(&qr, rplist->name) == 0) {
125                         syslog(LOG_DEBUG, "Processing <%s>...", rplist->name);
126                         if ( (qr.QRflags & QR_MAILBOX) == 0) {
127                                 syslog(LOG_DEBUG, "  -- not a mailbox");
128                         }
129                         else {
130
131                                 qr.QRgen = time(NULL);
132                                 syslog(LOG_DEBUG, "  -- fixed!");
133                         }
134                         CtdlPutRoomLock(&qr);
135                 }
136
137                 ptr = rplist;
138                 rplist = rplist->next;
139                 free(ptr);
140         }
141 }
142
143 /*
144  * quick fix to bump mailbox generation numbers
145  */
146 void bump_mailbox_generation_numbers(void) {
147         syslog(LOG_WARNING, "Applying security fix to mailbox rooms");
148         CtdlForEachRoom(cmd_bmbx_backend, NULL);
149         cmd_bmbx_backend(NULL, NULL);
150         return;
151 }
152
153
154 /* 
155  * Back end processing function for convert_ctdluid_to_minusone()
156  */
157 void cbtm_backend(struct ctdluser *usbuf, void *data) {
158         static struct UserProcList *uplist = NULL;
159         struct UserProcList *ptr;
160         struct ctdluser us;
161
162         /* Lazy programming here.  Call this function as a ForEachUser backend
163          * in order to queue up the room names, or call it with a null user
164          * to make it do the processing.
165          */
166         if (usbuf != NULL) {
167                 ptr = (struct UserProcList *)
168                         malloc(sizeof (struct UserProcList));
169                 if (ptr == NULL) return;
170
171                 safestrncpy(ptr->user, usbuf->fullname, sizeof ptr->user);
172                 ptr->next = uplist;
173                 uplist = ptr;
174                 return;
175         }
176
177         while (uplist != NULL) {
178
179                 if (CtdlGetUserLock(&us, uplist->user) == 0) {
180                         syslog(LOG_DEBUG, "Processing <%s>...", uplist->user);
181                         if (us.uid == CTDLUID) {
182                                 us.uid = (-1);
183                         }
184                         CtdlPutUserLock(&us);
185                 }
186
187                 ptr = uplist;
188                 uplist = uplist->next;
189                 free(ptr);
190         }
191 }
192
193 /*
194  * quick fix to change all CTDLUID users to (-1)
195  */
196 void convert_ctdluid_to_minusone(void) {
197         syslog(LOG_WARNING, "Applying uid changes");
198         ForEachUser(cbtm_backend, NULL);
199         cbtm_backend(NULL, NULL);
200         return;
201 }
202
203
204
205 /*
206  * These accounts may have been created by code that ran between mid 2008 and early 2011.
207  * If present they are no longer in use and may be deleted.
208  */
209 void remove_thread_users(void) {
210         char *deleteusers[] = {
211                 "SYS_checkpoint",
212                 "SYS_extnotify",
213                 "SYS_IGnet Queue",
214                 "SYS_indexer",
215                 "SYS_network",
216                 "SYS_popclient",
217                 "SYS_purger",
218                 "SYS_rssclient",
219                 "SYS_select_on_master",
220                 "SYS_SMTP Send"
221         };
222
223         int i;
224         struct ctdluser usbuf;
225         for (i=0; i<(sizeof(deleteusers)/sizeof(char *)); ++i) {
226                 if (CtdlGetUser(&usbuf, deleteusers[i]) == 0) {
227                         usbuf.axlevel = 0;
228                         strcpy(usbuf.password, "deleteme");
229                         CtdlPutUser(&usbuf);
230                         syslog(LOG_INFO,
231                                 "System user account <%s> is no longer in use and will be deleted.",
232                                 deleteusers[i]
233                         );
234                 }
235         }
236 }
237
238
239 /*
240  * Attempt to guess the name of the time zone currently in use
241  * on the underlying host system.
242  */
243 void guess_time_zone(void) {
244         FILE *fp;
245         char buf[PATH_MAX];
246
247         fp = popen(file_guesstimezone, "r");
248         if (fp) {
249                 if (fgets(buf, sizeof buf, fp) && (strlen(buf) > 2)) {
250                         buf[strlen(buf)-1] = 0;
251                         safestrncpy(config.c_default_cal_zone, buf, sizeof config.c_default_cal_zone);
252                         syslog(LOG_INFO, "Configuring timezone: %s", config.c_default_cal_zone);
253                 }
254                 fclose(fp);
255         }
256 }
257
258
259 /*
260  * Perform any upgrades that can be done automatically based on our knowledge of the previous
261  * version of Citadel server that was running here.
262  *
263  * Note that if the previous version was 0 then this is a new installation running for the first time.
264  */
265 void update_config(void) {
266         get_config();
267
268         if (CitControl.version == 0) {
269                 config.c_instant_expunge = 1;
270         }
271
272         if (CitControl.version < 606) {
273                 config.c_rfc822_strict_from = 0;
274         }
275
276         if (CitControl.version < 609) {
277                 config.c_purge_hour = 3;
278         }
279
280         if (CitControl.version < 615) {
281                 config.c_ldap_port = 389;
282         }
283
284         if (CitControl.version < 623) {
285                 strcpy(config.c_ip_addr, "*");
286         }
287
288         if (CitControl.version < 650) {
289                 config.c_enable_fulltext = 1;
290         }
291
292         if (CitControl.version < 652) {
293                 config.c_auto_cull = 1;
294         }
295
296         if (CitControl.version < 725) {
297                 config.c_xmpp_c2s_port = 5222;
298                 config.c_xmpp_s2s_port = 5269;
299         }
300
301         if (IsEmptyStr(config.c_default_cal_zone)) {
302                 guess_time_zone();
303         }
304
305         put_config();
306 }
307
308
309
310 /*
311  * Based on the server version number reported by the existing database,
312  * run in-place data format upgrades until everything is up to date.
313  */
314 void check_server_upgrades(void) {
315
316         get_control();
317         syslog(LOG_INFO, "Existing database version on disk is %d.%02d",
318                 (CitControl.version / 100),
319                 (CitControl.version % 100)
320         );
321
322         if (CitControl.version < REV_LEVEL) {
323                 syslog(LOG_WARNING,
324                         "Server hosted updates need to be processed at this time.  Please wait..."
325                 );
326         }
327         else {
328                 return;
329         }
330
331         update_config();
332
333         if ((CitControl.version > 000) && (CitControl.version < 555)) {
334                 syslog(LOG_EMERG, "This database is too old to be upgraded.  Citadel server will exit.");
335                 exit(EXIT_FAILURE);
336         }
337         if ((CitControl.version > 000) && (CitControl.version < 591)) {
338                 bump_mailbox_generation_numbers();
339         }
340         if ((CitControl.version > 000) && (CitControl.version < 608)) {
341                 convert_ctdluid_to_minusone();
342         }
343         if ((CitControl.version > 000) && (CitControl.version < 659)) {
344                 rebuild_euid_index();
345         }
346         if (CitControl.version < 735) {
347                 fix_sys_user_name();
348         }
349         if (CitControl.version < 736) {
350                 rebuild_usersbynumber();
351         }
352         if (CitControl.version < 790) {
353                 remove_thread_users();
354         }
355         CitControl.version = REV_LEVEL;
356         put_control();
357 }
358
359
360 CTDL_MODULE_UPGRADE(upgrade)
361 {
362         check_server_upgrades();
363         
364         /* return our module id for the Log */
365         return "upgrade";
366 }