"Fossies" - the Fresh Open Source Software Archive

Member "citadel/modules/migrate/serv_migrate.c" (5 Jun 2021, 35677 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_migrate.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.01_vs_902.

    1 // This module dumps and/or loads the Citadel database in XML format.
    2 //
    3 // Copyright (c) 1987-2021 by the citadel.org team
    4 //
    5 // This program is open source software; you can redistribute it and/or modify
    6 // it under the terms of the GNU General Public License version 3.
    7 //
    8 // This program is distributed in the hope that it will be useful,
    9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
   10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11 // GNU General Public License for more details.
   12 //
   13 // Explanation of <progress> tags:
   14 //
   15 // 0%       started
   16 // 2%       finished exporting configuration
   17 // 7%       finished exporting users
   18 // 12%      finished exporting openids
   19 // 17%      finished exporting rooms
   20 // 18%      finished exporting floors
   21 // 25%      finished exporting visits
   22 // 26-99%   exporting messages
   23 // 100%     finished exporting messages
   24 //
   25 // These tags are inserted into the XML stream to give the reader an approximation of its progress.
   26 
   27 #include "sysdep.h"
   28 #include <stdlib.h>
   29 #include <unistd.h>
   30 #include <stdio.h>
   31 #include <fcntl.h>
   32 #include <signal.h>
   33 #include <pwd.h>
   34 #include <errno.h>
   35 #include <sys/types.h>
   36 #include <time.h>
   37 #include <sys/wait.h>
   38 #include <string.h>
   39 #include <ctype.h>
   40 #include <limits.h>
   41 #include <expat.h>
   42 #include <libcitadel.h>
   43 #include "citadel.h"
   44 #include "server.h"
   45 #include "citserver.h"
   46 #include "support.h"
   47 #include "config.h"
   48 #include "database.h"
   49 #include "msgbase.h"
   50 #include "user_ops.h"
   51 #include "euidindex.h"
   52 #include "ctdl_module.h"
   53 
   54 char migr_tempfilename1[PATH_MAX];
   55 char migr_tempfilename2[PATH_MAX];
   56 FILE *migr_global_message_list;
   57 int total_msgs = 0;
   58 char *ikey = NULL;          // If we're importing a config key we store it here.
   59 
   60 //*****************************************************************************
   61 //*       Code which implements the export appears in this section            *
   62 //*****************************************************************************
   63 
   64 // Output a string to the client with these characters escaped:  & < > " '
   65 void xml_strout(char *str) {
   66 
   67     char *c = str;
   68 
   69     if (str == NULL) {
   70         return;
   71     }
   72 
   73     while (*c != 0) {
   74         if (*c == '\"') {
   75             client_write(HKEY("&quot;"));
   76         }
   77         else if (*c == '\'') {
   78             client_write(HKEY("&apos;"));
   79         }
   80         else if (*c == '<') {
   81             client_write(HKEY("&lt;"));
   82         }
   83         else if (*c == '>') {
   84             client_write(HKEY("&gt;"));
   85         }
   86         else if (*c == '&') {
   87             client_write(HKEY("&amp;"));
   88         }
   89         else {
   90             client_write(c, 1);
   91         }
   92         ++c;
   93     }
   94 }
   95 
   96 
   97 // Export a user record as XML
   98 void migr_export_users_backend(char *username, void *data) {
   99 
  100     struct ctdluser u;
  101     if (CtdlGetUser(&u, username) != 0) {
  102         return;
  103     }
  104 
  105     client_write(HKEY("<user>\n"));
  106     cprintf("<u_version>%d</u_version>\n", u.version);
  107     cprintf("<u_uid>%ld</u_uid>\n", (long)u.uid);
  108     client_write(HKEY("<u_password>")); xml_strout(u.password);     client_write(HKEY("</u_password>\n"));
  109     cprintf("<u_flags>%u</u_flags>\n", u.flags);
  110     cprintf("<u_timescalled>%ld</u_timescalled>\n", u.timescalled);
  111     cprintf("<u_posted>%ld</u_posted>\n", u.posted);
  112     cprintf("<u_axlevel>%d</u_axlevel>\n", u.axlevel);
  113     cprintf("<u_usernum>%ld</u_usernum>\n", u.usernum);
  114     cprintf("<u_lastcall>%ld</u_lastcall>\n", (long)u.lastcall);
  115     cprintf("<u_USuserpurge>%d</u_USuserpurge>\n", u.USuserpurge);
  116     client_write(HKEY("<u_fullname>")); xml_strout(u.fullname);     client_write(HKEY("</u_fullname>\n"));
  117     cprintf("<u_msgnum_bio>%ld</u_msgnum_bio>\n", u.msgnum_bio);
  118     cprintf("<u_msgnum_pic>%ld</u_msgnum_pic>\n", u.msgnum_pic);
  119     cprintf("<u_emailaddrs>%s</u_emailaddrs>\n", u.emailaddrs);
  120     cprintf("<u_msgnum_inboxrules>%ld</u_msgnum_inboxrules>\n", u.msgnum_inboxrules);
  121     cprintf("<u_lastproc_inboxrules>%ld</u_lastproc_inboxrules>\n", u.lastproc_inboxrules);
  122     client_write(HKEY("</user>\n"));
  123 }
  124 
  125 
  126 void migr_export_users(void) {
  127     ForEachUser(migr_export_users_backend, NULL);
  128 }
  129 
  130 
  131 void migr_export_room_msg(long msgnum, void *userdata) {
  132     static int count = 0;
  133 
  134     cprintf("%ld,", msgnum);
  135     if (++count%10==0) {
  136         cprintf("\n");
  137     }
  138     fprintf(migr_global_message_list, "%ld\n", msgnum);
  139 }
  140 
  141 
  142 void migr_export_rooms_backend(struct ctdlroom *buf, void *data) {
  143     client_write(HKEY("<room>\n"));
  144     client_write(HKEY("<QRname>"));
  145     xml_strout(buf->QRname);
  146     client_write(HKEY("</QRname>\n"));
  147     client_write(HKEY("<QRpasswd>"));
  148     xml_strout(buf->QRpasswd);
  149     client_write(HKEY("</QRpasswd>\n"));
  150     cprintf("<QRroomaide>%ld</QRroomaide>\n", buf->QRroomaide);
  151     cprintf("<QRhighest>%ld</QRhighest>\n", buf->QRhighest);
  152     cprintf("<QRgen>%ld</QRgen>\n", (long)buf->QRgen);
  153     cprintf("<QRflags>%u</QRflags>\n", buf->QRflags);
  154     if (buf->QRflags & QR_DIRECTORY) {
  155         client_write(HKEY("<QRdirname>"));
  156         xml_strout(buf->QRdirname);
  157         client_write(HKEY("</QRdirname>\n"));
  158     }
  159     cprintf("<QRfloor>%d</QRfloor>\n", buf->QRfloor);
  160     cprintf("<QRmtime>%ld</QRmtime>\n", (long)buf->QRmtime);
  161     cprintf("<QRexpire_mode>%d</QRexpire_mode>\n", buf->QRep.expire_mode);
  162     cprintf("<QRexpire_value>%d</QRexpire_value>\n", buf->QRep.expire_value);
  163     cprintf("<QRnumber>%ld</QRnumber>\n", buf->QRnumber);
  164     cprintf("<QRorder>%d</QRorder>\n", buf->QRorder);
  165     cprintf("<QRflags2>%u</QRflags2>\n", buf->QRflags2);
  166     cprintf("<QRdefaultview>%d</QRdefaultview>\n", buf->QRdefaultview);
  167     cprintf("<msgnum_info>%ld</msgnum_info>\n", buf->msgnum_info);
  168     cprintf("<msgnum_pic>%ld</msgnum_pic>\n", buf->msgnum_pic);
  169     client_write(HKEY("</room>\n"));
  170 
  171     // message list goes inside this tag
  172     CtdlGetRoom(&CC->room, buf->QRname);
  173     client_write(HKEY("<room_messages>"));
  174     client_write(HKEY("<FRname>"));
  175     xml_strout(buf->QRname);            // buf->QRname rather than CC->room.QRname to guarantee consistency
  176     client_write(HKEY("</FRname>\n"));
  177     client_write(HKEY("<FRmsglist>\n"));
  178     CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL, migr_export_room_msg, NULL);
  179     client_write(HKEY("</FRmsglist>\n"));
  180     client_write(HKEY("</room_messages>\n"));
  181 }
  182 
  183 
  184 void migr_export_rooms(void) {
  185     char cmd[SIZ];
  186     migr_global_message_list = fopen(migr_tempfilename1, "w");
  187     if (migr_global_message_list != NULL) {
  188         CtdlForEachRoom(migr_export_rooms_backend, NULL);
  189         fclose(migr_global_message_list);
  190     }
  191 
  192     // Process the 'global' message list.  (Sort it and remove dups.
  193     // Dups are ok because a message may be in more than one room, but
  194     // this will be handled by exporting the reference count, not by
  195     // exporting the message multiple times.)
  196     snprintf(cmd, sizeof cmd, "sort -n <%s >%s", migr_tempfilename1, migr_tempfilename2);
  197     if (system(cmd) != 0) {
  198         syslog(LOG_ERR, "migrate: error %d", errno);
  199     }
  200     snprintf(cmd, sizeof cmd, "uniq <%s >%s", migr_tempfilename2, migr_tempfilename1);
  201     if (system(cmd) != 0) {
  202         syslog(LOG_ERR, "migrate: error %d", errno);
  203     }
  204 
  205     snprintf(cmd, sizeof cmd, "wc -l %s", migr_tempfilename1);
  206     FILE *fp = popen(cmd, "r");
  207     if (fp) {
  208         fgets(cmd, sizeof cmd, fp);
  209         pclose(fp);
  210         total_msgs = atoi(cmd);
  211     }
  212     else {
  213         total_msgs = 1; // any nonzero just to keep it from barfing
  214     }
  215     syslog(LOG_DEBUG, "migrate: total messages to be exported: %d", total_msgs);
  216 }
  217 
  218 
  219 void migr_export_floors(void) {
  220         struct floor qfbuf, *buf;
  221         int i;
  222 
  223         for (i=0; i < MAXFLOORS; ++i) {
  224         client_write(HKEY("<floor>\n"));
  225         cprintf("<f_num>%d</f_num>\n", i);
  226                 CtdlGetFloor(&qfbuf, i);
  227         buf = &qfbuf;
  228         cprintf("<f_flags>%u</f_flags>\n", buf->f_flags);
  229         client_write(HKEY("<f_name>")); xml_strout(buf->f_name); client_write(HKEY("</f_name>\n"));
  230         cprintf("<f_ref_count>%d</f_ref_count>\n", buf->f_ref_count);
  231         cprintf("<f_ep_expire_mode>%d</f_ep_expire_mode>\n", buf->f_ep.expire_mode);
  232         cprintf("<f_ep_expire_value>%d</f_ep_expire_value>\n", buf->f_ep.expire_value);
  233         client_write(HKEY("</floor>\n"));
  234     }
  235 }
  236 
  237 
  238 // Return nonzero if the supplied string contains only characters which are valid in a sequence set.
  239 int is_sequence_set(char *s) {
  240     if (!s) return(0);
  241 
  242     char *c = s;
  243     char ch;
  244     while (ch = *c++, ch) {
  245         if (!strchr("0123456789*,:", ch)) {
  246             return(0);
  247         }
  248     }
  249     return(1);
  250 }
  251 
  252 
  253 // Traverse the visits file...
  254 void migr_export_visits(void) {
  255     visit vbuf;
  256     struct cdbdata *cdbv;
  257 
  258     cdb_rewind(CDB_VISIT);
  259 
  260     while (cdbv = cdb_next_item(CDB_VISIT), cdbv != NULL) {
  261         memset(&vbuf, 0, sizeof(visit));
  262         memcpy(&vbuf, cdbv->ptr,
  263                ((cdbv->len > sizeof(visit)) ?
  264             sizeof(visit) : cdbv->len));
  265         cdb_free(cdbv);
  266 
  267         client_write(HKEY("<visit>\n"));
  268         cprintf("<v_roomnum>%ld</v_roomnum>\n", vbuf.v_roomnum);
  269         cprintf("<v_roomgen>%ld</v_roomgen>\n", vbuf.v_roomgen);
  270         cprintf("<v_usernum>%ld</v_usernum>\n", vbuf.v_usernum);
  271 
  272         client_write(HKEY("<v_seen>"));
  273         if ( (!IsEmptyStr(vbuf.v_seen)) && (is_sequence_set(vbuf.v_seen)) ) {
  274             xml_strout(vbuf.v_seen);
  275         }
  276         else {
  277             cprintf("%ld", vbuf.v_lastseen);
  278         }
  279         client_write(HKEY("</v_seen>"));
  280 
  281         if ( (!IsEmptyStr(vbuf.v_answered)) && (is_sequence_set(vbuf.v_answered)) ) {
  282             client_write(HKEY("<v_answered>"));
  283             xml_strout(vbuf.v_answered);
  284             client_write(HKEY("</v_answered>\n"));
  285         }
  286 
  287         cprintf("<v_flags>%u</v_flags>\n", vbuf.v_flags);
  288         cprintf("<v_view>%d</v_view>\n", vbuf.v_view);
  289         client_write(HKEY("</visit>\n"));
  290     }
  291 }
  292 
  293 
  294 void migr_export_message(long msgnum) {
  295     struct MetaData smi;
  296     struct CtdlMessage *msg;
  297     struct ser_ret smr;
  298     long bytes_written = 0;
  299     long this_block = 0;
  300 
  301     // We can use a static buffer here because there will never be more than
  302     // one of this operation happening at any given time, and it's really best
  303     // to just keep it allocated once instead of torturing malloc/free.
  304     // Call this function with msgnum "-1" to free the buffer when finished.
  305 
  306     static int encoded_alloc = 0;
  307     static char *encoded_msg = NULL;
  308 
  309     if (msgnum < 0) {
  310         if ((encoded_alloc == 0) && (encoded_msg != NULL)) {
  311             free(encoded_msg);
  312             encoded_alloc = 0;
  313             encoded_msg = NULL;
  314         }
  315         return;
  316     }
  317 
  318     // Ok, here we go ...
  319 
  320     msg = CtdlFetchMessage(msgnum, 1);
  321     if (msg == NULL) return;                // fail silently
  322 
  323     client_write(HKEY("<message>\n"));
  324     GetMetaData(&smi, msgnum);
  325     cprintf("<msg_msgnum>%ld</msg_msgnum>\n", msgnum);
  326     cprintf("<msg_meta_refcount>%d</msg_meta_refcount>\n", smi.meta_refcount);
  327     cprintf("<msg_meta_rfc822_length>%ld</msg_meta_rfc822_length>\n", smi.meta_rfc822_length);
  328     client_write(HKEY("<msg_meta_content_type>")); xml_strout(smi.meta_content_type); client_write(HKEY("</msg_meta_content_type>\n"));
  329 
  330     client_write(HKEY("<msg_text>"));
  331     CtdlSerializeMessage(&smr, msg);
  332     CM_Free(msg);
  333 
  334     // Predict the buffer size we need.  Expand the buffer if necessary.
  335     int encoded_len = smr.len * 15 / 10 ;
  336     if (encoded_len > encoded_alloc) {
  337         encoded_alloc = encoded_len;
  338         encoded_msg = realloc(encoded_msg, encoded_alloc);
  339     }
  340 
  341     if (encoded_msg == NULL) {
  342         // Questionable hack that hopes it'll work next time and we only lose one message
  343         encoded_alloc = 0;
  344     }
  345     else {
  346         // Once we do the encoding we know the exact size
  347         encoded_len = CtdlEncodeBase64(encoded_msg, (char *)smr.ser, smr.len, 1);
  348 
  349         // Careful now.  If the message is gargantuan, trying to write multiple gigamegs in one
  350         // big write operation can make our transport unhappy.  So we'll chunk it up 10 KB at a time.
  351         bytes_written = 0;
  352         while ( (bytes_written < encoded_len) && (!server_shutting_down) ) {
  353             this_block = encoded_len - bytes_written;
  354             if (this_block > 10240) {
  355                 this_block = 10240;
  356             }
  357             client_write(&encoded_msg[bytes_written], this_block);
  358             bytes_written += this_block;
  359         }
  360     }
  361 
  362     free(smr.ser);
  363 
  364     client_write(HKEY("</msg_text>\n"));
  365     client_write(HKEY("</message>\n"));
  366 }
  367 
  368 
  369 void migr_export_openids(void) {
  370     struct cdbdata *cdboi;
  371     long usernum;
  372     char url[512];
  373 
  374     cdb_rewind(CDB_EXTAUTH);
  375     while (cdboi = cdb_next_item(CDB_EXTAUTH), cdboi != NULL) {
  376         if (cdboi->len > sizeof(long)) {
  377             client_write(HKEY("<openid>\n"));
  378             memcpy(&usernum, cdboi->ptr, sizeof(long));
  379             snprintf(url, sizeof url, "%s", (cdboi->ptr)+sizeof(long) );
  380             client_write(HKEY("<oid_url>"));
  381             xml_strout(url);
  382             client_write(HKEY("</oid_url>\n"));
  383             cprintf("<oid_usernum>%ld</oid_usernum>\n", usernum);
  384             client_write(HKEY("</openid>\n"));
  385         }
  386         cdb_free(cdboi);
  387     }
  388 }
  389 
  390 
  391 void migr_export_configs(void) {
  392     struct cdbdata *cdbcfg;
  393     int keylen = 0;
  394     char *key = NULL;
  395     char *value = NULL;
  396 
  397     cdb_rewind(CDB_CONFIG);
  398     while (cdbcfg = cdb_next_item(CDB_CONFIG), cdbcfg != NULL) {
  399 
  400         keylen = strlen(cdbcfg->ptr);
  401         key = cdbcfg->ptr;
  402         value = cdbcfg->ptr + keylen + 1;
  403 
  404         client_write("<config key=\"", 13);
  405         xml_strout(key);
  406         client_write("\">", 2);
  407         xml_strout(value);
  408         client_write("</config>\n", 10);
  409         cdb_free(cdbcfg);
  410     }
  411 }
  412 
  413 
  414 void migr_export_messages(void) {
  415     char buf[SIZ];
  416     long msgnum;
  417     int count = 0;
  418     int progress = 0;
  419     int prev_progress = 0;
  420     CitContext *Ctx;
  421 
  422     Ctx = CC;
  423     migr_global_message_list = fopen(migr_tempfilename1, "r");
  424     if (migr_global_message_list != NULL) {
  425         syslog(LOG_INFO, "migrate: opened %s", migr_tempfilename1);
  426         while ((Ctx->kill_me == 0) && 
  427                (fgets(buf, sizeof(buf), migr_global_message_list) != NULL)) {
  428             msgnum = atol(buf);
  429             if (msgnum > 0L) {
  430                 migr_export_message(msgnum);
  431                 ++count;
  432             }
  433             progress = (count * 74 / total_msgs) + 25 ;
  434             if ((progress > prev_progress) && (progress < 100)) {
  435                 cprintf("<progress>%d</progress>\n", progress);
  436             }
  437             prev_progress = progress;
  438         }
  439         fclose(migr_global_message_list);
  440     }
  441     if (Ctx->kill_me == 0) {
  442         syslog(LOG_INFO, "migrate: exported %d messages.", count);
  443     }
  444     else {
  445         syslog(LOG_ERR, "migrate: export aborted due to client disconnect!");
  446     }
  447 
  448     migr_export_message(-1L);   // This frees the encoding buffer
  449 }
  450 
  451 
  452 void migr_do_export(void) {
  453     CitContext *Ctx;
  454 
  455     Ctx = CC;
  456     cprintf("%d Exporting all Citadel databases.\n", LISTING_FOLLOWS);
  457     Ctx->dont_term = 1;
  458 
  459     client_write(HKEY("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"));
  460     client_write(HKEY("<citadel_migrate_data>\n"));
  461     cprintf("<version>%d</version>\n", REV_LEVEL);
  462     cprintf("<progress>%d</progress>\n", 0);
  463 
  464     // export the configuration database
  465     migr_export_configs();
  466     cprintf("<progress>%d</progress>\n", 2);
  467     
  468     if (Ctx->kill_me == 0)  migr_export_users();
  469     cprintf("<progress>%d</progress>\n", 7);
  470     if (Ctx->kill_me == 0)  migr_export_openids();
  471     cprintf("<progress>%d</progress>\n", 12);
  472     if (Ctx->kill_me == 0)  migr_export_rooms();
  473     cprintf("<progress>%d</progress>\n", 17);
  474     if (Ctx->kill_me == 0)  migr_export_floors();
  475     cprintf("<progress>%d</progress>\n", 18);
  476     if (Ctx->kill_me == 0)  migr_export_visits();
  477     cprintf("<progress>%d</progress>\n", 25);
  478     if (Ctx->kill_me == 0)  migr_export_messages();
  479     cprintf("<progress>%d</progress>\n", 100);
  480     client_write(HKEY("</citadel_migrate_data>\n"));
  481     client_write(HKEY("000\n"));
  482     Ctx->dont_term = 0;
  483 }
  484 
  485 
  486 //                              Import code
  487 //   Here's the code that implements the import side.  It's going to end up
  488 //       being one big loop with lots of global variables.  I don't care.
  489 // You wouldn't run multiple concurrent imports anyway.  If this offends your
  490 //  delicate sensibilities  then go rewrite it in Ruby on Rails or something.
  491 
  492 
  493 int citadel_migrate_data = 0;       // Are we inside a <citadel_migrate_data> tag pair?
  494 StrBuf *migr_chardata = NULL;
  495 StrBuf *migr_MsgData = NULL;
  496 struct ctdluser usbuf;
  497 struct ctdlroom qrbuf;
  498 char openid_url[512];
  499 long openid_usernum = 0;
  500 char FRname[ROOMNAMELEN];
  501 struct floor flbuf;
  502 int floornum = 0;
  503 visit vbuf;
  504 struct MetaData smi;
  505 long import_msgnum = 0;
  506 
  507 // This callback stores up the data which appears in between tags.
  508 void migr_xml_chardata(void *data, const XML_Char *s, int len) {
  509     StrBufAppendBufPlain(migr_chardata, s, len, 0);
  510 }
  511 
  512 
  513 void migr_xml_start(void *data, const char *el, const char **attr) {
  514     int i;
  515 
  516     //  *** GENERAL STUFF ***
  517 
  518     // Reset chardata_len to zero and init buffer
  519     FlushStrBuf(migr_chardata);
  520     FlushStrBuf(migr_MsgData);
  521 
  522     if (!strcasecmp(el, "citadel_migrate_data")) {
  523         ++citadel_migrate_data;
  524 
  525         // As soon as it looks like the input data is a genuine Citadel XML export,
  526         // whack the existing database on disk to make room for the new one.
  527         if (citadel_migrate_data == 1) {
  528             syslog(LOG_INFO, "migrate: erasing existing databases so we can receive the incoming import");
  529             for (i = 0; i < MAXCDB; ++i) {
  530                 cdb_trunc(i);
  531             }
  532         }
  533         return;
  534     }
  535 
  536     if (citadel_migrate_data != 1) {
  537         syslog(LOG_ERR, "migrate: out-of-sequence tag <%s> detected.  Warning: ODD-DATA!", el);
  538         return;
  539     }
  540 
  541     // When we begin receiving XML for one of these record types, clear out the associated
  542     // buffer so we don't accidentally carry over any data from a previous record.
  543     if (!strcasecmp(el, "user"))            memset(&usbuf, 0, sizeof (struct ctdluser));
  544     else if (!strcasecmp(el, "openid"))     memset(openid_url, 0, sizeof openid_url);
  545     else if (!strcasecmp(el, "room"))       memset(&qrbuf, 0, sizeof (struct ctdlroom));
  546     else if (!strcasecmp(el, "room_messages"))  memset(FRname, 0, sizeof FRname);
  547     else if (!strcasecmp(el, "floor"))      memset(&flbuf, 0, sizeof (struct floor));
  548     else if (!strcasecmp(el, "visit"))      memset(&vbuf, 0, sizeof (visit));
  549 
  550     else if (!strcasecmp(el, "message")) {
  551         memset(&smi, 0, sizeof (struct MetaData));
  552         import_msgnum = 0;
  553     }
  554     else if (!strcasecmp(el, "config")) {
  555         if (ikey != NULL) {
  556             free(ikey);
  557             ikey = NULL;
  558         }
  559         while (*attr) {
  560             if (!strcasecmp(attr[0], "key")) {
  561                 ikey = strdup(attr[1]);
  562             }
  563             attr += 2;
  564         }
  565     }
  566 
  567 }
  568 
  569 
  570 int migr_userrecord(void *data, const char *el) {
  571     if (!strcasecmp(el, "u_version"))           usbuf.version = atoi(ChrPtr(migr_chardata));
  572     else if (!strcasecmp(el, "u_uid"))          usbuf.uid = atol(ChrPtr(migr_chardata));
  573     else if (!strcasecmp(el, "u_password"))         safestrncpy(usbuf.password, ChrPtr(migr_chardata), sizeof usbuf.password);
  574     else if (!strcasecmp(el, "u_flags"))            usbuf.flags = atoi(ChrPtr(migr_chardata));
  575     else if (!strcasecmp(el, "u_timescalled"))      usbuf.timescalled = atol(ChrPtr(migr_chardata));
  576     else if (!strcasecmp(el, "u_posted"))           usbuf.posted = atol(ChrPtr(migr_chardata));
  577     else if (!strcasecmp(el, "u_axlevel"))          usbuf.axlevel = atoi(ChrPtr(migr_chardata));
  578     else if (!strcasecmp(el, "u_usernum"))          usbuf.usernum = atol(ChrPtr(migr_chardata));
  579     else if (!strcasecmp(el, "u_lastcall"))         usbuf.lastcall = atol(ChrPtr(migr_chardata));
  580     else if (!strcasecmp(el, "u_USuserpurge"))      usbuf.USuserpurge = atoi(ChrPtr(migr_chardata));
  581     else if (!strcasecmp(el, "u_fullname"))         safestrncpy(usbuf.fullname, ChrPtr(migr_chardata), sizeof usbuf.fullname);
  582     else if (!strcasecmp(el, "u_msgnum_bio"))       usbuf.msgnum_bio = atol(ChrPtr(migr_chardata));
  583     else if (!strcasecmp(el, "u_msgnum_pic"))       usbuf.msgnum_pic = atol(ChrPtr(migr_chardata));
  584     else if (!strcasecmp(el, "u_emailaddrs"))       safestrncpy(usbuf.emailaddrs, ChrPtr(migr_chardata), sizeof usbuf.emailaddrs);
  585     else if (!strcasecmp(el, "u_msgnum_inboxrules"))    usbuf.msgnum_inboxrules = atol(ChrPtr(migr_chardata));
  586     else if (!strcasecmp(el, "u_lastproc_inboxrules"))  usbuf.lastproc_inboxrules = atol(ChrPtr(migr_chardata));
  587     else return 0;
  588     return 1;
  589 }
  590 
  591 
  592 int migr_roomrecord(void *data, const char *el) {
  593     if (!strcasecmp(el, "QRname"))              safestrncpy(qrbuf.QRname, ChrPtr(migr_chardata), sizeof qrbuf.QRname);
  594     else if (!strcasecmp(el, "QRpasswd"))           safestrncpy(qrbuf.QRpasswd, ChrPtr(migr_chardata), sizeof qrbuf.QRpasswd);
  595     else if (!strcasecmp(el, "QRroomaide"))         qrbuf.QRroomaide = atol(ChrPtr(migr_chardata));
  596     else if (!strcasecmp(el, "QRhighest"))          qrbuf.QRhighest = atol(ChrPtr(migr_chardata));
  597     else if (!strcasecmp(el, "QRgen"))          qrbuf.QRgen = atol(ChrPtr(migr_chardata));
  598     else if (!strcasecmp(el, "QRflags"))            qrbuf.QRflags = atoi(ChrPtr(migr_chardata));
  599     else if (!strcasecmp(el, "QRdirname"))          safestrncpy(qrbuf.QRdirname, ChrPtr(migr_chardata), sizeof qrbuf.QRdirname);
  600     else if (!strcasecmp(el, "QRfloor"))            qrbuf.QRfloor = atoi(ChrPtr(migr_chardata));
  601     else if (!strcasecmp(el, "QRmtime"))            qrbuf.QRmtime = atol(ChrPtr(migr_chardata));
  602     else if (!strcasecmp(el, "QRexpire_mode"))      qrbuf.QRep.expire_mode = atoi(ChrPtr(migr_chardata));
  603     else if (!strcasecmp(el, "QRexpire_value"))     qrbuf.QRep.expire_value = atoi(ChrPtr(migr_chardata));
  604     else if (!strcasecmp(el, "QRnumber"))           qrbuf.QRnumber = atol(ChrPtr(migr_chardata));
  605     else if (!strcasecmp(el, "QRorder"))            qrbuf.QRorder = atoi(ChrPtr(migr_chardata));
  606     else if (!strcasecmp(el, "QRflags2"))           qrbuf.QRflags2 = atoi(ChrPtr(migr_chardata));
  607     else if (!strcasecmp(el, "QRdefaultview"))      qrbuf.QRdefaultview = atoi(ChrPtr(migr_chardata));
  608     else if (!strcasecmp(el, "msgnum_info"))        qrbuf.msgnum_info = atol(ChrPtr(migr_chardata));
  609     else if (!strcasecmp(el, "msgnum_pic"))         qrbuf.msgnum_pic = atol(ChrPtr(migr_chardata));
  610     else return 0;
  611     return 1;
  612 }
  613 
  614 
  615 int migr_floorrecord(void *data, const char *el) {
  616     if (!strcasecmp(el, "f_num"))               floornum = atoi(ChrPtr(migr_chardata));
  617     else if (!strcasecmp(el, "f_flags"))            flbuf.f_flags = atoi(ChrPtr(migr_chardata));
  618     else if (!strcasecmp(el, "f_name"))         safestrncpy(flbuf.f_name, ChrPtr(migr_chardata), sizeof flbuf.f_name);
  619     else if (!strcasecmp(el, "f_ref_count"))        flbuf.f_ref_count = atoi(ChrPtr(migr_chardata));
  620     else if (!strcasecmp(el, "f_ep_expire_mode"))       flbuf.f_ep.expire_mode = atoi(ChrPtr(migr_chardata));
  621     else if (!strcasecmp(el, "f_ep_expire_value"))      flbuf.f_ep.expire_value = atoi(ChrPtr(migr_chardata));
  622     else return 0;
  623     return 1;
  624 }
  625 
  626 
  627 int migr_visitrecord(void *data, const char *el) {
  628     if (!strcasecmp(el, "v_roomnum"))           vbuf.v_roomnum = atol(ChrPtr(migr_chardata));
  629     else if (!strcasecmp(el, "v_roomgen"))          vbuf.v_roomgen = atol(ChrPtr(migr_chardata));
  630     else if (!strcasecmp(el, "v_usernum"))          vbuf.v_usernum = atol(ChrPtr(migr_chardata));
  631 
  632     else if (!strcasecmp(el, "v_seen")) {
  633         int is_textual_seen = 0;
  634         int i;
  635         int max = StrLength(migr_chardata);
  636 
  637         vbuf.v_lastseen = atol(ChrPtr(migr_chardata));
  638         is_textual_seen = 0;
  639         for (i=0; i < max; ++i) 
  640             if (!isdigit(ChrPtr(migr_chardata)[i]))
  641                 is_textual_seen = 1;
  642         if (is_textual_seen)
  643             safestrncpy(vbuf.v_seen, ChrPtr(migr_chardata), sizeof vbuf.v_seen);
  644     }
  645 
  646     else if (!strcasecmp(el, "v_answered"))         safestrncpy(vbuf.v_answered, ChrPtr(migr_chardata), sizeof vbuf.v_answered);
  647     else if (!strcasecmp(el, "v_flags"))            vbuf.v_flags = atoi(ChrPtr(migr_chardata));
  648     else if (!strcasecmp(el, "v_view"))         vbuf.v_view = atoi(ChrPtr(migr_chardata));
  649     else return 0;
  650     return 1;
  651 }
  652 
  653 
  654 void migr_xml_end(void *data, const char *el) {
  655     const char *ptr;
  656     int msgcount = 0;
  657     long msgnum = 0L;
  658     long *msglist = NULL;
  659     int msglist_alloc = 0;
  660     // *** GENERAL STUFF ***
  661 
  662     if (!strcasecmp(el, "citadel_migrate_data")) {
  663         --citadel_migrate_data;
  664         return;
  665     }
  666 
  667     if (citadel_migrate_data != 1) {
  668         syslog(LOG_ERR, "migrate: out-of-sequence tag <%s> detected.  Warning: ODD-DATA!", el);
  669         return;
  670     }
  671 
  672     // syslog(LOG_DEBUG, "END TAG: <%s> DATA: <%s>\n", el, (migr_chardata_len ? migr_chardata : ""));
  673 
  674     // *** CONFIG ***
  675 
  676     if (!strcasecmp(el, "config")) {
  677         syslog(LOG_DEBUG, "migrate: imported config key=%s", ikey);
  678 
  679         if (ikey != NULL) {
  680             CtdlSetConfigStr(ikey, (char *)ChrPtr(migr_chardata));
  681             free(ikey);
  682             ikey = NULL;
  683         }
  684         else {
  685             syslog(LOG_INFO, "migrate: closed a <config> tag but no key name was supplied.");
  686         }
  687     }
  688 
  689     // *** USER ***
  690     else if ((!strncasecmp(el, HKEY("u_"))) && 
  691          migr_userrecord(data, el))
  692         ; /* Nothing to do anymore */
  693     else if (!strcasecmp(el, "user")) {
  694         CtdlPutUser(&usbuf);
  695         syslog(LOG_INFO, "migrate: imported user: %s", usbuf.fullname);
  696     }
  697 
  698     // *** OPENID ***
  699 
  700     else if (!strcasecmp(el, "oid_url"))            safestrncpy(openid_url, ChrPtr(migr_chardata), sizeof openid_url);
  701     else if (!strcasecmp(el, "oid_usernum"))        openid_usernum = atol(ChrPtr(migr_chardata));
  702 
  703     else if (!strcasecmp(el, "openid")) {           // see serv_openid_rp.c for a description of the record format
  704         char *oid_data;
  705         int oid_data_len;
  706         oid_data_len = sizeof(long) + strlen(openid_url) + 1;
  707         oid_data = malloc(oid_data_len);
  708         memcpy(oid_data, &openid_usernum, sizeof(long));
  709         memcpy(&oid_data[sizeof(long)], openid_url, strlen(openid_url) + 1);
  710         cdb_store(CDB_EXTAUTH, openid_url, strlen(openid_url), oid_data, oid_data_len);
  711         free(oid_data);
  712         syslog(LOG_INFO, "migrate: imported OpenID: %s (%ld)", openid_url, openid_usernum);
  713     }
  714 
  715     // *** ROOM ***
  716     else if ((!strncasecmp(el, HKEY("QR"))) && 
  717          migr_roomrecord(data, el))
  718         ; // Nothing to do anymore
  719     else if (!strcasecmp(el, "room")) {
  720         CtdlPutRoom(&qrbuf);
  721         syslog(LOG_INFO, "migrate: imported room: %s", qrbuf.QRname);
  722     }
  723 
  724     // *** ROOM MESSAGE POINTERS ***
  725 
  726     else if (!strcasecmp(el, "FRname")) {
  727         safestrncpy(FRname, ChrPtr(migr_chardata), sizeof FRname);
  728     }
  729 
  730     else if (!strcasecmp(el, "FRmsglist")) {
  731         if (!IsEmptyStr(FRname)) {
  732             msgcount = 0;
  733             msglist_alloc = 1000;
  734             msglist = malloc(sizeof(long) * msglist_alloc);
  735 
  736             syslog(LOG_DEBUG, "migrate: message list for: %s", FRname);
  737 
  738             ptr = ChrPtr(migr_chardata);
  739             while (*ptr != 0) {
  740                 while ((*ptr != 0) && (!isdigit(*ptr))) {
  741                     ++ptr;
  742                 }
  743                 if ((*ptr != 0) && (isdigit(*ptr))) {
  744                     msgnum = atol(ptr);
  745                     if (msgnum > 0L) {
  746                         if (msgcount >= msglist_alloc) {
  747                             msglist_alloc *= 2;
  748                             msglist = realloc(msglist, sizeof(long) * msglist_alloc);
  749                         }
  750                         msglist[msgcount++] = msgnum;
  751                         }
  752                     }
  753                     while ((*ptr != 0) && (isdigit(*ptr))) {
  754                         ++ptr;
  755                     }
  756                 }
  757             }
  758             if (msgcount > 0) {
  759                 CtdlSaveMsgPointersInRoom(FRname, msglist, msgcount, 0, NULL, 1);
  760             }
  761             free(msglist);
  762             msglist = NULL;
  763             msglist_alloc = 0;
  764             syslog(LOG_DEBUG, "migrate: imported %d messages.", msgcount);
  765             if (server_shutting_down) {
  766                 return;
  767         }
  768     }
  769 
  770     // *** FLOORS ***
  771     else if ((!strncasecmp(el, HKEY("f_"))) && 
  772          migr_floorrecord(data, el))
  773         ; // Nothing to do anymore
  774 
  775     else if (!strcasecmp(el, "floor")) {
  776         CtdlPutFloor(&flbuf, floornum);
  777         syslog(LOG_INFO, "migrate: imported floor #%d (%s)", floornum, flbuf.f_name);
  778     }
  779 
  780     // *** VISITS ***
  781     else if ((!strncasecmp(el, HKEY("v_"))) && migr_visitrecord(data, el)) {
  782         ; // Nothing to do anymore
  783     }
  784     else if (!strcasecmp(el, "visit")) {
  785         put_visit(&vbuf);
  786         syslog(LOG_INFO, "migrate: imported visit: %ld/%ld/%ld", vbuf.v_roomnum, vbuf.v_roomgen, vbuf.v_usernum);
  787     }
  788 
  789     // *** MESSAGES ***
  790     
  791     else if (!strcasecmp(el, "msg_msgnum"))         smi.meta_msgnum = import_msgnum = atol(ChrPtr(migr_chardata));
  792     else if (!strcasecmp(el, "msg_meta_refcount"))      smi.meta_refcount = atoi(ChrPtr(migr_chardata));
  793     else if (!strcasecmp(el, "msg_meta_rfc822_length")) smi.meta_rfc822_length = atoi(ChrPtr(migr_chardata));
  794     else if (!strcasecmp(el, "msg_meta_content_type"))  safestrncpy(smi.meta_content_type, ChrPtr(migr_chardata), sizeof smi.meta_content_type);
  795 
  796     else if (!strcasecmp(el, "msg_text")) {
  797         long rc;
  798         struct CtdlMessage *msg;
  799 
  800         FlushStrBuf(migr_MsgData);
  801         StrBufDecodeBase64To(migr_chardata, migr_MsgData);
  802 
  803         msg = CtdlDeserializeMessage(import_msgnum, -1,
  804                          ChrPtr(migr_MsgData), 
  805                          StrLength(migr_MsgData));
  806         if (msg != NULL) {
  807             rc = CtdlSaveThisMessage(msg, import_msgnum, 0);
  808             if (rc == 0) {
  809                 PutMetaData(&smi);
  810             }
  811             CM_Free(msg);
  812         }
  813         else {
  814             rc = -1;
  815         }
  816 
  817         syslog(LOG_INFO,
  818                "migrate: %s message #%ld, size=%d, ref=%d, body=%ld, content-type: %s",
  819                (rc!= 0)?"failed to import":"imported",
  820                import_msgnum,
  821                StrLength(migr_MsgData),
  822                smi.meta_refcount,
  823                smi.meta_rfc822_length,
  824                smi.meta_content_type
  825         );
  826         memset(&smi, 0, sizeof(smi));
  827     }
  828 
  829     // *** MORE GENERAL STUFF ***
  830 
  831     FlushStrBuf(migr_chardata);
  832 }
  833 
  834 
  835 // Import begins here
  836 void migr_do_import(void) {
  837     XML_Parser xp;
  838     char buf[SIZ];
  839     
  840     unbuffer_output();
  841     migr_chardata = NewStrBufPlain(NULL, SIZ * 20);
  842     migr_MsgData = NewStrBufPlain(NULL, SIZ * 20);
  843     xp = XML_ParserCreate(NULL);
  844     if (!xp) {
  845         cprintf("%d Failed to create XML parser instance\n", ERROR+INTERNAL_ERROR);
  846         return;
  847     }
  848     XML_SetElementHandler(xp, migr_xml_start, migr_xml_end);
  849     XML_SetCharacterDataHandler(xp, migr_xml_chardata);
  850 
  851     CC->dont_term = 1;
  852 
  853     cprintf("%d sock it to me\n", SEND_LISTING);
  854     unbuffer_output();
  855 
  856     client_set_inbound_buf(SIZ * 10);
  857 
  858     while (client_getln(buf, sizeof buf) >= 0 && strcmp(buf, "000")) {
  859         XML_Parse(xp, buf, strlen(buf), 0);
  860         if (server_shutting_down)
  861             break;  // Should we break or return?
  862     }
  863 
  864     XML_Parse(xp, "", 0, 1);
  865     XML_ParserFree(xp);
  866     FreeStrBuf(&migr_chardata);
  867     FreeStrBuf(&migr_MsgData);
  868     rebuild_euid_index();
  869     rebuild_usersbynumber();
  870     CtdlSetConfigInt("MM_fulltext_wordbreaker", -1);    // Set an invalid wordbreaker to force re-indexing
  871     CC->dont_term = 0;
  872 }
  873 
  874 
  875 // ******************************************************************************
  876 // *                         Dispatcher, Common code                            *
  877 // ******************************************************************************
  878 
  879 // Dump out the pathnames of directories which can be copied "as is"
  880 void migr_do_listdirs(void) {
  881     cprintf("%d Don't forget these:\n", LISTING_FOLLOWS);
  882     cprintf("files|%s\n",       ctdl_file_dir);
  883     cprintf("messages|%s\n",    ctdl_message_dir);
  884     cprintf("keys|%s\n",        ctdl_key_dir);
  885     cprintf("000\n");
  886 }
  887 
  888 
  889 // ******************************************************************************
  890 // *                    Repair database integrity                               *
  891 // ******************************************************************************
  892 
  893 StrBuf *PlainMessageBuf = NULL;
  894 HashList *UsedMessageIDS = NULL;
  895 
  896 int migr_restore_message_metadata(long msgnum, int refcount) {
  897     struct MetaData smi;
  898     struct CtdlMessage *msg;
  899     char *mptr = NULL;
  900 
  901     // We can use a static buffer here because there will never be more than
  902     // one of this operation happening at any given time, and it's really best
  903     // to just keep it allocated once instead of torturing malloc/free.
  904     // Call this function with msgnum "-1" to free the buffer when finished.
  905     static int encoded_alloc = 0;
  906     static char *encoded_msg = NULL;
  907 
  908     if (msgnum < 0) {
  909         if ((encoded_alloc == 0) && (encoded_msg != NULL)) {
  910             free(encoded_msg);
  911             encoded_alloc = 0;
  912             encoded_msg = NULL;
  913             // todo FreeStrBuf(&PlainMessageBuf); PlainMessageBuf = NULL;
  914         }
  915         return 0;
  916     }
  917 
  918     if (PlainMessageBuf == NULL) {
  919         PlainMessageBuf = NewStrBufPlain(NULL, 10*SIZ);
  920     }
  921 
  922     // Ok, here we go ...
  923 
  924     msg = CtdlFetchMessage(msgnum, 1);
  925     if (msg == NULL) {
  926         return 1;
  927     }
  928 
  929     GetMetaData(&smi, msgnum);
  930     smi.meta_msgnum = msgnum;
  931     smi.meta_refcount = refcount;
  932     
  933     // restore the content type from the message body:
  934     mptr = bmstrcasestr(msg->cm_fields[eMesageText], "Content-type:");
  935     if (mptr != NULL) {
  936         char *aptr;
  937         safestrncpy(smi.meta_content_type, &mptr[13], sizeof smi.meta_content_type);
  938         striplt(smi.meta_content_type);
  939         aptr = smi.meta_content_type;
  940         while (!IsEmptyStr(aptr)) {
  941             if ((*aptr == ';')
  942                 || (*aptr == ' ')
  943                 || (*aptr == 13)
  944                 || (*aptr == 10)) {
  945                 memset(aptr, 0, sizeof(smi.meta_content_type) - (aptr - smi.meta_content_type));
  946             }
  947             else aptr++;
  948         }
  949     }
  950 
  951     CC->redirect_buffer = PlainMessageBuf;
  952     CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1, QP_EADDR);
  953     smi.meta_rfc822_length = StrLength(CC->redirect_buffer);
  954     CC->redirect_buffer = NULL;
  955 
  956     syslog(LOG_INFO,
  957            "migrate: setting message #%ld meta data to: refcount=%d, bodylength=%ld, content-type: %s",
  958            smi.meta_msgnum,
  959            smi.meta_refcount,
  960            smi.meta_rfc822_length,
  961            smi.meta_content_type
  962     );
  963 
  964     PutMetaData(&smi);
  965     CM_Free(msg);
  966     return 0;
  967 }
  968 
  969 
  970 void migr_check_room_msg(long msgnum, void *userdata) {
  971     fprintf(migr_global_message_list, "%ld %s\n", msgnum, CC->room.QRname);
  972 }
  973 
  974 
  975 void migr_check_rooms_backend(struct ctdlroom *buf, void *data) {
  976     // message list goes inside this tag
  977     CtdlGetRoom(&CC->room, buf->QRname);
  978     CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL, migr_check_room_msg, NULL);
  979 }
  980 
  981 
  982 void RemoveMessagesFromRooms(StrBuf *RoomNameVec, long msgnum) {
  983     struct MetaData smi;
  984     const char *Pos = NULL;
  985     StrBuf *oneRoom = NewStrBuf();
  986 
  987     syslog(LOG_INFO, "migrate: removing message pointer %ld from these rooms: %s", msgnum, ChrPtr(RoomNameVec));
  988 
  989     while (Pos != StrBufNOTNULL){
  990         StrBufExtract_NextToken(oneRoom, RoomNameVec, &Pos, '|');
  991         CtdlDeleteMessages(ChrPtr(oneRoom), &msgnum, 1, "");
  992     };
  993     GetMetaData(&smi, msgnum);
  994     AdjRefCount(msgnum, -smi.meta_refcount);
  995 }
  996 
  997 
  998 void migr_do_restore_meta(void) {
  999     char buf[SIZ];
 1000     int failGetMessage;
 1001     long msgnum = 0;
 1002     int lastnum = 0;
 1003     int refcount = 0;
 1004     CitContext *Ctx;
 1005     char *prn;
 1006     StrBuf *RoomNames;
 1007     char cmd[SIZ];
 1008 
 1009     migr_global_message_list = fopen(migr_tempfilename1, "w");
 1010     if (migr_global_message_list != NULL) {
 1011         CtdlForEachRoom(migr_check_rooms_backend, NULL);
 1012         fclose(migr_global_message_list);
 1013     }
 1014 
 1015     // Process the 'global' message list.  (Sort it and remove dups.
 1016     // Dups are ok because a message may be in more than one room, but
 1017     // this will be handled by exporting the reference count, not by
 1018     // exporting the message multiple times.)
 1019     snprintf(cmd, sizeof cmd, "sort -n <%s >%s", migr_tempfilename1, migr_tempfilename2);
 1020     if (system(cmd) != 0) {
 1021         syslog(LOG_ERR, "migrate: error %d", errno);
 1022     }
 1023 
 1024     RoomNames = NewStrBuf();
 1025     Ctx = CC;
 1026     migr_global_message_list = fopen(migr_tempfilename2, "r");
 1027     if (migr_global_message_list != NULL) {
 1028         syslog(LOG_INFO, "migrate: opened %s", migr_tempfilename1);
 1029         while ((Ctx->kill_me == 0) && 
 1030                (fgets(buf, sizeof(buf), migr_global_message_list) != NULL)) {
 1031             msgnum = atol(buf);
 1032             if (msgnum == 0L) 
 1033                 continue;
 1034             if (lastnum == 0) {
 1035                 lastnum = msgnum;
 1036             }
 1037             prn = strchr(buf, ' ');
 1038             if (lastnum != msgnum) {
 1039                 failGetMessage = migr_restore_message_metadata(lastnum, refcount);
 1040                 if (failGetMessage) {
 1041                     RemoveMessagesFromRooms(RoomNames, lastnum);
 1042                 }
 1043                 refcount = 1;
 1044                 lastnum = msgnum;
 1045                 if (prn != NULL) {
 1046                     StrBufPlain(RoomNames, prn + 1, -1);
 1047                 }
 1048                 StrBufTrim(RoomNames);
 1049             }
 1050             else {
 1051                 if (prn != NULL) {
 1052                     if (StrLength(RoomNames) > 0) {
 1053                         StrBufAppendBufPlain(RoomNames, HKEY("|"), 0);
 1054                     }
 1055                     StrBufAppendBufPlain(RoomNames, prn, -1, 1);
 1056                     StrBufTrim(RoomNames);
 1057                 }
 1058                 refcount ++;
 1059             }
 1060             lastnum = msgnum;
 1061         }
 1062         failGetMessage = migr_restore_message_metadata(msgnum, refcount);
 1063         if (failGetMessage) {
 1064             RemoveMessagesFromRooms(RoomNames, lastnum);
 1065         }
 1066         fclose(migr_global_message_list);
 1067     }
 1068 
 1069     migr_restore_message_metadata(-1L, -1); // This frees the encoding buffer
 1070     cprintf("%d system analysis completed", CIT_OK);
 1071     Ctx->kill_me = 1;
 1072 }
 1073 
 1074 
 1075 // ******************************************************************************
 1076 // *                         Dispatcher, Common code                            *
 1077 // ******************************************************************************
 1078 void cmd_migr(char *cmdbuf) {
 1079     char cmd[32];
 1080     
 1081     if (CtdlAccessCheck(ac_aide)) return;
 1082     
 1083     if (CtdlTrySingleUser()) {
 1084         CtdlDisableHouseKeeping();
 1085         CtdlMakeTempFileName(migr_tempfilename1, sizeof migr_tempfilename1);
 1086         CtdlMakeTempFileName(migr_tempfilename2, sizeof migr_tempfilename2);
 1087 
 1088         extract_token(cmd, cmdbuf, 0, '|', sizeof cmd);
 1089         if (!strcasecmp(cmd, "export")) {
 1090             migr_do_export();
 1091         }
 1092         else if (!strcasecmp(cmd, "import")) {
 1093             migr_do_import();
 1094         }
 1095         else if (!strcasecmp(cmd, "listdirs")) {
 1096             migr_do_listdirs();
 1097         }
 1098         else if (!strcasecmp(cmd, "restoremeta")) {
 1099             migr_do_restore_meta();
 1100         }
 1101         else {
 1102             cprintf("%d illegal command\n", ERROR + ILLEGAL_VALUE);
 1103         }
 1104 
 1105         unlink(migr_tempfilename1);
 1106         unlink(migr_tempfilename2);
 1107 
 1108         CtdlEnableHouseKeeping();
 1109         CtdlEndSingleUser();
 1110     }
 1111     else {
 1112         cprintf("%d The migrator is already running.\n", ERROR + RESOURCE_BUSY);
 1113     }
 1114 }
 1115 
 1116 
 1117 // ******************************************************************************
 1118 // *                              Module Hook                                   *
 1119 // ******************************************************************************
 1120 
 1121 CTDL_MODULE_INIT(migrate)
 1122 {
 1123     if (!threading)
 1124     {
 1125         CtdlRegisterProtoHook(cmd_migr, "MIGR", "Across-the-wire migration");
 1126     }
 1127     
 1128     // return our module name for the log
 1129     return "migrate";
 1130 }