ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/modules/database/standard.c
Revision: 3389
Committed: Fri Apr 25 14:12:15 2014 UTC (11 years, 4 months ago) by michael
Content type: text/x-csrc
File size: 29887 byte(s)
Log Message:
- Imported ircservices-5.1.24

File Contents

# Content
1 /* Routines to load/save Services databases in standard format.
2 *
3 * IRC Services is copyright (c) 1996-2009 Andrew Church.
4 * E-mail: <achurch@achurch.org>
5 * Parts written by Andrew Kempe and others.
6 * This program is free but copyrighted software; see the file GPL.txt for
7 * details.
8 */
9
10 #include "services.h"
11 #include "modules.h"
12 #include "conffile.h"
13 #include "databases.h"
14 #include "encrypt.h"
15
16 #include "fileutil.h"
17
18 #define SAFE(x) do { if ((x) < 0) goto fail; } while (0)
19
20 /*************************************************************************/
21
22 /* Database file format.
23 * Note: all integer values are big-endian.
24 *
25 * File header:
26 * 4 bytes -- version number (uint32)
27 * 4 bytes -- size of header (uint32)
28 * 4 bytes -- absolute offset to field list (uint32)
29 * 4 bytes -- absolute offset to first record descriptor table (uint32)
30 *
31 * Field list:
32 * 4 bytes -- size of field list (uint32)
33 * 4 bytes -- number of fields (uint32)
34 * 4 bytes -- size of a single record == sum of field sizes (uint32)
35 * N bytes -- field definitions
36 *
37 * Field definition:
38 * 4 bytes -- field data size in bytes
39 * 2 bytes -- field data type (DBTYPE_*) (uint16)
40 * 2 bytes -- length of field name, including trailing \0 (uint16)
41 * N bytes -- field name
42 *
43 * Record descriptor table:
44 * 4 bytes -- absolute offset to next table, or 0 if none (uint32)
45 * 4 bytes -- size of this table in bytes
46 * N bytes -- record entries
47 *
48 * Entry in record descriptor table:
49 * 4 bytes -- absolute offset to record data
50 * 4 bytes -- total length of record, including any strings
51 *
52 * Record data:
53 * N bytes -- main record data
54 * P bytes -- first string
55 * Q bytes -- second string
56 * ...
57 *
58 * Field data:
59 * DBTYPE_INT8: 8-bit value
60 * DBTYPE_UINT8: 8-bit value
61 * DBTYPE_INT16: 16-bit value
62 * DBTYPE_UINT16: 16-bit value
63 * DBTYPE_INT32: 32-bit value
64 * DBTYPE_UINT32: 32-bit value
65 * DBTYPE_TIME: 64-bit value
66 * DBTYPE_STRING: 32-bit offset to string from start of record
67 * DBTYPE_BUFFER: N-byte buffer contents
68 * DBTYPE_PASSWORD: 32-bit cipher string offset, (N-4) byte password buffer
69 *
70 * String data:
71 * 2 bytes -- string length including trailing \0 (uint16), 0 if NULL
72 * N bytes -- string contents
73 */
74
75 #define NEWDB_VERSION ('I'<<24 | 'S'<<16 | 'D'<<8 | 1) /* IRC Services DB */
76 #define RECTABLE_LEN 0x400 /* anything will do, really */
77 #define RECTABLE_SIZE (RECTABLE_LEN*8)
78
79 /* Structure for keeping track of where to put data: */
80 typedef struct {
81 const DBTable *table;
82 int nfields;
83 struct {
84 DBField *field;
85 int32 offset; /* -1 if not found on load */
86 int rawsize; /* native size on system */
87 int filesize; /* size when written to file */
88 } *fields;
89 } TableInfo;
90
91 /*************************************************************************/
92 /*********************** Internal helper routines ************************/
93 /*************************************************************************/
94
95 /* Helper routine: creates a TableInfo for a table. */
96
97 static TableInfo *create_tableinfo(const DBTable *table)
98 {
99 TableInfo *ti;
100 int i;
101
102 ti = malloc(sizeof(*ti));
103 if (!ti) {
104 module_log("create_tableinfo(): Out of memory for table %s",
105 table->name);
106 return NULL;
107 }
108 ti->table = table;
109 for (i = 0; table->fields[i].name; i++)
110 ;
111 ti->nfields = i;
112 ti->fields = malloc(sizeof(*ti->fields) * ti->nfields);
113 for (i = 0; i < ti->nfields; i++) {
114 uint32 fieldsize;
115 int rawsize = 0;
116 ti->fields[i].field = &table->fields[i];
117 switch (ti->fields[i].field->type) {
118 case DBTYPE_INT8:
119 case DBTYPE_UINT8: fieldsize = 1; break;
120 case DBTYPE_INT16:
121 case DBTYPE_UINT16: fieldsize = 2; break;
122 case DBTYPE_INT32:
123 case DBTYPE_UINT32: fieldsize = 4; break;
124 case DBTYPE_TIME: fieldsize = 8; rawsize = sizeof(time_t); break;
125 case DBTYPE_STRING: fieldsize = 4; rawsize = sizeof(char *); break;
126 case DBTYPE_BUFFER: fieldsize = ti->fields[i].field->length; break;
127 case DBTYPE_PASSWORD: fieldsize = PASSMAX+4;
128 rawsize = sizeof(Password); break;
129 default:
130 module_log("create_tableinfo(): Invalid type (%d) for field %s"
131 " in table %s", ti->fields[i].field->type,
132 ti->fields[i].field->name, ti->table->name);
133 return NULL;
134 }
135 if (!rawsize)
136 rawsize = fieldsize;
137 ti->fields[i].rawsize = rawsize;
138 ti->fields[i].filesize = fieldsize;
139 ti->fields[i].offset = -1;
140 }
141 return ti;
142 }
143
144 /*************************************************************************/
145
146 /* Helper routine: frees a TableInfo. */
147
148 static void free_tableinfo(TableInfo *ti)
149 {
150 if (ti) {
151 free(ti->fields);
152 free(ti);
153 }
154 }
155
156 /*************************************************************************/
157
158 /* Helper routine: returns the filename for a table. */
159
160 static const char *make_filename(const DBTable *table)
161 {
162 static const char okchars[] = /* characters allowed in filenames */
163 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
164 static char namebuf[1000];
165 const char *s;
166 char *d = namebuf;
167
168 for (s = table->name; *s && d-namebuf < sizeof(namebuf)-5; s++, d++) {
169 if (strchr(okchars, *s))
170 *d = *s;
171 else
172 *d = '_';
173 }
174 strcpy(d, ".sdb"); /* safe: space left for this above */
175 return namebuf;
176 }
177
178 /*************************************************************************/
179 /*********************** Database loading routines ***********************/
180 /*************************************************************************/
181
182 static int read_file_header(dbFILE *f, uint32 *fieldofs_ret,
183 uint32 *recofs_ret);
184 static int read_field_list(TableInfo *ti, dbFILE *f, uint32 *recsize_ret);
185 static int read_records(TableInfo *ti, dbFILE *f, uint32 recsize);
186
187 static int standard_load_table(DBTable *table)
188 {
189 TableInfo *ti;
190 const char *filename;
191 dbFILE *f;
192 uint32 fieldofs; /* field list offset */
193 uint32 recofs; /* record descriptor table offset */
194 uint32 recsize; /* size of a record, less strings */
195
196 /* Set up the TableInfo structure */
197 ti = create_tableinfo(table);
198 if (!ti)
199 return 0;
200
201 /* Open the file */
202 filename = make_filename(table);
203 f = open_db(filename, "r", NEWDB_VERSION);
204 if (!f) {
205 module_log_perror("Can't open %s for reading", filename);
206 free_tableinfo(ti);
207 return 0;
208 }
209
210 /* Check the file version and header size, and read header fields */
211 if (!read_file_header(f, &fieldofs, &recofs))
212 goto fail;
213
214 /* Read in the field list and make sure it fits with the DBTable */
215 if (fseek(f->fp, fieldofs, SEEK_SET) < 0) {
216 module_log_perror("Can't seek in %s", filename);
217 goto fail;
218 }
219 if (!read_field_list(ti, f, &recsize))
220 goto fail;
221
222 /* Read in the records */
223 if (fseek(f->fp, recofs, SEEK_SET) < 0) {
224 module_log_perror("Can't seek in %s", filename);
225 goto fail;
226 }
227 if (!read_records(ti, f, recsize))
228 goto fail;
229
230 /* Call the postload routine, if any */
231 if (table->postload && !(*table->postload)()) {
232 module_log_perror("Table %s postload routine failed", table->name);
233 goto fail;
234 }
235
236 /* Done! */
237 close_db(f);
238 free_tableinfo(ti);
239 return 1;
240
241 fail:
242 close_db(f);
243 free_tableinfo(ti);
244 return 0;
245 }
246
247 /*************************************************************************/
248
249 /* Read the file header and verify the version and header size, then return
250 * the field list offset and record descriptor table offset.
251 */
252
253 static int read_file_header(dbFILE *f, uint32 *fieldofs_ret,
254 uint32 *recofs_ret)
255 {
256 uint32 version; /* version number stored in file */
257 uint32 hdrsize; /* file header size */
258
259 SAFE(read_uint32(&version, f));
260 if (version != NEWDB_VERSION) {
261 module_log("Bad version number on %s", f->filename);
262 return 0;
263 }
264 SAFE(read_uint32(&hdrsize, f));
265 if (hdrsize < 16) {
266 module_log("Bad header size on %s", f->filename);
267 return 0;
268 }
269 SAFE(read_uint32(fieldofs_ret, f));
270 SAFE(read_uint32(recofs_ret, f));
271 return 1;
272
273 fail:
274 module_log("Read error on %s", f->filename);
275 return 0;
276 }
277
278 /*************************************************************************/
279
280 /* Read in the field list, matching it with the field list in the DBTable
281 * structure and filling in field offsets. The record size (as stored in
282 * the file) is returned in *recsize_ret.
283 */
284
285 static int read_field_list(TableInfo *ti, dbFILE *f, uint32 *recsize_ret)
286 {
287 uint32 thispos; /* current file position */
288 uint32 maxpos; /* end of table */
289 uint32 nfields;
290 int32 recofs = 0;
291 int i, j;
292
293 SAFE(read_uint32(&maxpos, f)); /* field list size */
294 maxpos += ftell(f->fp)-4;
295 SAFE(read_uint32(&nfields, f)); /* number of fields */
296 SAFE(read_uint32(recsize_ret, f)); /* record size--filled in later */
297 for (i = 0; i < nfields; i++) {
298 uint32 length;
299 uint16 type;
300 uint16 strsize;
301 char *fieldname;
302 SAFE((int32)(thispos = ftell(f->fp)));
303 SAFE(read_uint32(&length, f));
304 SAFE(read_uint16(&type, f));
305 SAFE(read_uint16(&strsize, f));
306 if (thispos+8+strsize > maxpos) {
307 module_log("load_table(): premature end of field list");
308 return 0;
309 }
310 SAFE(fseek(f->fp, -2, SEEK_CUR));
311 SAFE(read_string(&fieldname, f));
312 if (!fieldname) { /* impossible */
313 module_log("load_table(): BUG: field name is NULL");
314 return 0;
315 }
316 for (j = 0; j < ti->nfields; j++) {
317 DBField *field = ti->fields[j].field;
318 if (type == field->type
319 && length == ti->fields[j].filesize
320 && strcmp(field->name,fieldname) == 0
321 ) {
322 ti->fields[j].offset = recofs;
323 break;
324 }
325 }
326 free(fieldname);
327 recofs += length;
328 }
329 return 1;
330
331 fail:
332 module_log_perror("Error reading from %s", f->filename);
333 return 0;
334 }
335
336 /*************************************************************************/
337
338 /* Read in table records. */
339
340 static int read_records(TableInfo *ti, dbFILE *f, uint32 recsize)
341 {
342 uint32 *rectable; /* Table of record pointers and lengths */
343 int recnum; /* Record number within descriptor table */
344 int reccount; /* Record number in file */
345 void *record; /* Record pointer from first()/next() */
346 void *recbuf; /* Buffer for storing record data */
347 int i;
348
349 /* Allocate record buffer */
350 recbuf = malloc(recsize);
351 if (!recbuf)
352 return 0;
353
354 /* Variable init */
355 rectable = NULL;
356 reccount = 0;
357 record = NULL;
358
359 /* Termination check has to be performed in the middle of the loop,
360 * since we may need to read the record descriptor table first */
361 for (recnum = 0; ; recnum = (recnum+1) % (rectable[1]/8), reccount++) {
362
363 if (!recnum) { /* Start of a new record descriptor table */
364 uint32 nextofs, tablesize;
365
366 /* Seek to next table, if this isn't the first */
367 if (rectable) {
368 if (!rectable[0]) {
369 module_log("read_records(): %s: next table is 0!",
370 f->filename);
371 return 0;
372 }
373 SAFE(fseek(f->fp, rectable[0], SEEK_SET));
374 free(rectable);
375 rectable = NULL;
376 }
377
378 /* Read in and check the `next' pointer and table size */
379 SAFE(read_uint32(&nextofs, f));
380 SAFE(read_uint32(&tablesize, f));
381 if (!tablesize) {
382 module_log("read_records(): %s: rectable size is 0!",
383 f->filename);
384 return 0;
385 }
386
387 /* Allocate the table memory */
388 rectable = malloc(tablesize);
389 if (!rectable) {
390 module_log("read_records(): %s: no memory for rectable!",
391 f->filename);
392 return 0;
393 }
394
395 /* Read in the table */
396 rectable[0] = nextofs;
397 rectable[1] = tablesize;
398 for (i = 2; i < tablesize/4; i++)
399 SAFE(read_uint32(&rectable[i], f));
400
401 /* First record in the table is at index 1 */
402 recnum++;
403 }
404
405 /* If this is the last record, bail out */
406 if (!rectable[recnum*2])
407 break;
408
409 /* Make sure the record size is large enough */
410 if (rectable[recnum*2+1] < recsize) {
411 module_log("read_records(): %s: record %d is too small,"
412 " skipping", f->filename, reccount);
413 continue;
414 }
415
416 /* Allocate a new record */
417 record = ti->table->newrec();
418 if (!record) {
419 module_log("read_records(): %s: newrec() failed for record %d!",
420 f->filename, reccount);
421 return 0;
422 }
423
424 /* Seek to the location of this record and read the main record data */
425 SAFE(fseek(f->fp, rectable[recnum*2], SEEK_SET));
426 if (fread(recbuf, recsize, 1, f->fp) != 1)
427 goto fail;
428
429 /* Read fields from record buffer and strings from file */
430 for (i = 0; i < ti->nfields; i++) {
431 const DBField *field = ti->fields[i].field;
432 const void *src = (const int8 *)recbuf + ti->fields[i].offset;
433 if (ti->fields[i].offset < 0) /* field not present in file */
434 continue;
435 if (field->type == DBTYPE_STRING) {
436 uint32 strofs;
437 char *string;
438 strofs = ((uint8 *)src)[0] << 24
439 | ((uint8 *)src)[1] << 16
440 | ((uint8 *)src)[2] << 8
441 | ((uint8 *)src)[3];
442 if (strofs >= rectable[recnum*2+1]) {
443 module_log("read_records(): %s: string for field `%s' of"
444 " record %d is out of range, skipping",
445 f->filename, field->name, reccount);
446 continue;
447 }
448 SAFE(fseek(f->fp, rectable[recnum*2] + strofs, SEEK_SET));
449 SAFE(read_string(&string, f));
450 put_dbfield(record, field, &string);
451 } else if (field->type == DBTYPE_PASSWORD) {
452 uint32 strofs;
453 Password pass;
454 char *cipher;
455 strofs = ((uint8 *)src)[0] << 24
456 | ((uint8 *)src)[1] << 16
457 | ((uint8 *)src)[2] << 8
458 | ((uint8 *)src)[3];
459 if (strofs >= rectable[recnum*2+1]) {
460 module_log("read_records(): %s: string for field `%s' of"
461 " record %d is out of range, skipping",
462 f->filename, field->name, reccount);
463 continue;
464 }
465 SAFE(fseek(f->fp, rectable[recnum*2] + strofs, SEEK_SET));
466 SAFE(read_string((char **)&cipher, f));
467 init_password(&pass);
468 set_password(&pass, src+4, cipher);
469 free(cipher);
470 put_dbfield(record, field, &pass);
471 } else if (field->type == DBTYPE_BUFFER) {
472 put_dbfield(record, field, src);
473 } else {
474 char fieldbuf[16]; /* Big enough for any int or time type */
475 switch (field->type) {
476 case DBTYPE_INT8:
477 case DBTYPE_UINT8:
478 *((uint8 *)fieldbuf) = ((uint8 *)src)[0];
479 break;
480 case DBTYPE_INT16:
481 case DBTYPE_UINT16:
482 *((uint16 *)fieldbuf) = ((uint8 *)src)[0] << 8
483 | ((uint8 *)src)[1];
484 break;
485 case DBTYPE_INT32:
486 case DBTYPE_UINT32:
487 *((uint32 *)fieldbuf) = ((uint8 *)src)[0] << 24
488 | ((uint8 *)src)[1] << 16
489 | ((uint8 *)src)[2] << 8
490 | ((uint8 *)src)[3];
491 break;
492 case DBTYPE_TIME:
493 *((time_t *)fieldbuf) = ((uint8 *)src)[4] << 24
494 | ((uint8 *)src)[5] << 16
495 | ((uint8 *)src)[6] << 8
496 | ((uint8 *)src)[7];
497 #if SIZEOF_TIME_T >= 8
498 *((time_t *)fieldbuf) |=(time_t)((uint8 *)src)[0] << 56
499 | (time_t)((uint8 *)src)[1] << 48
500 | (time_t)((uint8 *)src)[2] << 40
501 | (time_t)((uint8 *)src)[3] << 32;
502 #endif
503 break;
504 case DBTYPE_STRING:
505 case DBTYPE_BUFFER:
506 case DBTYPE_PASSWORD:
507 /* Handled above--listed here to avoid warnings */
508 break;
509 }
510 put_dbfield(record, field, fieldbuf);
511 }
512 } /* for each field */
513
514 /* Add record to table */
515 ti->table->insert(record);
516 record = NULL;
517
518 } /* for each record */
519
520 /* All done */
521 free(recbuf);
522 free(rectable);
523 return 1;
524
525 fail:
526 module_log_perror("read_records(): Read error on %s", f->filename);
527 if (record)
528 ti->table->freerec(record);
529 free(recbuf);
530 free(rectable);
531 return 0;
532 }
533
534 /*************************************************************************/
535 /*********************** Database saving routines ************************/
536 /*************************************************************************/
537
538 static int write_file_header(TableInfo *ti, dbFILE *f);
539 static int write_field_list(TableInfo *ti, dbFILE *f, uint32 *recsize_ret);
540 static int write_records(TableInfo *ti, dbFILE *f, uint32 recsize);
541
542 static int standard_save_table(DBTable *table)
543 {
544 TableInfo *ti;
545 const char *filename;
546 dbFILE *f;
547 uint32 recsize; /* size of a record, less strings */
548
549 /* Set up the TableInfo structure */
550 ti = create_tableinfo(table);
551 if (!ti)
552 return 0;
553
554 /* Open the file */
555 filename = make_filename(table);
556 f = open_db(filename, "w", NEWDB_VERSION);
557 if (!f) {
558 module_log_perror("Can't open %s for writing", filename);
559 free_tableinfo(ti);
560 return 0;
561 }
562
563 /* Write the file header */
564 SAFE(write_file_header(ti, f));
565
566 /* Write the field list */
567 SAFE(write_field_list(ti, f, &recsize));
568
569 /* Write the actual records */
570 SAFE(write_records(ti, f, recsize));
571
572 /* Done! */
573 close_db(f);
574 free_tableinfo(ti);
575 return 1;
576
577 fail:
578 module_log_perror("Can't write to %s", filename);
579 restore_db(f);
580 free_tableinfo(ti);
581 return 0;
582 }
583
584 /*************************************************************************/
585
586 /* Write the file header. */
587
588 static int write_file_header(TableInfo *ti, dbFILE *f)
589 {
590 /* Note that the version number is already written */
591 SAFE(write_int32(16, f)); /* header size */
592 SAFE(write_int32(0, f)); /* field list offset--filled in later */
593 SAFE(write_int32(0, f)); /* record desc. table offset--filled in later */
594 return 0;
595 fail:
596 return -1;
597 }
598
599 /*************************************************************************/
600
601 /* Write the field list, filling in ti->fields[].offset as we go, and send
602 * back the size of a record; also fills in field list offset in file
603 * header.
604 */
605
606 static int write_field_list(TableInfo *ti, dbFILE *f, uint32 *recsize_ret)
607 {
608 uint32 listpos; /* file position of start of table */
609 uint32 endpos; /* file position of end of table */
610 uint32 recsize = 0;
611 int i, nfields = 0;
612
613 SAFE((int32)(listpos = ftell(f->fp)));
614 SAFE(fseek(f->fp, 8, SEEK_SET));
615 SAFE(write_int32(listpos, f));
616 SAFE(fseek(f->fp, listpos, SEEK_SET));
617 SAFE(write_int32(0, f)); /* field list size--filled in later*/
618 SAFE(write_int32(0, f)); /* number of fields--filled in later */
619 SAFE(write_int32(0, f)); /* record size--filled in later */
620 for (i = 0; i < ti->nfields; i++) {
621 if (ti->fields[i].field->load_only)
622 continue;
623 SAFE(write_int32(ti->fields[i].filesize, f));
624 SAFE(write_int16(ti->fields[i].field->type, f));
625 SAFE(write_string(ti->fields[i].field->name, f));
626 ti->fields[i].offset = recsize;
627 nfields++;
628 recsize += ti->fields[i].filesize;
629 }
630 SAFE((int32)(endpos = ftell(f->fp)));
631 SAFE(fseek(f->fp, listpos, SEEK_SET));
632 SAFE(write_int32(endpos-listpos, f));
633 SAFE(write_int32(nfields, f));
634 SAFE(write_int32(recsize, f));
635 SAFE(fseek(f->fp, endpos, SEEK_SET));
636 *recsize_ret = recsize;
637 return 0;
638 fail:
639 return -1;
640 }
641
642 /*************************************************************************/
643
644 /* Write out table records. */
645
646 static int write_records(TableInfo *ti, dbFILE *f, uint32 recsize)
647 {
648 uint32 listpos; /* Offset of current record descriptor table */
649 int recnum; /* Record number within table */
650 const void *record; /* Record pointer from first()/next() */
651 void *recbuf = NULL; /* Buffer for storing record data */
652 int i;
653
654 /* Allocate record buffer */
655 recbuf = malloc(recsize);
656 if (!recbuf)
657 return -1;
658
659 /* Variable init */
660 listpos = 12; /* First record descriptor table pointer is recorded here */
661 recnum = 0;
662
663 /* Note the lack of a termination condition in this loop--we need to
664 * write a 0 to terminate the record list, which may involve creating a
665 * new descriptor table, so we let the first part of the loop run and
666 * break out in the middle when we hit the end of the table. */
667 for (record = ti->table->first(); ; record = ti->table->next()) {
668 uint32 thispos; /* Offset this record is written at */
669 uint32 nextpos; /* Temp variable to hold next record's offset */
670
671 if (!recnum) { /* Start of a new rectable */
672 uint32 oldpos = listpos;
673 static char dummy_rectable[RECTABLE_SIZE];
674
675 /* Seek to a multiple of the table size, just for the hell of it */
676 SAFE((int32)(listpos = ftell(f->fp)));
677 listpos = (listpos + (RECTABLE_SIZE-1))
678 / RECTABLE_SIZE * RECTABLE_SIZE;
679 SAFE(fseek(f->fp, listpos, SEEK_SET));
680
681 /* Write out an empty descriptor table */
682 SAFE(write_buffer(dummy_rectable, f));
683 SAFE(fseek(f->fp, listpos, SEEK_SET));
684
685 /* Write the `next' pointer (currently 0) and size */
686 SAFE(write_int32(0, f));
687 SAFE(write_int32(RECTABLE_SIZE, f));
688
689 /* Write the new table offset into the old `next' pointer */
690 SAFE(fseek(f->fp, oldpos, SEEK_SET));
691 SAFE(write_int32(listpos, f));
692
693 /* Seek to where the next record should be written */
694 SAFE(fseek(f->fp, listpos + RECTABLE_SIZE, SEEK_SET));
695
696 /* The first record is written at index 1 (don't smash the
697 * next pointer) */
698 recnum = 1;
699 }
700
701 /* If this is the last record, record a 0 to terminate the record
702 * list and bail out */
703 if (!record) {
704 SAFE(fseek(f->fp, listpos + recnum*8, SEEK_SET));
705 SAFE(write_int32(0, f));
706 SAFE(write_int32(0, f));
707 break;
708 }
709
710 /* Save the location of this record */
711 SAFE((int32)(thispos = ftell(f->fp)));
712
713 /* Seek to end of record, for writing strings */
714 SAFE(fseek(f->fp, thispos + recsize, SEEK_SET));
715
716 /* Write fields to record buffer and strings to file */
717 for (i = 0; i < ti->nfields; i++) {
718 const DBField *field = ti->fields[i].field;
719 uint8 *dest = (uint8 *)recbuf + ti->fields[i].offset;
720 if (field->load_only)
721 continue;
722 if (field->type == DBTYPE_STRING) {
723 const char *string;
724 uint32 strofs;
725 get_dbfield(record, field, (void *)&string);
726 SAFE((int32)(strofs = ftell(f->fp)));
727 strofs -= thispos;
728 dest[0] = strofs >> 24;
729 dest[1] = strofs >> 16;
730 dest[2] = strofs >> 8;
731 dest[3] = strofs;
732 SAFE(write_string(string, f));
733 } else if (field->type == DBTYPE_PASSWORD) {
734 Password pass;
735 uint32 strofs;
736 get_dbfield(record, field, (void *)&pass);
737 SAFE((int32)(strofs = ftell(f->fp)));
738 strofs -= thispos;
739 dest[0] = strofs >> 24;
740 dest[1] = strofs >> 16;
741 dest[2] = strofs >> 8;
742 dest[3] = strofs;
743 SAFE(write_string(pass.cipher, f));
744 memcpy(dest+4, pass.password, PASSMAX);
745 } else if (field->type == DBTYPE_BUFFER) {
746 get_dbfield(record, field, dest);
747 } else {
748 uint8 fieldbuf[16]; /* Big enough for any int or time type */
749 get_dbfield(record, field, fieldbuf);
750 switch (field->type) {
751 case DBTYPE_INT8:
752 case DBTYPE_UINT8:
753 dest[0] = *((uint8 *)fieldbuf);
754 break;
755 case DBTYPE_INT16:
756 case DBTYPE_UINT16:
757 dest[0] = *((uint16 *)fieldbuf) >> 8;
758 dest[1] = *((uint16 *)fieldbuf);
759 break;
760 case DBTYPE_INT32:
761 case DBTYPE_UINT32:
762 dest[0] = *((uint32 *)fieldbuf) >> 24;
763 dest[1] = *((uint32 *)fieldbuf) >> 16;
764 dest[2] = *((uint32 *)fieldbuf) >> 8;
765 dest[3] = *((uint32 *)fieldbuf);
766 break;
767 case DBTYPE_TIME:
768 #if SIZEOF_TIME_T >= 8
769 dest[0] = *((time_t *)fieldbuf) >> 56;
770 dest[1] = *((time_t *)fieldbuf) >> 48;
771 dest[2] = *((time_t *)fieldbuf) >> 40;
772 dest[3] = *((time_t *)fieldbuf) >> 32;
773 #else
774 /* Copy the sign bit into the four high bytes */
775 dest[0] = (int32) *((time_t *)fieldbuf) >> 31;
776 dest[1] = (int32) *((time_t *)fieldbuf) >> 31;
777 dest[2] = (int32) *((time_t *)fieldbuf) >> 31;
778 dest[3] = (int32) *((time_t *)fieldbuf) >> 31;
779 #endif
780 dest[4] = *((time_t *)fieldbuf) >> 24;
781 dest[5] = *((time_t *)fieldbuf) >> 16;
782 dest[6] = *((time_t *)fieldbuf) >> 8;
783 dest[7] = *((time_t *)fieldbuf);
784 break;
785 case DBTYPE_STRING:
786 case DBTYPE_BUFFER:
787 case DBTYPE_PASSWORD:
788 /* Handled above--listed here to avoid warnings */
789 break;
790 }
791 }
792 } /* for each field */
793
794 /* Save the location of the next record */
795 SAFE((int32)(nextpos = ftell(f->fp)));
796
797 /* Write record pointer and length to table */
798 SAFE(fseek(f->fp, listpos + recnum*8, SEEK_SET));
799 SAFE(write_int32(thispos, f));
800 SAFE(write_int32(nextpos-thispos, f));
801
802 /* Write record buffer to file */
803 SAFE(fseek(f->fp, thispos, SEEK_SET));
804 if (fwrite(recbuf, recsize, 1, f->fp) != 1)
805 goto fail;
806
807 /* Seek to next record's starting position */
808 SAFE(fseek(f->fp, nextpos, SEEK_SET));
809
810 /* Move to the next record index */
811 recnum = (recnum+1) % RECTABLE_LEN;
812 }
813
814 /* All done */
815 free(recbuf);
816 return 0;
817
818 fail:
819 free(recbuf);
820 return -1;
821 }
822
823 /*************************************************************************/
824 /***************************** Module stuff ******************************/
825 /*************************************************************************/
826
827 #ifndef INCLUDE_IN_VERSION4
828
829 ConfigDirective module_config[] = {
830 { NULL }
831 };
832
833
834 static DBModule dbmodule_standard = {
835 .load_table = standard_load_table,
836 .save_table = standard_save_table,
837 };
838
839 /*************************************************************************/
840
841 int init_module(void)
842 {
843 if (!register_dbmodule(&dbmodule_standard)) {
844 module_log("Unable to register module with database core");
845 exit_module(0);
846 return 0;
847 }
848
849 return 1;
850 }
851
852 /*************************************************************************/
853
854 int exit_module(int shutdown)
855 {
856 unregister_dbmodule(&dbmodule_standard);
857 return 1;
858 }
859
860 /*************************************************************************/
861
862 #endif /* !INCLUDE_IN_VERSION4 */
863
864 /*
865 * Local variables:
866 * c-file-style: "stroustrup"
867 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
868 * indent-tabs-mode: nil
869 * End:
870 *
871 * vim: expandtab shiftwidth=4:
872 */