249c825053524575e3bf5b990a442bf9d88fc111
[citadel.git] / citadel / modules / upgrade / serv_upgrade.c
1 /*
2  * Transparently handle the upgrading of server data formats.
3  *
4  * Copyright (c) 1987-2016 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 convert_ctdluid_to_minusone()
99  */
100 void cbtm_backend(struct ctdluser *usbuf, void *data) {
101         static struct UserProcList *uplist = NULL;
102         struct UserProcList *ptr;
103         struct ctdluser us;
104
105         /* Lazy programming here.  Call this function as a ForEachUser backend
106          * in order to queue up the room names, or call it with a null user
107          * to make it do the processing.
108          */
109         if (usbuf != NULL) {
110                 ptr = (struct UserProcList *)
111                         malloc(sizeof (struct UserProcList));
112                 if (ptr == NULL) return;
113
114                 safestrncpy(ptr->user, usbuf->fullname, sizeof ptr->user);
115                 ptr->next = uplist;
116                 uplist = ptr;
117                 return;
118         }
119
120         while (uplist != NULL) {
121
122                 if (CtdlGetUserLock(&us, uplist->user) == 0) {
123                         syslog(LOG_DEBUG, "Processing <%s>...", uplist->user);
124                         if (us.uid == CTDLUID) {
125                                 us.uid = (-1);
126                         }
127                         CtdlPutUserLock(&us);
128                 }
129
130                 ptr = uplist;
131                 uplist = uplist->next;
132                 free(ptr);
133         }
134 }
135
136 /*
137  * quick fix to change all CTDLUID users to (-1)
138  */
139 void convert_ctdluid_to_minusone(void) {
140         syslog(LOG_WARNING, "Applying uid changes");
141         ForEachUser(cbtm_backend, NULL);
142         cbtm_backend(NULL, NULL);
143         return;
144 }
145
146
147
148 /*
149  * These accounts may have been created by code that ran between mid 2008 and early 2011.
150  * If present they are no longer in use and may be deleted.
151  */
152 void remove_thread_users(void) {
153         char *deleteusers[] = {
154                 "SYS_checkpoint",
155                 "SYS_extnotify",
156                 "SYS_IGnet Queue",
157                 "SYS_indexer",
158                 "SYS_network",
159                 "SYS_popclient",
160                 "SYS_purger",
161                 "SYS_rssclient",
162                 "SYS_select_on_master",
163                 "SYS_SMTP Send"
164         };
165
166         int i;
167         struct ctdluser usbuf;
168         for (i=0; i<(sizeof(deleteusers)/sizeof(char *)); ++i) {
169                 if (CtdlGetUser(&usbuf, deleteusers[i]) == 0) {
170                         usbuf.axlevel = 0;
171                         strcpy(usbuf.password, "deleteme");
172                         CtdlPutUser(&usbuf);
173                         syslog(LOG_INFO,
174                                 "System user account <%s> is no longer in use and will be deleted.",
175                                 deleteusers[i]
176                         );
177                 }
178         }
179 }
180
181
182 /*
183  * Attempt to guess the name of the time zone currently in use
184  * on the underlying host system.
185  */
186 void guess_time_zone(void) {
187         FILE *fp;
188         char buf[PATH_MAX];
189
190         fp = popen(file_guesstimezone, "r");
191         if (fp) {
192                 if (fgets(buf, sizeof buf, fp) && (strlen(buf) > 2)) {
193                         buf[strlen(buf)-1] = 0;
194                         CtdlSetConfigStr("c_default_cal_zone", buf);
195                         syslog(LOG_INFO, "Configuring timezone: %s", buf);
196                 }
197                 fclose(fp);
198         }
199 }
200
201
202 /*
203  * Perform any upgrades that can be done automatically based on our knowledge of the previous
204  * version of Citadel server that was running here.
205  *
206  * Note that if the previous version was 0 then this is a new installation running for the first time.
207  */
208 void update_config(void) {
209
210         int oldver = CtdlGetConfigInt("MM_hosted_upgrade_level");
211
212         if (oldver < 606) {
213                 CtdlSetConfigInt("c_rfc822_strict_from", 0);
214         }
215
216         if (oldver < 609) {
217                 CtdlSetConfigInt("c_purge_hour", 3);
218         }
219
220         if (oldver < 615) {
221                 CtdlSetConfigInt("c_ldap_port", 389);
222         }
223
224         if (oldver < 623) {
225                 CtdlSetConfigStr("c_ip_addr", "*");
226         }
227
228         if (oldver < 650) {
229                 CtdlSetConfigInt("c_enable_fulltext", 1);
230         }
231
232         if (oldver < 652) {
233                 CtdlSetConfigInt("c_auto_cull", 1);
234         }
235
236         if (oldver < 725) {
237                 CtdlSetConfigInt("c_xmpp_c2s_port", 5222);
238                 CtdlSetConfigInt("c_xmpp_s2s_port", 5269);
239         }
240
241         if (oldver < 830) {
242                 CtdlSetConfigInt("c_nntp_port", 119);
243                 CtdlSetConfigInt("c_nntps_port", 563);
244         }
245
246         if (IsEmptyStr(CtdlGetConfigStr("c_default_cal_zone"))) {
247                 guess_time_zone();
248         }
249 }
250
251
252
253 /*
254  * Based on the server version number reported by the existing database,
255  * run in-place data format upgrades until everything is up to date.
256  */
257 void check_server_upgrades(void) {
258
259         syslog(LOG_INFO, "Existing database version on disk is %d", CtdlGetConfigInt("MM_hosted_upgrade_level"));
260
261         if (CtdlGetConfigInt("MM_hosted_upgrade_level") < REV_LEVEL) {
262                 syslog(LOG_WARNING,
263                         "Server hosted updates need to be processed at this time.  Please wait..."
264                 );
265         }
266         else {
267                 return;
268         }
269
270         update_config();
271
272         if ((CtdlGetConfigInt("MM_hosted_upgrade_level") > 000) && (CtdlGetConfigInt("MM_hosted_upgrade_level") < 591)) {
273                 syslog(LOG_EMERG, "This database is too old to be upgraded.  Citadel server will exit.");
274                 exit(EXIT_FAILURE);
275         }
276         if ((CtdlGetConfigInt("MM_hosted_upgrade_level") > 000) && (CtdlGetConfigInt("MM_hosted_upgrade_level") < 608)) {
277                 convert_ctdluid_to_minusone();
278         }
279         if ((CtdlGetConfigInt("MM_hosted_upgrade_level") > 000) && (CtdlGetConfigInt("MM_hosted_upgrade_level") < 659)) {
280                 rebuild_euid_index();
281         }
282         if (CtdlGetConfigInt("MM_hosted_upgrade_level") < 735) {
283                 fix_sys_user_name();
284         }
285         if (CtdlGetConfigInt("MM_hosted_upgrade_level") < 736) {
286                 rebuild_usersbynumber();
287         }
288         if (CtdlGetConfigInt("MM_hosted_upgrade_level") < 790) {
289                 remove_thread_users();
290         }
291         if (CtdlGetConfigInt("MM_hosted_upgrade_level") < 810) {
292                 struct ctdlroom QRoom;
293                 if (!CtdlGetRoom(&QRoom, SMTP_SPOOLOUT_ROOM)) {
294                         QRoom.QRdefaultview = VIEW_QUEUE;
295                         CtdlPutRoom(&QRoom);
296                 }
297                 if (!CtdlGetRoom(&QRoom, FNBL_QUEUE_ROOM)) {
298                         QRoom.QRdefaultview = VIEW_QUEUE;
299                         CtdlPutRoom(&QRoom);
300                 }
301         }
302
303         CtdlSetConfigInt("MM_hosted_upgrade_level", REV_LEVEL);
304
305         /*
306          * Negative values for maxsessions are not allowed.
307          */
308         if (CtdlGetConfigInt("c_maxsessions") < 0) {
309                 CtdlSetConfigInt("c_maxsessions", 0);
310         }
311
312         /* We need a system default message expiry policy, because this is
313          * the top level and there's no 'higher' policy to fall back on.
314          * By default, do not expire messages at all.
315          */
316         if (CtdlGetConfigInt("c_ep_mode") == 0) {
317                 CtdlSetConfigInt("c_ep_mode", EXPIRE_MANUAL);
318                 CtdlSetConfigInt("c_ep_value", 0);
319         }
320 }
321
322
323 CTDL_MODULE_UPGRADE(upgrade)
324 {
325         check_server_upgrades();
326         
327         /* return our module id for the Log */
328         return "upgrade";
329 }