"Fossies" - the Fresh Open Source Software Archive

Member "citadel/modules/upgrade/serv_upgrade.c" (5 Jun 2021, 16301 Bytes) of package /linux/www/citadel.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "serv_upgrade.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.01_vs_902.

    1 /*
    2  * Transparently handle the upgrading of server data formats.  If we see
    3  * an existing version number of our database, we can make some intelligent
    4  * guesses about what kind of data format changes need to be applied, and
    5  * we apply them transparently.
    6  *
    7  * Copyright (c) 1987-2020 by the citadel.org team
    8  *
    9  * This program is open source software; you can redistribute it and/or
   10  * modify it under the terms of the GNU General Public License version 3.
   11  *
   12  * This program is distributed in the hope that it will be useful,
   13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15  * GNU General Public License for more details.
   16  */
   17 
   18 #include "sysdep.h"
   19 #include <stdlib.h>
   20 #include <unistd.h>
   21 #include <stdio.h>
   22 #include <fcntl.h>
   23 #include <signal.h>
   24 #include <pwd.h>
   25 #include <errno.h>
   26 #include <sys/types.h>
   27 #include <time.h>
   28 #include <sys/wait.h>
   29 #include <string.h>
   30 #include <limits.h>
   31 #include <libcitadel.h>
   32 #include "citadel.h"
   33 #include "server.h"
   34 #include "citserver.h"
   35 #include "support.h"
   36 #include "config.h"
   37 #include "control.h"
   38 #include "database.h"
   39 #include "user_ops.h"
   40 #include "msgbase.h"
   41 #include "serv_upgrade.h"
   42 #include "euidindex.h"
   43 #include "ctdl_module.h"
   44 #include "serv_vcard.h"
   45 #include "internet_addressing.h"
   46 
   47 /*
   48  * oldver is the version number of Citadel Server which was active on the previous run of the program, learned from the system configuration.
   49  * If we are running a new Citadel Server for the first time, oldver will be 0.
   50  * We keep this value around for the entire duration of the program run because we'll need it during several stages of startup.
   51  */
   52 int oldver = 0;
   53 
   54 /*
   55  * 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     while (CtdlGetUserByNumber(&usbuf, 0) == 0) {
   63         /* delete user with number 0 and no name */
   64         if (IsEmptyStr(usbuf.fullname)) {
   65             cdb_delete(CDB_USERS, "", 0);
   66         }
   67         else {
   68             /* temporarily set this user to -1 */
   69             usbuf.usernum = -1;
   70             CtdlPutUser(&usbuf);
   71         }
   72     }
   73 
   74     /* Delete any "user 0" accounts */
   75     while (CtdlGetUserByNumber(&usbuf, -1) == 0) {
   76         makeuserkey(usernamekey, usbuf.fullname);
   77         cdb_delete(CDB_USERS, usernamekey, strlen(usernamekey));
   78     }
   79 }
   80 
   81 
   82 /* 
   83  * Back end processing function for reindex_uids()
   84  */
   85 void reindex_uids_backend(char *username, void *data) {
   86 
   87     struct ctdluser us;
   88 
   89     if (CtdlGetUserLock(&us, username) == 0) {
   90         syslog(LOG_DEBUG, "Processing <%s> (%d)", us.fullname, us.uid);
   91         if (us.uid == CTDLUID) {
   92             us.uid = NATIVE_AUTH_UID;
   93         }
   94         CtdlPutUserLock(&us);
   95         if ((us.uid > 0) && (us.uid != NATIVE_AUTH_UID)) {      // if non-native auth , index by uid
   96             StrBuf *claimed_id = NewStrBuf();
   97             StrBufPrintf(claimed_id, "uid:%d", us.uid);
   98             attach_extauth(&us, claimed_id);
   99             FreeStrBuf(&claimed_id);
  100         }
  101     }
  102 }
  103 
  104 
  105 /*
  106  * Build extauth index of all users with uid-based join (system auth, LDAP auth)
  107  * Also changes all users with a uid of CTDLUID to NATIVE_AUTH_UID (-1)
  108  */
  109 void reindex_uids(void) {
  110     syslog(LOG_WARNING, "upgrade: reindexing and applying uid changes");
  111     ForEachUser(reindex_uids_backend, NULL);
  112     return;
  113 }
  114 
  115 
  116 /*
  117  * These accounts may have been created by code that ran between mid 2008 and early 2011.
  118  * If present they are no longer in use and may be deleted.
  119  */
  120 void remove_thread_users(void) {
  121     char *deleteusers[] = {
  122         "SYS_checkpoint",
  123         "SYS_extnotify",
  124         "SYS_IGnet Queue",
  125         "SYS_indexer",
  126         "SYS_network",
  127         "SYS_popclient",
  128         "SYS_purger",
  129         "SYS_rssclient",
  130         "SYS_select_on_master",
  131         "SYS_SMTP Send"
  132     };
  133 
  134     int i;
  135     struct ctdluser usbuf;
  136     for (i=0; i<(sizeof(deleteusers)/sizeof(char *)); ++i) {
  137         if (CtdlGetUser(&usbuf, deleteusers[i]) == 0) {
  138             usbuf.axlevel = 0;
  139             strcpy(usbuf.password, "deleteme");
  140             CtdlPutUser(&usbuf);
  141             syslog(LOG_INFO,
  142                 "System user account <%s> is no longer in use and will be deleted.",
  143                 deleteusers[i]
  144             );
  145         }
  146     }
  147 }
  148 
  149 
  150 /*
  151  * Attempt to guess the name of the time zone currently in use
  152  * on the underlying host system.
  153  */
  154 void guess_time_zone(void) {
  155     FILE *fp;
  156     char buf[PATH_MAX];
  157 
  158     fp = popen(file_guesstimezone, "r");
  159     if (fp) {
  160         if (fgets(buf, sizeof buf, fp) && (strlen(buf) > 2)) {
  161             buf[strlen(buf)-1] = 0;
  162             CtdlSetConfigStr("c_default_cal_zone", buf);
  163             syslog(LOG_INFO, "Configuring timezone: %s", buf);
  164         }
  165         fclose(fp);
  166     }
  167 }
  168 
  169 
  170 /*
  171  * Per-room callback function for ingest_old_roominfo_and_roompic_files()
  172  *
  173  * This is the second pass, where we process the list of rooms with info or pic files.
  174  */
  175 void iorarf_oneroom(char *roomname, char *infofile, char *picfile)
  176 {
  177     FILE *fp;
  178     long data_length;
  179     char *unencoded_data;
  180     char *encoded_data;
  181     long info_msgnum = 0;
  182     long pic_msgnum = 0;
  183     char subject[SIZ];
  184 
  185     // Test for the presence of a legacy "room info file"
  186     if (!IsEmptyStr(infofile)) {
  187         fp = fopen(infofile, "r");
  188     }
  189     else {
  190         fp = NULL;
  191     }
  192     if (fp) {
  193         fseek(fp, 0, SEEK_END);
  194         data_length = ftell(fp);
  195 
  196         if (data_length >= 1) {
  197             rewind(fp);
  198             unencoded_data = malloc(data_length);
  199             if (unencoded_data) {
  200                 fread(unencoded_data, data_length, 1, fp);
  201                 encoded_data = malloc((data_length * 2) + 100);
  202                 if (encoded_data) {
  203                     sprintf(encoded_data, "Content-type: text/plain\nContent-transfer-encoding: base64\n\n");
  204                     CtdlEncodeBase64(&encoded_data[strlen(encoded_data)], unencoded_data, data_length, 1);
  205                     snprintf(subject, sizeof subject, "Imported room banner for %s", roomname);
  206                     info_msgnum = quickie_message("Citadel", NULL, NULL, SYSCONFIGROOM, encoded_data, FMT_RFC822, subject);
  207                     free(encoded_data);
  208                 }
  209                 free(unencoded_data);
  210             }
  211         }
  212         fclose(fp);
  213         if (info_msgnum > 0) unlink(infofile);
  214     }
  215 
  216     // Test for the presence of a legacy "room picture file" and import it.
  217     if (!IsEmptyStr(picfile)) {
  218         fp = fopen(picfile, "r");
  219     }
  220     else {
  221         fp = NULL;
  222     }
  223     if (fp) {
  224         fseek(fp, 0, SEEK_END);
  225         data_length = ftell(fp);
  226 
  227         if (data_length >= 1) {
  228             rewind(fp);
  229             unencoded_data = malloc(data_length);
  230             if (unencoded_data) {
  231                 fread(unencoded_data, data_length, 1, fp);
  232                 encoded_data = malloc((data_length * 2) + 100);
  233                 if (encoded_data) {
  234                     sprintf(encoded_data, "Content-type: image/gif\nContent-transfer-encoding: base64\n\n");
  235                     CtdlEncodeBase64(&encoded_data[strlen(encoded_data)], unencoded_data, data_length, 1);
  236                     snprintf(subject, sizeof subject, "Imported room icon for %s", roomname);
  237                     pic_msgnum = quickie_message("Citadel", NULL, NULL, SYSCONFIGROOM, encoded_data, FMT_RFC822, subject);
  238                     free(encoded_data);
  239                 }
  240                 free(unencoded_data);
  241             }
  242         }
  243         fclose(fp);
  244         if (pic_msgnum > 0) unlink(picfile);
  245     }
  246 
  247     // Now we have the message numbers of our new banner and icon.  Record them in the room record.
  248     // NOTE: we are not deleting the old msgnum_info because that position in the record was previously
  249     // a pointer to the highest message number which existed in the room when the info file was saved,
  250     // and we don't want to delete messages that are not *actually* old banners.
  251     struct ctdlroom qrbuf;
  252     if (CtdlGetRoomLock(&qrbuf, roomname) == 0) {
  253         qrbuf.msgnum_info = info_msgnum;
  254         qrbuf.msgnum_pic = pic_msgnum;
  255         CtdlPutRoomLock(&qrbuf);
  256     }
  257 
  258 }
  259 
  260 
  261 struct iorarf_list {
  262     struct iorarf_list *next;
  263     char name[ROOMNAMELEN];
  264     char info[PATH_MAX];
  265     char pic[PATH_MAX];
  266 };
  267 
  268 
  269 /*
  270  * Per-room callback function for ingest_old_roominfo_and_roompic_files()
  271  *
  272  * This is the first pass, where the list of qualifying rooms is gathered.
  273  */
  274 void iorarf_backend(struct ctdlroom *qrbuf, void *data)
  275 {
  276     FILE *fp;
  277     struct iorarf_list **iorarf_list = (struct iorarf_list **)data;
  278 
  279     struct iorarf_list *i = malloc(sizeof(struct iorarf_list));
  280     i->next = *iorarf_list;
  281     strcpy(i->name, qrbuf->QRname);
  282     strcpy(i->info, "");
  283     strcpy(i->pic, "");
  284 
  285     // Test for the presence of a legacy "room info file"
  286     assoc_file_name(i->info, sizeof i->info, qrbuf, ctdl_info_dir);
  287     fp = fopen(i->info, "r");
  288     if (fp) {
  289         fclose(fp);
  290     }
  291     else {
  292         i->info[0] = 0;
  293     }
  294 
  295     // Test for the presence of a legacy "room picture file"
  296     assoc_file_name(i->pic, sizeof i->pic, qrbuf, ctdl_image_dir);
  297     fp = fopen(i->pic, "r");
  298     if (fp) {
  299         fclose(fp);
  300     }
  301     else {
  302         i->pic[0] = 0;
  303     }
  304 
  305     if ( (!IsEmptyStr(i->info)) || (!IsEmptyStr(i->pic)) ) {
  306         *iorarf_list = i;
  307     }
  308     else {
  309         free(i);
  310     }
  311 }
  312 
  313 
  314 /*
  315  * Prior to Citadel Server version 902, room info and pictures (which comprise the
  316  * displayed banner for each room) were stored in the filesystem.  If we are upgrading
  317  * from version >000 to version >=902, ingest those files into the database.
  318  */
  319 void ingest_old_roominfo_and_roompic_files(void)
  320 {
  321     struct iorarf_list *il = NULL;
  322 
  323     CtdlForEachRoom(iorarf_backend, &il);
  324 
  325     struct iorarf_list *p;
  326     while (il) {
  327         iorarf_oneroom(il->name, il->info, il->pic);
  328         p = il->next;
  329         free(il);
  330         il = p;
  331     }
  332 
  333     unlink(ctdl_info_dir);
  334 }
  335 
  336 
  337 /*
  338  * For upgrades in which a new config setting appears for the first time, set default values.
  339  * For new installations (oldver == 0) also set default values.
  340  */
  341 void update_config(void) {
  342 
  343     if (oldver < 606) {
  344         CtdlSetConfigInt("c_rfc822_strict_from", 0);
  345     }
  346 
  347     if (oldver < 609) {
  348         CtdlSetConfigInt("c_purge_hour", 3);
  349     }
  350 
  351     if (oldver < 615) {
  352         CtdlSetConfigInt("c_ldap_port", 389);
  353     }
  354 
  355     if (oldver < 623) {
  356         CtdlSetConfigStr("c_ip_addr", "*");
  357     }
  358 
  359     if (oldver < 650) {
  360         CtdlSetConfigInt("c_enable_fulltext", 1);
  361     }
  362 
  363     if (oldver < 652) {
  364         CtdlSetConfigInt("c_auto_cull", 1);
  365     }
  366 
  367     if (oldver < 725) {
  368         CtdlSetConfigInt("c_xmpp_c2s_port", 5222);
  369         CtdlSetConfigInt("c_xmpp_s2s_port", 5269);
  370     }
  371 
  372     if (oldver < 830) {
  373         CtdlSetConfigInt("c_nntp_port", 119);
  374         CtdlSetConfigInt("c_nntps_port", 563);
  375     }
  376 
  377     if (IsEmptyStr(CtdlGetConfigStr("c_default_cal_zone"))) {
  378         guess_time_zone();
  379     }
  380 }
  381 
  382 
  383 /*
  384  * Helper function for move_inet_addrs_from_vcards_to_user_records()
  385  *
  386  * Call this function as a ForEachUser backend in order to queue up
  387  * user names, or call it with a null user to make it do the processing.
  388  * This allows us to maintain the list as a static instead of passing
  389  * pointers around.
  390  */
  391 void miafvtur_backend(char *username, void *data) {
  392     struct ctdluser usbuf;
  393     char primary_inet_email[512] = { 0 };
  394     char other_inet_emails[512] = { 0 };
  395     char combined_inet_emails[512] = { 0 };
  396 
  397     if (CtdlGetUser(&usbuf, username) != 0) {
  398         return;
  399     }
  400 
  401     struct vCard *v = vcard_get_user(&usbuf);
  402     if (!v) return;
  403     extract_inet_email_addrs(primary_inet_email, sizeof primary_inet_email, other_inet_emails, sizeof other_inet_emails, v, 1);
  404     vcard_free(v);
  405     
  406     if ( (IsEmptyStr(primary_inet_email)) && (IsEmptyStr(other_inet_emails)) ) {
  407         return;
  408     }
  409 
  410     snprintf(combined_inet_emails, 512, "%s%s%s",
  411         (!IsEmptyStr(primary_inet_email) ? primary_inet_email : ""),
  412         ((!IsEmptyStr(primary_inet_email)&&(!IsEmptyStr(other_inet_emails))) ? "|" : ""),
  413         (!IsEmptyStr(other_inet_emails) ? other_inet_emails : "")
  414     );
  415 
  416     CtdlSetEmailAddressesForUser(usbuf.fullname, combined_inet_emails);
  417 }
  418 
  419 
  420 /*
  421  * If our system still has a "refcount_adjustments.dat" sitting around from an old version, ingest it now.
  422  */
  423 int ProcessOldStyleAdjRefCountQueue(void)
  424 {
  425     int r;
  426     FILE *fp;
  427     struct arcq arcq_rec;
  428     int num_records_processed = 0;
  429 
  430     fp = fopen(file_arcq, "rb");
  431     if (fp == NULL) {
  432         return(num_records_processed);
  433     }
  434 
  435     syslog(LOG_INFO, "msgbase: ingesting %s", file_arcq);
  436 
  437     while (fread(&arcq_rec, sizeof(struct arcq), 1, fp) == 1) {
  438         AdjRefCount(arcq_rec.arcq_msgnum, arcq_rec.arcq_delta);
  439         ++num_records_processed;
  440     }
  441 
  442     fclose(fp);
  443     r = unlink(file_arcq);
  444     if (r != 0) {
  445         syslog(LOG_ERR, "%s: %m", file_arcq);
  446     }
  447 
  448     return(num_records_processed);
  449 }
  450 
  451 
  452 /*
  453  * Prior to version 912 we kept a user's various Internet email addresses in their vCards.
  454  * This function moves them over to the user record, which is where we keep them now.
  455  */
  456 void move_inet_addrs_from_vcards_to_user_records(void)
  457 {
  458     ForEachUser(miafvtur_backend, NULL);
  459     CtdlRebuildDirectoryIndex();
  460 }
  461 
  462 
  463 
  464 
  465 /*
  466  * We found the legacy sieve config in the user's config room.  Store the message number in the user record.
  467  */
  468 void mifm_found_config(long msgnum, void *userdata) {
  469     struct ctdluser *us = (struct ctdluser *)userdata;
  470 
  471     us->msgnum_inboxrules = msgnum;
  472     syslog(LOG_DEBUG, "user: <%s> inbox filter msgnum: <%ld>", us->fullname, us->msgnum_inboxrules);
  473 }
  474 
  475 
  476 /*
  477  * Helper function for migrate_inbox_filter_msgnums()
  478  */
  479 void mifm_backend(char *username, void *data) {
  480     struct ctdluser us;
  481     char roomname[ROOMNAMELEN];
  482 
  483     if (CtdlGetUserLock(&us, username) == 0) {
  484         /* Take a spin through the user's personal config room */
  485         syslog(LOG_DEBUG, "Processing <%s> (%ld)", us.fullname, us.usernum);
  486         snprintf(roomname, sizeof roomname, "%010ld.%s", us.usernum, USERCONFIGROOM);
  487         if (CtdlGetRoom(&CC->room, roomname) == 0) {
  488             CtdlForEachMessage(MSGS_LAST, 1, NULL, SIEVECONFIG, NULL, mifm_found_config, (void *)&us );
  489         }
  490         CtdlPutUserLock(&us);
  491     }
  492 }
  493 
  494 
  495 /*
  496  * Prior to version 930 we used a MIME type search to locate the user's inbox filter rules. 
  497  * This function locates those ruleset messages and simply stores the message number in the user record.
  498  */
  499 void migrate_inbox_filter_msgnums(void)
  500 {
  501     ForEachUser(mifm_backend, NULL);
  502 }
  503 
  504 
  505 /*
  506  * Create a default administrator account so we can log in to a new installation
  507  */
  508 void create_default_admin_account(void) {
  509     struct ctdluser usbuf;
  510 
  511     create_user(DEFAULT_ADMIN_USERNAME, CREATE_USER_DO_NOT_BECOME_USER, (-1));
  512     CtdlGetUser(&usbuf, DEFAULT_ADMIN_USERNAME);
  513     safestrncpy(usbuf.password, DEFAULT_ADMIN_PASSWORD, sizeof(usbuf.password));
  514     usbuf.axlevel = AxAideU;
  515     CtdlPutUser(&usbuf);
  516     CtdlSetConfigStr("c_sysadm", DEFAULT_ADMIN_USERNAME);
  517 }
  518 
  519 
  520 /*
  521  * Based on the server version number reported by the existing database,
  522  * run in-place data format upgrades until everything is up to date.
  523  */
  524 void pre_startup_upgrades(void) {
  525 
  526     oldver = CtdlGetConfigInt("MM_hosted_upgrade_level");
  527     syslog(LOG_INFO, "Existing database version on disk is %d", oldver);
  528     update_config();
  529 
  530     if (oldver < REV_LEVEL) {
  531         syslog(LOG_WARNING, "Running pre-startup database upgrades.");
  532     }
  533     else {
  534         return;
  535     }
  536 
  537     if ((oldver > 000) && (oldver < 591)) {
  538         syslog(LOG_EMERG, "This database is too old to be upgraded.  Citadel server will exit.");
  539         exit(EXIT_FAILURE);
  540     }
  541     if ((oldver > 000) && (oldver < 913)) {
  542         reindex_uids();
  543     }
  544     if ((oldver > 000) && (oldver < 659)) {
  545         rebuild_euid_index();
  546     }
  547     if (oldver < 735) {
  548         fix_sys_user_name();
  549     }
  550     if (oldver < 736) {
  551         rebuild_usersbynumber();
  552     }
  553     if (oldver < 790) {
  554         remove_thread_users();
  555     }
  556     if (oldver < 810) {
  557         struct ctdlroom QRoom;
  558         if (!CtdlGetRoom(&QRoom, SMTP_SPOOLOUT_ROOM)) {
  559             QRoom.QRdefaultview = VIEW_QUEUE;
  560             CtdlPutRoom(&QRoom);
  561         }
  562     }
  563 
  564     if ((oldver > 000) && (oldver < 902)) {
  565         ingest_old_roominfo_and_roompic_files();
  566     }
  567 
  568     CtdlSetConfigInt("MM_hosted_upgrade_level", REV_LEVEL);
  569 
  570     /*
  571      * Negative values for maxsessions are not allowed.
  572      */
  573     if (CtdlGetConfigInt("c_maxsessions") < 0) {
  574         CtdlSetConfigInt("c_maxsessions", 0);
  575     }
  576 
  577     /* We need a system default message expiry policy, because this is
  578      * the top level and there's no 'higher' policy to fall back on.
  579      * By default, do not expire messages at all.
  580      */
  581     if (CtdlGetConfigInt("c_ep_mode") == 0) {
  582         CtdlSetConfigInt("c_ep_mode", EXPIRE_MANUAL);
  583         CtdlSetConfigInt("c_ep_value", 0);
  584     }
  585 
  586     /*
  587      * If this is the first run on an empty database, create a default administrator
  588      */
  589     if (oldver == 0) {
  590         create_default_admin_account();
  591     }
  592 }
  593 
  594 
  595 /*
  596  * Based on the server version number reported by the existing database,
  597  * run in-place data format upgrades until everything is up to date.
  598  */
  599 void post_startup_upgrades(void) {
  600 
  601     syslog(LOG_INFO, "Existing database version on disk is %d", oldver);
  602 
  603     if (oldver < REV_LEVEL) {
  604         syslog(LOG_WARNING, "Running post-startup database upgrades.");
  605     }
  606     else {
  607         return;
  608     }
  609 
  610     if ((oldver > 000) && (oldver < 912)) {
  611         move_inet_addrs_from_vcards_to_user_records();
  612     }
  613 
  614     if ((oldver > 000) && (oldver < 922)) {
  615         ProcessOldStyleAdjRefCountQueue();
  616     }
  617 
  618     if ((oldver > 000) && (oldver < 930)) {
  619         migrate_inbox_filter_msgnums();
  620     }
  621 
  622 }
  623 
  624 
  625 CTDL_MODULE_UPGRADE(upgrade)
  626 {
  627     pre_startup_upgrades();
  628     
  629     /* return our module id for the Log */
  630     return "upgrade";
  631 }
  632 
  633 
  634 CTDL_MODULE_INIT(upgrade)
  635 {
  636     if (!threading) {
  637         post_startup_upgrades();
  638     }
  639 
  640     /* return our module name for the log */
  641         return "upgrade";
  642 }