6f412c3421262fb315db1b19fa59538063d44be4
[citadel.git] / citadel / utils / ctdl3264.c
1 // Attempt to  your database from 32-bit to 64-bit.
2 // Don't run this.  It doesn't work and if you try to run it you will immediately die.
3 //
4 // Copyright (c) 2023 by Art Cancro citadel.org
5 //
6 // This program is open source software.  Use, duplication, or disclosure
7 // is subject to the terms of the GNU General Public License, version 3.
8
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <ctype.h>
12 #include <stdio.h>
13 #include <signal.h>
14 #include <sys/types.h>
15 #include <sys/socket.h>
16 #include <sys/un.h>
17 #include <netdb.h>
18 #include <string.h>
19 #include <pwd.h>
20 #include <errno.h>
21 #include <stdarg.h>
22 #include <limits.h>
23 #include <libcitadel.h>
24 #include <zlib.h>
25 #include <db.h>
26 #include "../server/sysdep.h"
27 #include "../server/citadel_defs.h"
28 #include "../server/server.h"
29 #include "../server/citadel_dirs.h"
30 #include "ctdl3264_structs.h"
31
32 // Open a database environment
33 DB_ENV *open_dbenv(char *src_dir) {
34
35         DB_ENV *dbenv = NULL;
36
37         int ret;
38         int i;
39         u_int32_t flags = 0;
40         int dbversion_major, dbversion_minor, dbversion_patch;
41
42         printf( "db: open_dbenv() starting\n"
43                 "db:    Linked zlib: %s\n"
44                 "db: Compiled libdb: %s\n"
45                 "db:   Linked libdb: %s\n",
46                 zlibVersion(),
47                 DB_VERSION_STRING,
48                 db_version(&dbversion_major, &dbversion_minor, &dbversion_patch)
49         );
50
51         // Create synthetic integer version numbers and compare them.
52         // Never run with a libdb older than the one with which it was compiled.
53         int compiled_db_version = ( (DB_VERSION_MAJOR * 1000000) + (DB_VERSION_MINOR * 1000) + (DB_VERSION_PATCH) );
54         int linked_db_version = ( (dbversion_major * 1000000) + (dbversion_minor * 1000) + (dbversion_patch) );
55         if (compiled_db_version > linked_db_version) {
56                 printf( "db: ctdl3264 is running with a version of libdb older than the one with which it was compiled.\n"
57                         "db: This is an invalid configuration.  ctdl3264 will now exit to prevent data loss.");
58                 exit(CTDLEXIT_DB);
59         }
60
61         printf("db: Setting up DB environment\n");
62         ret = db_env_create(&dbenv, 0);
63         if (ret) {
64                 printf("db: db_env_create: %s\n", db_strerror(ret));
65                 printf("db: exit code %d\n", ret);
66                 exit(CTDLEXIT_DB);
67         }
68
69         // We want to specify the shared memory buffer pool cachesize, but everything else is the default.
70         ret = dbenv->set_cachesize(dbenv, 0, 64 * 1024, 0);
71         if (ret) {
72                 printf("db: set_cachesize: %s\n", db_strerror(ret));
73                 dbenv->close(dbenv, 0);
74                 printf("db: exit code %d\n", ret);
75                 exit(CTDLEXIT_DB);
76         }
77
78         if ((ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT))) {
79                 printf("db: set_lk_detect: %s\n", db_strerror(ret));
80                 dbenv->close(dbenv, 0);
81                 printf("db: exit code %d\n", ret);
82                 exit(CTDLEXIT_DB);
83         }
84
85         flags = DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_INIT_LOG;
86         printf("db: dbenv open(dir=%s, flags=%d)\n", src_dir, flags);
87         ret = dbenv->open(dbenv, src_dir, flags, 0);
88         if (ret) {
89                 printf("db: dbenv->open: %s\n", db_strerror(ret));
90                 dbenv->close(dbenv, 0);
91                 printf("db: exit code %d\n", ret);
92                 exit(CTDLEXIT_DB);
93         }
94
95         return(dbenv);
96 }
97
98
99 void close_dbenv(DB_ENV *dbenv) {
100         printf("db: closing dbenv\n");
101         int ret = dbenv->close(dbenv, 0);
102         if (ret) {
103                 printf("db: dbenv->close: %s\n", db_strerror(ret));
104         }
105 }
106
107
108 // convert function for a message in msgmain
109 void convert_msgmain(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
110         int32_t in_msgnum;
111         long out_msgnum;
112         memcpy(&in_msgnum, in_key->data, sizeof(in_msgnum));
113         out_msgnum = (long)in_msgnum;
114
115         if (in_key->size != 4) {
116                 printf("\033[31m\033[1m *** SOURCE DATABASE IS NOT 32-BIT *** ABORTING *** \033[0m\n");
117                 abort();
118         }
119
120         // If the msgnum is negative, we are looking at METADATA
121         if (in_msgnum < 0) {
122                 struct MetaData_32 *meta32 = (struct MetaData_32 *)in_data->data;
123
124                 printf("\033[32m\033[1mMetadata: msgnum=%d , refcount=%d , content_type=\"%s\" , rfc822len=%d\033[0m\n", meta32->meta_msgnum, meta32->meta_refcount, meta32->meta_content_type, meta32->meta_rfc822_length);
125
126                 out_key->size = sizeof(long);
127                 out_key->data = realloc(out_key->data, out_key->size);
128                 memcpy(out_key->data, &out_msgnum, sizeof(long));
129
130                 out_data->size = sizeof(struct MetaData);
131                 out_data->data = realloc(out_data->data, out_data->size);
132                 struct MetaData *meta64 = (struct MetaData *)out_data->data;
133                 memset(meta64, 0, sizeof(struct MetaData));
134                 meta64->meta_msgnum             = (long)        meta32->meta_msgnum;
135                 meta64->meta_refcount           = (int)         meta32->meta_refcount;
136                 strcpy(meta64->meta_content_type,               meta32->meta_content_type);
137                 meta64->meta_rfc822_length      = (long)        meta32->meta_rfc822_length;
138         }
139
140         // If the msgnum is positive, we are looking at a MESSAGE
141         else if (in_msgnum > 0) {
142                 out_key->size = sizeof(long);
143                 out_key->data = realloc(out_key->data, out_key->size);
144                 memcpy(out_key->data, &out_msgnum, sizeof(long));
145
146                 // copy the message verbatim
147                 out_data->size = in_data->size;
148                 out_data->data = realloc(out_data->data, out_data->size);
149                 memcpy(out_data->data, in_data->data, out_data->size);
150                 printf("\033[32m\033[1mMessage: %ld\033[0m\n", out_msgnum);
151         }
152
153         // If the msgnum is 0 it's probably not a valid record.
154         else {
155                 printf("msgmain: record 0 is impossible\n");
156         }
157 }
158
159
160 // convert function for a user record
161 void convert_users(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
162
163         // The key is a string so we can just copy it over
164         out_key->size = in_key->size;
165         out_key->data = realloc(out_key->data, out_key->size);
166         memcpy(out_key->data, in_key->data, in_key->size);
167
168         struct ctdluser_32 *user32 = (struct ctdluser_32 *)in_data->data;
169
170         out_data->size = sizeof(struct ctdluser);
171         out_data->data = realloc(out_data->data, out_data->size);
172         struct ctdluser *user64 = (struct ctdluser *)out_data->data;
173
174         user64->version                 = (int)         user32->version;
175         user64->uid                     = (uid_t)       user32->uid;
176         strcpy(user64->password,                        user32->password);
177         user64->flags                   = (unsigned)    user32->flags;
178         user64->timescalled             = (long)        user32->timescalled;
179         user64->posted                  = (long)        user32->posted;
180         user64->axlevel                 = (cit_uint8_t) user32->axlevel;
181         user64->usernum                 = (long)        user32->usernum;
182         user64->lastcall                = (time_t)      user32->lastcall;
183         user64->USuserpurge             = (int)         user32->USuserpurge;
184         strcpy(user64->fullname,                        user32->fullname);
185         user64->msgnum_bio              = (long)        user32->msgnum_bio;
186         user64->msgnum_pic              = (long)        user32->msgnum_pic;
187         strcpy(user64->emailaddrs,                      user32->emailaddrs);
188         user64->msgnum_inboxrules       = (long)        user32->msgnum_inboxrules;
189         user64->lastproc_inboxrules     = (long)        user32->lastproc_inboxrules;
190
191         printf("\033[32m\033[1mUser: %s\033[0m\n", user64->fullname);
192 }
193
194
195 // convert function for a room record
196 void convert_rooms(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
197
198         // The key is a string so we can just copy it over
199         out_key->size = in_key->size;
200         out_key->data = realloc(out_key->data, out_key->size);
201         memcpy(out_key->data, in_key->data, in_key->size);
202
203         // data
204         struct ctdlroom_32 *room32 = (struct ctdlroom_32 *)in_data->data;
205         out_data->size = sizeof(struct ctdlroom);
206         out_data->data = realloc(out_data->data, out_data->size);
207         struct ctdlroom *room64 = (struct ctdlroom *)out_data->data;
208
209         strcpy(room64->QRname,                          room32->QRname);
210         strcpy(room64->QRpasswd,                        room32->QRpasswd);
211         room64->QRroomaide              = (long)        room32->QRroomaide;
212         room64->QRhighest               = (long)        room32->QRhighest;
213         room64->QRgen                   = (time_t)      room32->QRgen;
214         room64->QRflags                 = (unsigned)    room32->QRflags;
215         strcpy(room64->QRdirname,                       room32->QRdirname);
216         room64->msgnum_info             = (long)        room32->msgnum_info;
217         room64->QRfloor                 = (char)        room32->QRfloor;
218         room64->QRmtime                 = (time_t)      room32->QRmtime;
219         room64->QRep.expire_mode        = (int)         room32->QRep.expire_mode;
220         room64->QRep.expire_value       = (int)         room32->QRep.expire_value;
221         room64->QRnumber                = (long)        room32->QRnumber;
222         room64->QRorder                 = (char)        room32->QRorder;
223         room64->QRflags2                = (unsigned)    room32->QRflags2;
224         room64->QRdefaultview           = (int)         room32->QRdefaultview;
225         room64->msgnum_pic              = (long)        room32->msgnum_pic;
226
227         printf("\033[32m\033[1mRoom: %s\033[0m\n", room64->QRname);
228 }
229
230
231 // convert function for a floor record
232 void convert_floors(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
233
234         // the key is an "int", and "int" is 32-bits on both 32 and 64 bit platforms.
235         out_key->size = in_key->size;
236         out_key->data = realloc(out_key->data, out_key->size);
237         memcpy(out_key->data, in_key->data, in_key->size);
238
239         // data
240         struct floor_32 *floor32 = (struct floor_32 *)in_data->data;
241         out_data->size = sizeof(struct floor);
242         out_data->data = realloc(out_data->data, out_data->size);
243         struct floor *floor64 = (struct floor *)out_data->data;
244
245         // these are probably bit-for-bit identical, actually ... but we do it the "right" way anyway
246         floor64->f_flags                = (unsigned short)      floor32->f_flags;
247         strcpy(floor64->f_name,                                 floor32->f_name);
248         floor64->f_ref_count            = (int)                 floor32->f_ref_count;
249         floor64->f_ep.expire_mode       = (int)                 floor32->f_ep.expire_mode;
250         floor64->f_ep.expire_value      = (int)                 floor32->f_ep.expire_value;
251
252         printf("\033[32m\033[1mFloor: %s\033[0m\n", floor64->f_name);
253 }
254
255
256 // convert function for a msglist or a fulltext index record
257 // (both are indexed by a long and the data is arrays of longs)
258 void convert_msglists(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
259         int i;
260
261         char *table = (which_cdb == CDB_FULLTEXT) ? "FullText" : "Msglist";
262
263         // records are indexed by a single "long" and contains an array of zero or more "long"s
264         // and remember ... "long" is int32_t on the source system
265         int32_t in_roomnum;
266         long out_roomnum;
267         memcpy(&in_roomnum, in_key->data, sizeof(in_roomnum));
268         out_roomnum = (long) in_roomnum;
269
270         if (in_key->size != 4) {
271                 printf("\033[31m\033[1m *** SOURCE DATABASE IS NOT 32-BIT *** ABORTING *** \033[0m\n");
272                 abort();
273         }
274
275         int num_msgs = in_data->size / sizeof(int32_t);
276         printf("\033[32m\033[1m%s: key %ld (%d messages)\033[0m\n", table, out_roomnum, num_msgs);
277
278         // the key is a "long"
279         out_key->size = sizeof(out_roomnum);
280         out_key->data = realloc(out_key->data, out_key->size);
281         memcpy(out_key->data, &out_roomnum, sizeof(out_roomnum));
282
283         // the data is another array, but a wider type
284         out_data->size = sizeof(long) * num_msgs;
285         out_data->data = realloc(out_data->data, out_data->size);
286
287         int32_t in_msg = 0;
288         long out_msg = 0;
289         for (i=0; i<num_msgs; ++i) {
290                 memcpy(&in_msg, (in_data->data + (i * sizeof(int32_t))), sizeof(int32_t));
291                 out_msg = (long) in_msg;
292                 memcpy((out_data->data + (i * sizeof(long))), &out_msg, sizeof(long));
293                 printf("msg#%ld\n", out_msg);
294         }
295 }
296
297
298 // convert function for a visit record
299 void convert_visits(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
300
301         // data
302         struct visit_32 *visit32 = (struct visit_32 *)in_data->data;
303         out_data->size = sizeof(struct visit);
304         out_data->data = realloc(out_data->data, out_data->size);
305         struct visit *visit64 = (struct visit *)out_data->data;
306
307         //  the data (zero it out so it will compress well)
308         memset(visit64, 0, sizeof(struct visit));
309         visit64->v_roomnum              = (long)        visit32->v_roomnum;
310         visit64->v_roomgen              = (long)        visit32->v_roomgen;
311         visit64->v_usernum              = (long)        visit32->v_usernum;
312         visit64->v_lastseen             = (long)        visit32->v_lastseen;
313         visit64->v_flags                = (unsigned)    visit32->v_flags;
314         strcpy(visit64->v_seen,                         visit32->v_seen);
315         strcpy(visit64->v_answered,                     visit32->v_answered);
316         visit64->v_view                 = (int)         visit32->v_view;
317
318         printf("\033[32m\033[1mVisit: room %ld, gen %ld, user %ld\033[0m\n", visit64->v_roomnum, visit64->v_roomgen, visit64->v_usernum);
319
320         // create the key (which is based on the data, so there is no need to  the old key)
321         out_key->size = sizeof(struct visit_index);
322         out_key->data = realloc(out_key->data, out_key->size);
323         struct visit_index *newvisitindex = (struct visit_index *) out_key->data;
324         newvisitindex->iRoomID          =               visit64->v_roomnum;
325         newvisitindex->iRoomGen         =               visit64->v_roomgen;
326         newvisitindex->iUserID          =               visit64->v_usernum;
327 }
328
329
330 // convert function for a directory record
331 void convert_dir(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
332
333         // the key is a string
334         out_key->size = in_key->size;
335         out_key->data = realloc(out_key->data, out_key->size + 1);
336         memcpy(out_key->data, in_key->data, in_key->size);
337         char *k = (char *)out_key->data;
338         k[out_key->size] = 0;
339
340         // the data is also a string
341         out_data->size = in_data->size;
342         out_data->data = realloc(out_data->data, out_data->size + 1);
343         memcpy(out_data->data, in_data->data, in_data->size);
344         char *d = (char *)out_data->data;
345         d[out_data->size] = 0;
346
347         // please excuse my friend, he isn't null terminated
348         printf("\033[32m\033[1mDirectory entry: %s -> %s\033[0m\n", (char *)out_key->data, (char *)out_data->data);
349 }
350
351
352 // convert function for a use table record
353 void convert_usetable(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
354
355         // the key is a string
356         out_key->size = in_key->size;
357         out_key->data = realloc(out_key->data, out_key->size);
358         memcpy(out_key->data, in_key->data, in_key->size);
359
360         // the data is a "struct UseTable"
361         struct UseTable_32 *use32 = (struct UseTable_32 *)in_data->data;
362         out_data->size = sizeof(struct UseTable);
363         out_data->data = realloc(out_data->data, out_data->size);
364         memset(out_data->data, 0, out_data->size);
365         struct UseTable *use64 = (struct UseTable *)out_data->data;
366
367         //  the data
368         strcpy(use64->ut_msgid,                         use32->ut_msgid);
369         use64->ut_timestamp             = (time_t)      use32->ut_timestamp;
370 }
371
372
373 // convert function for large message texts
374 void convert_bigmsgs(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
375
376         // The key is a packed long
377         int32_t in_msgnum;
378         long out_msgnum;
379         memcpy(&in_msgnum, in_key->data, sizeof(in_msgnum));
380         out_msgnum = (long)in_msgnum;
381
382         if (in_key->size != 4) {
383                 printf("\033[31m\033[1m *** SOURCE DATABASE IS NOT 32-BIT *** ABORTING *** \033[0m\n");
384                 abort();
385         }
386
387         // the data is binary-ish but has no packed integers
388         out_data->size = in_data->size;
389         out_data->data = realloc(out_data->data, out_data->size);
390         memcpy(out_data->data, in_data->data, in_data->size);
391
392         printf("\033[32m\033[1mBigmsg %ld , length %d\033[0m\n", out_msgnum, out_data->size);
393 }
394
395
396 // convert function for EUID Index records
397 void convert_euidindex(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
398
399         // The structure of an euidindex record *key* is:
400         // |----room_number----|----------EUID-------------|
401         //    (sizeof long)       (actual length of euid)
402
403         // The structure of an euidindex record *value* is:
404         // |-----msg_number----|----room_number----|----------EUID-------------|
405         //    (sizeof long)       (sizeof long)       (actual length of euid)
406
407         int32_t in_msgnum = 0;
408         int32_t in_roomnum = 0;
409         char euid[SIZ];
410         long out_msgnum = 0;
411         long out_roomnum = 0;
412
413         memcpy(&in_msgnum, in_data->data, sizeof(in_msgnum));
414         memcpy(&in_roomnum, in_data->data+sizeof(int32_t), sizeof(in_msgnum));
415         strcpy(euid, in_data->data+(sizeof(int32_t)*2));
416
417         out_msgnum = (long) in_msgnum;
418         out_roomnum = (long) in_roomnum;
419         printf("euidindex: msgnum=%ld, roomnum=%ld, euid=\"%s\"\n", out_msgnum, out_roomnum, euid);
420
421         out_key->size = sizeof(long) + strlen(euid) + 1;
422         out_key->data = realloc(out_key->data, out_key->size);
423         memcpy(out_key->data, &out_roomnum, sizeof(out_roomnum));
424         strcpy(out_key->data+sizeof(out_roomnum), euid);
425
426         out_data->size = sizeof(long) + sizeof(long) + strlen(euid) + 1;
427         out_data->data = realloc(out_data->data, out_data->size);
428         memcpy(out_data->data, &out_msgnum, sizeof(out_msgnum));
429         memcpy(out_data->data+sizeof(out_msgnum), &out_roomnum, sizeof(out_roomnum));
430         strcpy(out_data->data+sizeof(out_msgnum)+sizeof(out_roomnum), euid);
431
432
433         int i;
434         char ch;
435
436         printf("  in_key:             ");
437         for (i=0; i<in_key->size; ++i) {
438                 ch = 0;
439                 memcpy(&ch, in_key->data+i, 1);
440                 printf("%02X ", (int) ch);
441         }
442         printf("\n");
443
444         printf(" out_key: ");
445         for (i=0; i<out_key->size; ++i) {
446                 ch = 0;
447                 memcpy(&ch, out_key->data+i, 1);
448                 printf("%02X ", (int) ch);
449         }
450         printf("\n");
451
452         printf(" in_data:                         ");
453         for (i=0; i<in_data->size; ++i) {
454                 ch = 0;
455                 memcpy(&ch, in_data->data+i, 1);
456                 printf("%02X ", (int) ch);
457         }
458         printf("\n");
459
460         printf("out_data: ");
461         for (i=0; i<out_data->size; ++i) {
462                 ch = 0;
463                 memcpy(&ch, out_data->data+i, 1);
464                 printf("%02X ", (int) ch);
465         }
466         printf("\n");
467
468
469 }
470
471
472 // convert users-by-number records
473 void convert_usersbynumber(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
474
475         // key is a long
476         // and remember ... "long" is int32_t on the source system
477         int32_t in_usernum;
478         long out_usernum;
479         memcpy(&in_usernum, in_key->data, sizeof(in_usernum));
480         out_usernum = (long) in_usernum;
481
482         if (in_key->size != 4) {
483                 printf("\033[31m\033[1m *** SOURCE DATABASE IS NOT 32-BIT *** ABORTING *** \033[0m\n");
484                 abort();
485         }
486
487         out_key->size = sizeof(out_usernum);
488         out_key->data = realloc(out_key->data, out_key->size);
489         memcpy(out_key->data, &out_usernum, sizeof(out_usernum));
490
491         // value is a string
492         out_data->size = in_data->size;
493         out_data->data = realloc(out_data->data, out_data->size);
494         memcpy(out_data->data, in_data->data, in_data->size);
495
496         printf("usersbynumber: %ld --> %s\n", out_usernum, (char *)out_data->data);
497 }
498
499
500 // convert function for a config record
501 void convert_config(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
502
503         // the key is a string
504         out_key->size = in_key->size;
505         out_key->data = realloc(out_key->data, out_key->size + 1);
506         memcpy(out_key->data, in_key->data, in_key->size);
507         char *k = (char *)out_key->data;
508         k[out_key->size] = 0;
509
510         // the data is a pair of strings
511         out_data->size = in_data->size;
512         out_data->data = realloc(out_data->data, out_data->size + 1);
513         memcpy(out_data->data, in_data->data, in_data->size);
514         char *d = (char *)out_data->data;
515         d[out_data->size] = 0;
516
517         // please excuse my friend, he isn't null terminated
518         printf("\033[32m\033[1mConfig entry: %s -> %s\033[0m\n", (char *)out_key->data, (char *)out_data->data+strlen(out_data->data)+1);
519 }
520
521
522 // For obsolete databases, zero all the output
523 void zero_function(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) {
524         out_key->size = 0;
525         out_data->size = 0;
526 }
527
528
529 void (*convert_functions[])(int which_cdb, DBT *in_key, DBT *in_data, DBT *out_key, DBT *out_data) = {
530         convert_msgmain,        // CDB_MSGMAIN
531         convert_users,          // CDB_USERS
532         convert_rooms,          // CDB_ROOMS
533         convert_floors,         // CDB_FLOORTAB
534         convert_msglists,       // CDB_MSGLISTS
535         convert_visits,         // CDB_VISIT
536         convert_dir,            // CDB_DIRECTORY
537         convert_usetable,       // CDB_USETABLE
538         convert_bigmsgs,        // CDB_BIGMSGS
539         convert_msglists,       // CDB_FULLTEXT
540         convert_euidindex,      // CDB_EUIDINDEX
541         convert_usersbynumber,  // CDB_USERSBYNUMBER
542         zero_function,          // CDB_UNUSED1 (obsolete)
543         convert_config          // CDB_CONFIG
544 };
545
546
547 void convert_table(int which_cdb, DB_ENV *src_dbenv, DB_ENV *dst_dbenv) {
548         int ret;
549         int compressed;
550         char dbfilename[32];
551         uLongf destLen = 0;
552
553         printf("\033[43m\033[K\033[0m\n");
554         printf("\033[43m\033[30m Converting table %02x \033[K\033[0m\n", which_cdb);
555         printf("\033[43m\033[K\033[0m\n");
556
557         // shamelessly swiped from https://docs.oracle.com/database/bdb181/html/programmer_reference/am_cursor.html
558         DB *src_dbp, *dst_dbp;
559         DBC *src_dbcp;
560         DBT in_key, in_data, out_key, out_data, uncomp_data, recomp_data;
561         int num_rows = 0;
562
563         // create a database handle for the source table
564         ret = db_create(&src_dbp, src_dbenv, 0);
565         if (ret) {
566                 printf("db: db_create: %s\n", db_strerror(ret));
567                 printf("db: exit code %d\n", ret);
568                 exit(CTDLEXIT_DB);
569         }
570
571         // open the file containing the source table
572         snprintf(dbfilename, sizeof dbfilename, "cdb.%02x", which_cdb);
573         printf("\033[33m\033[1mdb: opening %s\033[0m\n", dbfilename);
574         ret = src_dbp->open(src_dbp, NULL, dbfilename, NULL, DB_BTREE, 0, 0600);
575         if (ret) {
576                 printf("db: db_open: %s\n", db_strerror(ret));
577                 printf("db: exit code %d\n", ret);
578                 exit(CTDLEXIT_DB);
579         }
580
581         // create a database handle for the destination table
582         ret = db_create(&dst_dbp, dst_dbenv, 0);
583         if (ret) {
584                 printf("db: db_create: %s\n", db_strerror(ret));
585                 printf("db: exit code %d\n", ret);
586                 exit(CTDLEXIT_DB);
587         }
588
589         // open the file containing the destination table
590         snprintf(dbfilename, sizeof dbfilename, "cdb.%02x", which_cdb);
591         printf("\033[33m\033[1mdb: opening %s\033[0m\n", dbfilename);
592         ret = dst_dbp->open(dst_dbp, NULL, dbfilename, NULL, DB_BTREE, (DB_CREATE | DB_TRUNCATE), 0600);
593         if (ret) {
594                 printf("db: db_open: %s\n", db_strerror(ret));
595                 printf("db: exit code %d\n", ret);
596                 exit(CTDLEXIT_DB);
597         }
598
599         // Acquire a cursor to read the source table
600         if ((ret = src_dbp->cursor(src_dbp, NULL, &src_dbcp, 0)) != 0) {
601                 printf("db: db_cursor: %s\n", db_strerror(ret));
602                 printf("db: exit code %d\n", ret);
603                 exit(CTDLEXIT_DB);
604         }
605
606         // Zero out these database keys
607         memset(&in_key,         0, sizeof(DBT));        // input
608         memset(&in_data,        0, sizeof(DBT));
609         memset(&out_key,        0, sizeof(DBT));        // output
610         memset(&out_data,       0, sizeof(DBT));
611         memset(&uncomp_data,    0, sizeof(DBT));        // decompressed input (the key doesn't change)
612         memset(&recomp_data,    0, sizeof(DBT));        // recompressed input (the key doesn't change)
613
614         // Walk through the database, calling convert functions as we go and clearing buffers before each call.
615         while (out_key.size = 0, out_data.size = 0, (ret = src_dbcp->get(src_dbcp, &in_key, &in_data, DB_NEXT)) == 0) {
616
617                 // Do we need to decompress?
618                 static int32_t magic = COMPRESS_MAGIC;
619                 compressed = 0;
620                 if ( (in_data.size >= sizeof(struct CtdlCompressHeader_32)) && (!memcmp(in_data.data, &magic, sizeof(magic))) ) {
621
622                         // yes, we need to decompress
623                         compressed = 1;
624                         struct CtdlCompressHeader_32 comp32;
625                         memcpy(&comp32, in_data.data, sizeof(struct CtdlCompressHeader_32));
626                         uncomp_data.size = comp32.uncompressed_len;
627                         uncomp_data.data = realloc(uncomp_data.data, uncomp_data.size);
628                         destLen = (uLongf)comp32.uncompressed_len;
629
630                         ret = uncompress((Bytef *)uncomp_data.data, (uLongf *)&destLen, (const Bytef *)in_data.data+sizeof(struct CtdlCompressHeader_32), (uLong)comp32.compressed_len);
631                         if (ret != Z_OK) {
632                                 printf("db: uncompress() error %d\n", ret);
633                                 exit(CTDLEXIT_DB);
634                         }
635                         printf("DB: %02x ,  in_keylen: %-3d ,  in_datalen: %-10d , dataptr: %012lx \033[31m(decompressed)\033[0m\n", which_cdb, (int)in_key.size, comp32.uncompressed_len, (long unsigned int)uncomp_data.data);
636                 }
637                 else {
638                         printf("DB: %02x ,  in_keylen: %-3d ,  in_datalen: %-10d , dataptr: %012lx\n", which_cdb, (int)in_key.size, (int)in_data.size, (long unsigned int)in_data.data);
639                 }
640
641                 // Call the convert function registered to this table
642                 convert_functions[which_cdb](which_cdb, &in_key, (compressed ? &uncomp_data : &in_data), &out_key, &out_data);
643
644                 // The logic here is that if the source data was compressed, we compress the output too
645                 if (compressed) {
646                         struct CtdlCompressHeader zheader;
647                         memset(&zheader, 0, sizeof(struct CtdlCompressHeader));
648                         zheader.magic = COMPRESS_MAGIC;
649                         zheader.uncompressed_len = out_data.size;
650                         recomp_data.data = realloc(recomp_data.data, ((out_data.size * 101) / 100) + 100 + sizeof(struct CtdlCompressHeader));
651
652                         if (compress2((Bytef *)(recomp_data.data + sizeof(struct CtdlCompressHeader)), &destLen, (Bytef *)out_data.data, (uLongf) out_data.size, 1) != Z_OK) {
653                                 printf("db: compress() error\n");
654                                 exit(CTDLEXIT_DB);
655                         }
656                         recomp_data.size = destLen;
657                 }
658
659                 // write the converted record to the target database
660                 if (out_key.size > 0) {
661
662
663                         // If we compressed the output, write recomp_data instead of out_data
664                         if (compressed) {
665                                 printf("DB: %02x , out_keylen: %-3d , out_datalen: %-10d , dataptr: %012lx \033[31m(compressed)\033[0m\n", which_cdb, (int)out_key.size, (int)recomp_data.size, (long unsigned int)recomp_data.data);
666                                 ret = dst_dbp->put(dst_dbp, NULL, &out_key, &recomp_data, 0);
667                         }
668                         else {
669                                 printf("DB: %02x , out_keylen: %-3d , out_datalen: %-10d , dataptr: %012lx\n", which_cdb, (int)out_key.size, (int)out_data.size, (long unsigned int)out_data.data);
670                                 ret = dst_dbp->put(dst_dbp, NULL, &out_key, &out_data, 0);
671                         }
672
673
674                         if (ret) {
675                                 printf("db: cdb_put(%d): %s", which_cdb, db_strerror(ret));
676                                 exit(CTDLEXIT_DB);
677                         }
678
679                         // Knowing the total number of rows isn't critical to the program.  It's just for the user to know.
680                         ++num_rows;
681                 }
682         }
683
684         if (ret != DB_NOTFOUND) {
685                 printf("db: db_get: %s\n", db_strerror(ret));
686                 printf("db: exit code %d\n", ret);
687                 exit(CTDLEXIT_DB);
688         }
689
690         printf("%d rows\n", num_rows);
691
692         // free any leftover out_data pointers
693         free(out_key.data);
694         free(out_data.data);
695         free(uncomp_data.data);
696         free(recomp_data.data);
697
698         // Flush the logs...
699         //printf("\033[33m\033[1mdb: flushing the database logs\033[0m\n");
700         //if ((ret = src_dbenv->log_flush(src_dbenv, NULL))) {
701                 //printf("db: log_flush: %s\n", db_strerror(ret));
702         //}
703
704         // ...and close the database (table)
705         printf("\033[33m\033[1mdb: closing database %02x\033[0m\n", which_cdb);
706         ret = src_dbp->close(src_dbp, 0);
707         if (ret) {
708                 printf("db: db_close: %s\n", db_strerror(ret));
709         }
710
711 }
712
713
714 int main(int argc, char **argv) {
715         char *src_dir = NULL;
716         char *dst_dir = NULL;
717         int confirmed = 0;
718         static DB_ENV *src_dbenv;               // Source DB environment (global)
719         static DB_ENV *dst_dbenv;               // Source DB environment (global)
720
721         // Check to make sure we're running on the target 64-bit system
722         if (sizeof(void *) != 8) {
723                 fprintf(stderr, "%s: this is a %ld-bit system.\n", argv[0], sizeof(void *)*8);
724                 fprintf(stderr, "%s: you must run this on a 64-bit system, onto which a 32-bit database has been copied.\n", argv[0]);
725                 exit(1);
726         }
727
728         // Parse command line
729         int a;
730         while ((a = getopt(argc, argv, "s:d:y")) != EOF) {
731                 switch (a) {
732                 case 's':
733                         src_dir = optarg;
734                         break;
735                 case 'd':
736                         dst_dir = optarg;
737                         break;
738                 case 'y':
739                         confirmed = 1;
740                         break;
741                 default:
742                         fprintf(stderr, "%s: usage: %s -s source_dir -d dest_dir\n", argv[0], argv[0]);
743                         exit(2);
744                 }
745         }
746
747         // Warn the user
748         printf("------------------------------------------------------------------------\n");
749         printf("ctdl3264 s a Citadel database written on a 32-bit system to one  \n");
750         printf("that can be run on a 64-bit system.  It is intended to be run OFFLINE.  \n");
751         printf("Neither the source nor the target data directories should be mounted by \n");
752         printf("a running Citadel server.  We \033[1mguarantee\033[0m data corruption if you do not   \n");
753         printf("observe this warning!  The source [-s] directory should contain a copy  \n");
754         printf("of the database from your 32-bit system.  The destination [-d] directory\n");
755         printf("should be empty and will receive your 64-bit database.                  \n");
756         printf("------------------------------------------------------------------------\n");
757         printf("     Source 32-bit directory: %s\n", src_dir);
758         printf("Destination 64-bit directory: %s\n", dst_dir);
759         printf("------------------------------------------------------------------------\n");
760
761         if (confirmed == 1) {
762                 printf("You have specified the [-y] flag, so processing will continue.\n");
763         }
764         else {
765                 printf("Please read [ https://www.citadel.org/ctdl3264.html ] to learn how to proceed.\n");
766                 exit(0);
767         }
768
769         src_dbenv = open_dbenv(src_dir);
770         dst_dbenv = open_dbenv(dst_dir);
771         for (int i = 0; i < MAXCDB; ++i) {
772                 convert_table(i, src_dbenv, dst_dbenv);
773         }
774         close_dbenv(src_dbenv);
775         close_dbenv(dst_dbenv);
776
777         exit(0);
778 }