More sane behavior on a new installation
[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 #include "ctdl_module.h"
52
53
54 /*
55  * Fix up the name for Citadel user 0 and try to remove any extra users with number 0
56  */
57 void fix_sys_user_name(void)
58 {
59         struct ctdluser usbuf;
60         char usernamekey[USERNAME_SIZE];
61
62         /** If we have a user called Citadel rename them to SYS_Citadel */
63         if (CtdlGetUser(&usbuf, "Citadel") == 0)
64         {
65                 rename_user("Citadel", "SYS_Citadel");
66         }
67
68         while (CtdlGetUserByNumber(&usbuf, 0) == 0)
69         {
70                 /* delete user with number 0 and no name */
71                 if (IsEmptyStr(usbuf.fullname)) {
72                         cdb_delete(CDB_USERS, "", 0);
73                 }
74                 else {
75                         /* temporarily set this user to -1 */
76                         usbuf.usernum = -1;
77                         CtdlPutUser(&usbuf);
78                 }
79         }
80
81         /* Make sure user SYS_* is user 0 */
82         while (CtdlGetUserByNumber(&usbuf, -1) == 0)
83         {
84                 if (strncmp(usbuf.fullname, "SYS_", 4))
85                 {       /* Delete any user 0 that doesn't start with SYS_ */
86                         makeuserkey(usernamekey, usbuf.fullname, cutuserkey(usbuf.fullname));
87                         cdb_delete(CDB_USERS, usernamekey, strlen(usernamekey));
88                 }
89                 else {
90                         usbuf.usernum = 0;
91                         CtdlPutUser(&usbuf);
92                 }
93         }
94 }
95
96
97 /* 
98  * Back end processing function for cmd_bmbx
99  */
100 void cmd_bmbx_backend(struct ctdlroom *qrbuf, void *data) {
101         static struct RoomProcList *rplist = NULL;
102         struct RoomProcList *ptr;
103         struct ctdlroom qr;
104
105         /* Lazy programming here.  Call this function as a CtdlForEachRoom backend
106          * in order to queue up the room names, or call it with a null room
107          * to make it do the processing.
108          */
109         if (qrbuf != NULL) {
110                 ptr = (struct RoomProcList *) malloc(sizeof (struct RoomProcList));
111                 if (ptr == NULL) return;
112
113                 safestrncpy(ptr->name, qrbuf->QRname, sizeof ptr->name);
114                 ptr->next = rplist;
115                 rplist = ptr;
116                 return;
117         }
118
119         while (rplist != NULL) {
120
121                 if (CtdlGetRoomLock(&qr, rplist->name) == 0) {
122                         syslog(LOG_DEBUG, "Processing <%s>...", rplist->name);
123                         if ( (qr.QRflags & QR_MAILBOX) == 0) {
124                                 syslog(LOG_DEBUG, "  -- not a mailbox");
125                         }
126                         else {
127
128                                 qr.QRgen = time(NULL);
129                                 syslog(LOG_DEBUG, "  -- fixed!");
130                         }
131                         CtdlPutRoomLock(&qr);
132                 }
133
134                 ptr = rplist;
135                 rplist = rplist->next;
136                 free(ptr);
137         }
138 }
139
140 /*
141  * quick fix to bump mailbox generation numbers
142  */
143 void bump_mailbox_generation_numbers(void) {
144         syslog(LOG_WARNING, "Applying security fix to mailbox rooms");
145         CtdlForEachRoom(cmd_bmbx_backend, NULL);
146         cmd_bmbx_backend(NULL, NULL);
147         return;
148 }
149
150
151 /* 
152  * Back end processing function for convert_ctdluid_to_minusone()
153  */
154 void cbtm_backend(struct ctdluser *usbuf, void *data) {
155         static struct UserProcList *uplist = NULL;
156         struct UserProcList *ptr;
157         struct ctdluser us;
158
159         /* Lazy programming here.  Call this function as a ForEachUser backend
160          * in order to queue up the room names, or call it with a null user
161          * to make it do the processing.
162          */
163         if (usbuf != NULL) {
164                 ptr = (struct UserProcList *)
165                         malloc(sizeof (struct UserProcList));
166                 if (ptr == NULL) return;
167
168                 safestrncpy(ptr->user, usbuf->fullname, sizeof ptr->user);
169                 ptr->next = uplist;
170                 uplist = ptr;
171                 return;
172         }
173
174         while (uplist != NULL) {
175
176                 if (CtdlGetUserLock(&us, uplist->user) == 0) {
177                         syslog(LOG_DEBUG, "Processing <%s>...", uplist->user);
178                         if (us.uid == CTDLUID) {
179                                 us.uid = (-1);
180                         }
181                         CtdlPutUserLock(&us);
182                 }
183
184                 ptr = uplist;
185                 uplist = uplist->next;
186                 free(ptr);
187         }
188 }
189
190 /*
191  * quick fix to change all CTDLUID users to (-1)
192  */
193 void convert_ctdluid_to_minusone(void) {
194         syslog(LOG_WARNING, "Applying uid changes");
195         ForEachUser(cbtm_backend, NULL);
196         cbtm_backend(NULL, NULL);
197         return;
198 }
199
200
201
202 /*
203  * These accounts may have been created by code that ran between mid 2008 and early 2011.
204  * If present they are no longer in use and may be deleted.
205  */
206 void remove_thread_users(void) {
207         char *deleteusers[] = {
208                 "SYS_checkpoint",
209                 "SYS_extnotify",
210                 "SYS_IGnet Queue",
211                 "SYS_indexer",
212                 "SYS_network",
213                 "SYS_popclient",
214                 "SYS_purger",
215                 "SYS_rssclient",
216                 "SYS_select_on_master",
217                 "SYS_SMTP Send"
218         };
219
220         int i;
221         struct ctdluser usbuf;
222         for (i=0; i<(sizeof(deleteusers)/sizeof(char *)); ++i) {
223                 if (CtdlGetUser(&usbuf, deleteusers[i]) == 0) {
224                         usbuf.axlevel = 0;
225                         strcpy(usbuf.password, "deleteme");
226                         CtdlPutUser(&usbuf);
227                         syslog(LOG_INFO,
228                                 "System user account <%s> is no longer in use and will be deleted.",
229                                 deleteusers[i]
230                         );
231                 }
232         }
233 }
234
235
236 /*
237  * Attempt to guess the name of the time zone currently in use
238  * on the underlying host system.
239  */
240 void guess_time_zone(void) {
241         FILE *fp;
242         char buf[PATH_MAX];
243
244         fp = popen(file_guesstimezone, "r");
245         if (fp) {
246                 if (fgets(buf, sizeof buf, fp) && (strlen(buf) > 2)) {
247                         buf[strlen(buf)-1] = 0;
248                         safestrncpy(config.c_default_cal_zone, buf, sizeof config.c_default_cal_zone);
249                         syslog(LOG_INFO, "Configuring timezone: %s", config.c_default_cal_zone);
250                 }
251                 fclose(fp);
252         }
253 }
254
255
256 /*
257  * Perform any upgrades that can be done automatically based on our knowledge of the previous
258  * version of Citadel server that was running here.
259  *
260  * Note that if the previous version was 0 then this is a new installation running for the first time.
261  */
262 void update_config(void) {
263         get_config();
264
265         if (CitControl.version < 606) {
266                 config.c_rfc822_strict_from = 0;
267         }
268
269         if (CitControl.version < 609) {
270                 config.c_purge_hour = 3;
271         }
272
273         if (CitControl.version < 615) {
274                 config.c_ldap_port = 389;
275         }
276
277         if (CitControl.version < 623) {
278                 strcpy(config.c_ip_addr, "*");
279         }
280
281         if (CitControl.version < 650) {
282                 config.c_enable_fulltext = 1;
283         }
284
285         if (CitControl.version < 652) {
286                 config.c_auto_cull = 1;
287         }
288
289         if (CitControl.version < 725) {
290                 config.c_xmpp_c2s_port = 5222;
291                 config.c_xmpp_s2s_port = 5269;
292         }
293
294         if (IsEmptyStr(config.c_default_cal_zone)) {
295                 guess_time_zone();
296         }
297
298         put_config();
299 }
300
301
302
303 /*
304  * Based on the server version number reported by the existing database,
305  * run in-place data format upgrades until everything is up to date.
306  */
307 void check_server_upgrades(void) {
308
309         get_control();
310         syslog(LOG_INFO, "Existing database version on disk is %d.%02d",
311                 (CitControl.version / 100),
312                 (CitControl.version % 100)
313         );
314
315         if (CitControl.version < REV_LEVEL) {
316                 syslog(LOG_WARNING,
317                         "Server hosted updates need to be processed at this time.  Please wait..."
318                 );
319         }
320         else {
321                 return;
322         }
323
324         update_config();
325
326         if ((CitControl.version > 000) && (CitControl.version < 555)) {
327                 syslog(LOG_EMERG, "This database is too old to be upgraded.  Citadel server will exit.");
328                 exit(EXIT_FAILURE);
329         }
330         if ((CitControl.version > 000) && (CitControl.version < 591)) {
331                 bump_mailbox_generation_numbers();
332         }
333         if ((CitControl.version > 000) && (CitControl.version < 608)) {
334                 convert_ctdluid_to_minusone();
335         }
336         if ((CitControl.version > 000) && (CitControl.version < 659)) {
337                 rebuild_euid_index();
338         }
339         if (CitControl.version < 735) {
340                 fix_sys_user_name();
341         }
342         if (CitControl.version < 736) {
343                 rebuild_usersbynumber();
344         }
345         if (CitControl.version < 790) {
346                 remove_thread_users();
347         }
348         CitControl.version = REV_LEVEL;
349
350         /*
351          * Negative values for maxsessions are not allowed.
352          */
353         if (config.c_maxsessions < 0) {
354                 config.c_maxsessions = 0;
355         }
356
357         /* We need a system default message expiry policy, because this is
358          * the top level and there's no 'higher' policy to fall back on.
359          * By default, do not expire messages at all.
360          */
361         if (config.c_ep.expire_mode == 0) {
362                 config.c_ep.expire_mode = EXPIRE_MANUAL;
363                 config.c_ep.expire_value = 0;
364         }
365
366         put_control();
367 }
368
369
370 CTDL_MODULE_UPGRADE(upgrade)
371 {
372         check_server_upgrades();
373         
374         /* return our module id for the Log */
375         return "upgrade";
376 }