Coverage Report

Created: 2026-01-17 08:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mdbtools/src/libmdb/props.c
Line
Count
Source
1
/* MDB Tools - A library for reading MS Access database file
2
 * Copyright (C) 2000-2011 Brian Bruns and others
3
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Library General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2 of the License, or (at your option) any later version.
8
 *
9
 * This library is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Library General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Library General Public
15
 * License along with this library; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17
 */
18
19
#include "mdbtools.h"
20
21
static GPtrArray *
22
mdb_read_props_list(MdbHandle *mdb, gchar *kkd, int len)
23
50
{
24
50
  guint32 record_len;
25
50
  int pos = 0;
26
50
  gchar *name;
27
50
  GPtrArray *names = NULL;
28
50
  int i=0;
29
30
50
  names = g_ptr_array_new();
31
#if MDB_DEBUG
32
  mdb_buffer_dump(kkd, 0, len);
33
#endif
34
50
  pos = 0;
35
100
  while (pos < len) {
36
50
    record_len = mdb_get_int16(kkd, pos);
37
50
    pos += 2;
38
50
    if (mdb_get_option(MDB_DEBUG_PROPS)) {
39
0
      fprintf(stderr, "%02d ",i++);
40
0
      mdb_buffer_dump(kkd, pos - 2, record_len + 2);
41
0
    }
42
50
    name = g_malloc(3*record_len + 1); /* worst case scenario is 3 bytes out per byte in */
43
50
    mdb_unicode2ascii(mdb, &kkd[pos], record_len, name, 3*record_len + 1);
44
45
50
    pos += record_len;
46
50
    g_ptr_array_add(names, name);
47
#if MDB_DEBUG
48
    printf("new len = %d\n", names->len);
49
#endif
50
50
  }
51
50
  return names;
52
50
}
53
static void
54
free_hash_entry(gpointer key, gpointer value, gpointer user_data)
55
3
{
56
3
  g_free(key);
57
3
  g_free(value);
58
3
}
59
void
60
mdb_free_props(MdbProperties *props)
61
49
{
62
49
  if (!props) return;
63
64
49
  if (props->name) g_free(props->name);
65
49
  if (props->hash) {
66
49
    g_hash_table_foreach(props->hash, free_hash_entry, 0);
67
49
    g_hash_table_destroy(props->hash);
68
49
  }
69
49
  g_free(props);
70
49
}
71
72
static void
73
50
do_g_free(gpointer ptr, gpointer user_data) {
74
50
  g_free(ptr);
75
50
}
76
77
static void
78
50
free_names(GPtrArray *names) {
79
50
  g_ptr_array_foreach(names, do_g_free, NULL);
80
50
  g_ptr_array_free(names, TRUE);
81
50
}
82
MdbProperties *
83
mdb_alloc_props(void)
84
49
{
85
49
  MdbProperties *props;
86
87
49
  props = g_malloc0(sizeof(MdbProperties));
88
89
49
  return props;
90
49
}
91
static MdbProperties *
92
mdb_read_props(MdbHandle *mdb, GPtrArray *names, gchar *kkd, int len)
93
49
{
94
49
  guint32 record_len, name_len;
95
49
  int pos = 0;
96
49
  guint elem;
97
49
  int dtype, dsize;
98
49
  gchar *name, *value;
99
49
  MdbProperties *props;
100
49
  int i=0;
101
102
#if MDB_DEBUG
103
  mdb_buffer_dump(kkd, 0, len);
104
#endif
105
49
  pos = 0;
106
107
49
  record_len = mdb_get_int16(kkd, pos);
108
49
  pos += 4;
109
49
  name_len = mdb_get_int16(kkd, pos);
110
49
  pos += 2;
111
49
  props = mdb_alloc_props();
112
49
  if (name_len) {
113
46
    props->name = g_malloc(3*name_len + 1);
114
46
    mdb_unicode2ascii(mdb, kkd+pos, name_len, props->name, 3*name_len + 1);
115
46
    mdb_debug(MDB_DEBUG_PROPS,"prop block named: %s", props->name);
116
46
  }
117
49
  pos += name_len;
118
119
49
  props->hash = g_hash_table_new(g_str_hash, g_str_equal);
120
121
52
  while (pos < len) {
122
4
    record_len = mdb_get_int16(kkd, pos);
123
4
    dtype = kkd[pos + 3];
124
4
    elem = mdb_get_int16(kkd, pos + 4);
125
4
    if (elem >= names->len)
126
1
      break;
127
3
    dsize = mdb_get_int16(kkd, pos + 6);
128
3
    if (dsize < 0 || pos + 8 + dsize > len)
129
0
      break;
130
3
    value = g_strdup_printf("%.*s", dsize, &kkd[pos+8]);
131
3
    name = g_ptr_array_index(names,elem);
132
3
    if (mdb_get_option(MDB_DEBUG_PROPS)) {
133
0
      fprintf(stderr, "%02d ",i++);
134
0
      mdb_debug(MDB_DEBUG_PROPS,"elem %d (%s) dsize %d dtype %d", elem, name, dsize, dtype);
135
0
      mdb_buffer_dump(value, 0, dsize);
136
0
    }
137
3
    if (dtype == MDB_MEMO) {
138
0
      dtype = MDB_TEXT;
139
3
    } else if (dtype == MDB_BINARY && dsize == 16 && strcmp(name, "GUID") == 0) {
140
0
      dtype = MDB_REPID;
141
0
    }
142
3
    if (dtype == MDB_BOOL) {
143
0
      g_hash_table_insert(props->hash, g_strdup(name),
144
0
        g_strdup(kkd[pos + 8] ? "yes" : "no"));
145
3
    } else if (dtype == MDB_BINARY || dtype == MDB_OLE) {
146
0
      g_hash_table_insert(props->hash, g_strdup(name),
147
0
        g_strdup_printf("(binary data of length %d)", dsize));
148
3
    } else {
149
3
      g_hash_table_insert(props->hash, g_strdup(name),
150
3
        mdb_col_to_string(mdb, kkd, pos + 8, dtype, dsize));
151
3
    }
152
3
    g_free(value);
153
3
    pos += record_len;
154
3
  }
155
49
  return props;
156
  
157
49
}
158
159
static void
160
print_keyvalue(gpointer key, gpointer value, gpointer outfile)
161
0
{
162
0
    fprintf((FILE*)outfile,"\t%s: %s\n", (gchar *)key, (gchar *)value);
163
0
}
164
void
165
0
mdb_dump_props(MdbProperties *props, FILE *outfile, int show_name) {
166
0
  if (show_name)
167
0
    fprintf(outfile,"name: %s\n", props->name ? props->name : "(none)");
168
0
  g_hash_table_foreach(props->hash, print_keyvalue, outfile);
169
0
  if (show_name)
170
0
    fputc('\n', outfile);
171
0
}
172
173
/*
174
 * That function takes a raw KKD/MR2 binary buffer,
175
 * typically read from LvProp in table MSysbjects
176
 * and returns a GPtrArray of MdbProps*
177
 */
178
GPtrArray*
179
4.90k
mdb_kkd_to_props(MdbHandle *mdb, void *buffer, size_t len) {
180
4.90k
  guint32 record_len;
181
4.90k
  guint16 record_type;
182
4.90k
  size_t pos;
183
4.90k
  GPtrArray *names = NULL;
184
4.90k
  MdbProperties *props;
185
4.90k
  GPtrArray *result;
186
187
#if MDB_DEBUG
188
  mdb_buffer_dump(buffer, 0, len);
189
#endif
190
4.90k
  mdb_debug(MDB_DEBUG_PROPS,"starting prop parsing of type %s", buffer);
191
192
4.90k
  if (strcmp("KKD", buffer) && strcmp("MR2", buffer)) {
193
1.92k
    fprintf(stderr, "Unrecognized format.\n");
194
1.92k
    mdb_buffer_dump(buffer, 0, len);
195
1.92k
    return NULL;
196
1.92k
  }
197
198
2.97k
  result = g_ptr_array_new();
199
200
2.97k
  pos = 4;
201
3.63k
  while (pos < len) {
202
653
    record_len = mdb_get_int32(buffer, pos);
203
653
    record_type = mdb_get_int16(buffer, pos + 4);
204
653
    mdb_debug(MDB_DEBUG_PROPS,"prop chunk type:0x%04x len:%d", record_type, record_len);
205
    //mdb_buffer_dump(buffer, pos+4, record_len);
206
653
    switch (record_type) {
207
50
      case 0x80:
208
50
        if (names) free_names(names);
209
50
        names = mdb_read_props_list(mdb, (char*)buffer+pos+6, record_len - 6);
210
50
        break;
211
49
      case 0x00:
212
49
      case 0x01:
213
49
      case 0x02:
214
49
        if (!names) {
215
0
          fprintf(stderr,"sequence error!\n");
216
0
          break;
217
0
        }
218
49
        props = mdb_read_props(mdb, names, (char*)buffer+pos+6, record_len - 6);
219
49
        g_ptr_array_add(result, props);
220
        //mdb_dump_props(props, stderr, 1);
221
49
        break;
222
554
      default:
223
554
        fprintf(stderr,"Unknown record type %d\n", record_type);
224
554
        break;
225
653
    }
226
653
    pos += record_len;
227
653
  }
228
2.97k
  if (names) free_names(names);
229
2.97k
  return result;
230
2.97k
}