Completed the overhaul of setup.
[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 #include <sys/utsname.h>
25
26 #if TIME_WITH_SYS_TIME
27 # include <sys/time.h>
28 # include <time.h>
29 #else
30 # if HAVE_SYS_TIME_H
31 #  include <sys/time.h>
32 # else
33 #  include <time.h>
34 # endif
35 #endif
36
37 #include <sys/wait.h>
38 #include <string.h>
39 #include <limits.h>
40 #include <libcitadel.h>
41 #include "citadel.h"
42 #include "server.h"
43 #include "citserver.h"
44 #include "support.h"
45 #include "config.h"
46 #include "control.h"
47 #include "database.h"
48 #include "user_ops.h"
49 #include "msgbase.h"
50 #include "serv_upgrade.h"
51 #include "euidindex.h"
52 #include "ctdl_module.h"
53
54
55 /*
56  * Fix up the name for Citadel user 0 and try to remove any extra users with number 0
57  */
58 void fix_sys_user_name(void)
59 {
60         struct ctdluser usbuf;
61         char usernamekey[USERNAME_SIZE];
62
63         /** If we have a user called Citadel rename them to SYS_Citadel */
64         if (CtdlGetUser(&usbuf, "Citadel") == 0)
65         {
66                 rename_user("Citadel", "SYS_Citadel");
67         }
68
69         while (CtdlGetUserByNumber(&usbuf, 0) == 0)
70         {
71                 /* delete user with number 0 and no name */
72                 if (IsEmptyStr(usbuf.fullname)) {
73                         cdb_delete(CDB_USERS, "", 0);
74                 }
75                 else {
76                         /* temporarily set this user to -1 */
77                         usbuf.usernum = -1;
78                         CtdlPutUser(&usbuf);
79                 }
80         }
81
82         /* Make sure user SYS_* is user 0 */
83         while (CtdlGetUserByNumber(&usbuf, -1) == 0)
84         {
85                 if (strncmp(usbuf.fullname, "SYS_", 4))
86                 {       /* Delete any user 0 that doesn't start with SYS_ */
87                         makeuserkey(usernamekey, usbuf.fullname, cutuserkey(usbuf.fullname));
88                         cdb_delete(CDB_USERS, usernamekey, strlen(usernamekey));
89                 }
90                 else {
91                         usbuf.usernum = 0;
92                         CtdlPutUser(&usbuf);
93                 }
94         }
95 }
96
97
98 /* 
99  * Back end processing function for cmd_bmbx
100  */
101 void cmd_bmbx_backend(struct ctdlroom *qrbuf, void *data) {
102         static struct RoomProcList *rplist = NULL;
103         struct RoomProcList *ptr;
104         struct ctdlroom qr;
105
106         /* Lazy programming here.  Call this function as a CtdlForEachRoom backend
107          * in order to queue up the room names, or call it with a null room
108          * to make it do the processing.
109          */
110         if (qrbuf != NULL) {
111                 ptr = (struct RoomProcList *) malloc(sizeof (struct RoomProcList));
112                 if (ptr == NULL) return;
113
114                 safestrncpy(ptr->name, qrbuf->QRname, sizeof ptr->name);
115                 ptr->next = rplist;
116                 rplist = ptr;
117                 return;
118         }
119
120         while (rplist != NULL) {
121
122                 if (CtdlGetRoomLock(&qr, rplist->name) == 0) {
123                         syslog(LOG_DEBUG, "Processing <%s>...", rplist->name);
124                         if ( (qr.QRflags & QR_MAILBOX) == 0) {
125                                 syslog(LOG_DEBUG, "  -- not a mailbox");
126                         }
127                         else {
128
129                                 qr.QRgen = time(NULL);
130                                 syslog(LOG_DEBUG, "  -- fixed!");
131                         }
132                         CtdlPutRoomLock(&qr);
133                 }
134
135                 ptr = rplist;
136                 rplist = rplist->next;
137                 free(ptr);
138         }
139 }
140
141 /*
142  * quick fix to bump mailbox generation numbers
143  */
144 void bump_mailbox_generation_numbers(void) {
145         syslog(LOG_WARNING, "Applying security fix to mailbox rooms");
146         CtdlForEachRoom(cmd_bmbx_backend, NULL);
147         cmd_bmbx_backend(NULL, NULL);
148         return;
149 }
150
151
152 /* 
153  * Back end processing function for convert_ctdluid_to_minusone()
154  */
155 void cbtm_backend(struct ctdluser *usbuf, void *data) {
156         static struct UserProcList *uplist = NULL;
157         struct UserProcList *ptr;
158         struct ctdluser us;
159
160         /* Lazy programming here.  Call this function as a ForEachUser backend
161          * in order to queue up the room names, or call it with a null user
162          * to make it do the processing.
163          */
164         if (usbuf != NULL) {
165                 ptr = (struct UserProcList *)
166                         malloc(sizeof (struct UserProcList));
167                 if (ptr == NULL) return;
168
169                 safestrncpy(ptr->user, usbuf->fullname, sizeof ptr->user);
170                 ptr->next = uplist;
171                 uplist = ptr;
172                 return;
173         }
174
175         while (uplist != NULL) {
176
177                 if (CtdlGetUserLock(&us, uplist->user) == 0) {
178                         syslog(LOG_DEBUG, "Processing <%s>...", uplist->user);
179                         if (us.uid == CTDLUID) {
180                                 us.uid = (-1);
181                         }
182                         CtdlPutUserLock(&us);
183                 }
184
185                 ptr = uplist;
186                 uplist = uplist->next;
187                 free(ptr);
188         }
189 }
190
191 /*
192  * quick fix to change all CTDLUID users to (-1)
193  */
194 void convert_ctdluid_to_minusone(void) {
195         syslog(LOG_WARNING, "Applying uid changes");
196         ForEachUser(cbtm_backend, NULL);
197         cbtm_backend(NULL, NULL);
198         return;
199 }
200
201
202
203 /*
204  * These accounts may have been created by code that ran between mid 2008 and early 2011.
205  * If present they are no longer in use and may be deleted.
206  */
207 void remove_thread_users(void) {
208         char *deleteusers[] = {
209                 "SYS_checkpoint",
210                 "SYS_extnotify",
211                 "SYS_IGnet Queue",
212                 "SYS_indexer",
213                 "SYS_network",
214                 "SYS_popclient",
215                 "SYS_purger",
216                 "SYS_rssclient",
217                 "SYS_select_on_master",
218                 "SYS_SMTP Send"
219         };
220
221         int i;
222         struct ctdluser usbuf;
223         for (i=0; i<(sizeof(deleteusers)/sizeof(char *)); ++i) {
224                 if (CtdlGetUser(&usbuf, deleteusers[i]) == 0) {
225                         usbuf.axlevel = 0;
226                         strcpy(usbuf.password, "deleteme");
227                         CtdlPutUser(&usbuf);
228                         syslog(LOG_INFO,
229                                 "System user account <%s> is no longer in use and will be deleted.",
230                                 deleteusers[i]
231                         );
232                 }
233         }
234 }
235
236
237 /*
238  * Attempt to guess the name of the time zone currently in use
239  * on the underlying host system.
240  */
241 void guess_time_zone(void) {
242         FILE *fp;
243         char buf[PATH_MAX];
244
245         fp = popen(file_guesstimezone, "r");
246         if (fp) {
247                 if (fgets(buf, sizeof buf, fp) && (strlen(buf) > 2)) {
248                         buf[strlen(buf)-1] = 0;
249                         safestrncpy(config.c_default_cal_zone, buf, sizeof config.c_default_cal_zone);
250                         syslog(LOG_INFO, "Configuring timezone: %s", config.c_default_cal_zone);
251                 }
252                 fclose(fp);
253         }
254 }
255
256
257 /*
258  * Put some sane default values into our configuration.  Some will be overridden when we run setup.
259  */
260 void brand_new_installation_set_defaults(void) {
261
262         struct passwd *pw;
263         struct utsname my_utsname;
264         struct hostent *he;
265
266         /* Determine our host name, in case we need to use it as a default */
267         uname(&my_utsname);
268
269         /* set some sample/default values in place of blanks... */
270         char c_nodename[256];
271         safestrncpy(c_nodename, my_utsname.nodename, sizeof c_nodename);
272         strtok(config.c_nodename, ".");
273         if (IsEmptyStr(config.c_fqdn) ) {
274                 if ((he = gethostbyname(my_utsname.nodename)) != NULL) {
275                         safestrncpy(config.c_fqdn, he->h_name, sizeof config.c_fqdn);
276                 }
277                 else {
278                         safestrncpy(config.c_fqdn, my_utsname.nodename, sizeof config.c_fqdn);
279                 }
280         }
281
282         safestrncpy(config.c_humannode, "Citadel Server", sizeof config.c_humannode);
283         safestrncpy(config.c_phonenum, "US 800 555 1212", sizeof config.c_phonenum);
284         config.c_initax = 4;
285         safestrncpy(config.c_moreprompt, "<more>", sizeof config.c_moreprompt);
286         safestrncpy(config.c_twitroom, "Trashcan", sizeof config.c_twitroom);
287         safestrncpy(config.c_baseroom, BASEROOM, sizeof config.c_baseroom);
288         safestrncpy(config.c_aideroom, "Aide", sizeof config.c_aideroom);
289         config.c_port_number = 504;
290         config.c_sleeping = 900;
291         config.c_instant_expunge = 1;
292
293         if (config.c_ctdluid == 0) {
294                 pw = getpwnam("citadel");
295                 if (pw != NULL) {
296                         config.c_ctdluid = pw->pw_uid;
297                 }
298         }
299         if (config.c_ctdluid == 0) {
300                 pw = getpwnam("bbs");
301                 if (pw != NULL) {
302                         config.c_ctdluid = pw->pw_uid;
303                 }
304         }
305         if (config.c_ctdluid == 0) {
306                 pw = getpwnam("guest");
307                 if (pw != NULL) {
308                         config.c_ctdluid = pw->pw_uid;
309                 }
310         }
311         if (config.c_createax == 0) {
312                 config.c_createax = 3;
313         }
314
315         /*
316          * Default port numbers for various services
317          */
318         config.c_smtp_port = 25;
319         config.c_pop3_port = 110;
320         config.c_imap_port = 143;
321         config.c_msa_port = 587;
322         config.c_smtps_port = 465;
323         config.c_pop3s_port = 995;
324         config.c_imaps_port = 993;
325         config.c_pftcpdict_port = -1 ;
326         config.c_managesieve_port = 2020;
327         config.c_xmpp_c2s_port = 5222;
328         config.c_xmpp_s2s_port = 5269;
329 }
330
331
332
333 /*
334  * Perform any upgrades that can be done automatically based on our knowledge of the previous
335  * version of Citadel server that was running here.
336  *
337  * Note that if the previous version was 0 then this is a new installation running for the first time.
338  */
339 void update_config(void) {
340         get_config();
341
342         if (CitControl.version == 0) {
343                 brand_new_installation_set_defaults();
344         }
345
346         if (CitControl.version < 606) {
347                 config.c_rfc822_strict_from = 0;
348         }
349
350         if (CitControl.version < 609) {
351                 config.c_purge_hour = 3;
352         }
353
354         if (CitControl.version < 615) {
355                 config.c_ldap_port = 389;
356         }
357
358         if (CitControl.version < 623) {
359                 strcpy(config.c_ip_addr, "*");
360         }
361
362         if (CitControl.version < 650) {
363                 config.c_enable_fulltext = 1;
364         }
365
366         if (CitControl.version < 652) {
367                 config.c_auto_cull = 1;
368         }
369
370         if (CitControl.version < 725) {
371                 config.c_xmpp_c2s_port = 5222;
372                 config.c_xmpp_s2s_port = 5269;
373         }
374
375         if (IsEmptyStr(config.c_default_cal_zone)) {
376                 guess_time_zone();
377         }
378
379         put_config();
380 }
381
382
383
384 /*
385  * Based on the server version number reported by the existing database,
386  * run in-place data format upgrades until everything is up to date.
387  */
388 void check_server_upgrades(void) {
389
390         get_control();
391         syslog(LOG_INFO, "Existing database version on disk is %d.%02d",
392                 (CitControl.version / 100),
393                 (CitControl.version % 100)
394         );
395
396         if (CitControl.version < REV_LEVEL) {
397                 syslog(LOG_WARNING,
398                         "Server hosted updates need to be processed at this time.  Please wait..."
399                 );
400         }
401         else {
402                 return;
403         }
404
405         update_config();
406
407         if ((CitControl.version > 000) && (CitControl.version < 555)) {
408                 syslog(LOG_EMERG, "This database is too old to be upgraded.  Citadel server will exit.");
409                 exit(EXIT_FAILURE);
410         }
411         if ((CitControl.version > 000) && (CitControl.version < 591)) {
412                 bump_mailbox_generation_numbers();
413         }
414         if ((CitControl.version > 000) && (CitControl.version < 608)) {
415                 convert_ctdluid_to_minusone();
416         }
417         if ((CitControl.version > 000) && (CitControl.version < 659)) {
418                 rebuild_euid_index();
419         }
420         if (CitControl.version < 735) {
421                 fix_sys_user_name();
422         }
423         if (CitControl.version < 736) {
424                 rebuild_usersbynumber();
425         }
426         if (CitControl.version < 790) {
427                 remove_thread_users();
428         }
429         CitControl.version = REV_LEVEL;
430
431         /*
432          * Negative values for maxsessions are not allowed.
433          */
434         if (config.c_maxsessions < 0) {
435                 config.c_maxsessions = 0;
436         }
437
438         /* We need a system default message expiry policy, because this is
439          * the top level and there's no 'higher' policy to fall back on.
440          * By default, do not expire messages at all.
441          */
442         if (config.c_ep.expire_mode == 0) {
443                 config.c_ep.expire_mode = EXPIRE_MANUAL;
444                 config.c_ep.expire_value = 0;
445         }
446
447         put_control();
448 }
449
450
451 CTDL_MODULE_UPGRADE(upgrade)
452 {
453         check_server_upgrades();
454         
455         /* return our module id for the Log */
456         return "upgrade";
457 }