1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include "config.h"
30
31 #include "../../util/util.h"
32
33 #include <libxml/tree.h>
34
35 #define xstreq(a,b) xmlStrEqual(
BAD_CAST a,
BAD_CAST b)
36
37 static int pg_load_ext_dav_config(
38 ServerConfiguration *cfg,
39 pool_handle_t *pool,
40 PgRepository *repo,
41 const char *file);
42
43
44 static const char *sql_get_repository_root =
"select resource_id from Resource where parent_id is NULL and nodename = $1 ;";
45
46
47
48
49
50
51 int pg_lookup_root(ResourceData *res,
const char *rootnode,
int64_t *rootid) {
52 PGconn *connection = res->data;
53
54 PGresult *result = PQexecParams(
55 connection,
56 sql_get_repository_root,
57 1,
58 NULL,
59 &rootnode,
60 NULL,
61 NULL,
62 0);
63
64 if(!result) {
65 log_ereport(
LOG_FAILURE,
"pg: root lookup failed: %s", PQerrorMessage(connection));
66 return 1;
67 }
68
69 int ret =
0;
70
71 int nrows = PQntuples(result);
72 if(nrows ==
1) {
73 char *resource_id_str = PQgetvalue(result,
0,
0);
74 if(resource_id_str) {
75 if(!util_strtoint(resource_id_str, rootid)) {
76 log_ereport(
LOG_FAILURE,
"pg: unexpected result for column resource_id", rootnode);
77 ret =
1;
78 }
79 }
80 }
else {
81 log_ereport(
LOG_FAILURE,
"pg: cannot find root resource ''%s''", rootnode);
82 ret =
1;
83 }
84 PQclear(result);
85
86 return ret;
87 }
88
89 PgRepository* pg_init_repo(ServerConfiguration *cfg,
pool_handle_t *pool, WSConfigNode *config) {
90 CxAllocator *a = pool_allocator(pool);
91
92 ConfigNode *pg = serverconfig_get_node(config,
CONFIG_NODE_OBJECT, cx_str(
"Postgresql"));
93 if(!pg) {
94 log_ereport(
LOG_MISCONFIG,
"pg_init_repo: missing postgresql config object");
95 return NULL;
96 }
97
98 cxstring cfg_respool = serverconfig_object_directive_value(pg, cx_str(
"ResourcePool"));
99 cxstring cfg_rootid = serverconfig_object_directive_value(pg, cx_str(
"RootId"));
100 cxstring cfg_rootnode = serverconfig_object_directive_value(pg, cx_str(
"RootNode"));
101 cxstring cfg_dav = serverconfig_object_directive_value(pg, cx_str(
"PGDavConfig"));
102
103
104 if(cfg_respool.length ==
0) {
105 return NULL;
106 }
107
108
109 int64_t root_id =
1;
110 if(cfg_rootid.length >
0) {
111 if(!util_strtoint(cfg_rootid.ptr, &root_id)) {
112 log_ereport(
LOG_MISCONFIG,
"pg_init_repo: RootId parameter is not an integer: %s", cfg_rootid.ptr);
113 return NULL;
114 }
115 }
116
117
118 if(cfg_rootid.length >
0 && cfg_rootnode.length >
0) {
119 log_ereport(
LOG_WARN,
"log_init_repo: RootId and RootNode specified, RootNode ignored");
120 }
else if(cfg_rootnode.length >
0) {
121
122
123
124
125 ResourceData *res = resourcepool_cfg_lookup(cfg, cfg_respool.ptr,
0);
126 if(!res) {
127 log_ereport(
LOG_MISCONFIG,
"pg_init_repo: resource lookup failed");
128 return NULL;
129 }
130
131 int lookup_err = pg_lookup_root(res, cfg_rootnode.ptr, &root_id);
132
133 resourcepool_free(
NULL,
NULL, res);
134 if(lookup_err) {
135
136 return NULL;
137 }
138 }
139
140 PgRepository *repo = pool_malloc(pool,
sizeof(PgRepository));
141 ZERO(repo,
sizeof(PgRepository));
142
143 repo->resourcepool = cx_strdup_a(a, cfg_respool);
144 repo->root_resource_id = root_id;
145
146
147 if(cfg_dav.length >
0) {
148
149 char *cfg_file_path = cfg_config_file_path(cfg_dav.ptr);
150 if(pg_load_ext_dav_config(cfg, pool, repo, cfg_file_path)) {
151
152 repo =
NULL;
153 }
154 free(cfg_file_path);
155 }
156
157 return repo;
158 }
159
160 static int pg_ext_get_config(
161 ServerConfiguration *cfg,
162 pool_handle_t *pool,
163 PgRepository *repo,
164 const char *file_path,
165 xmlNode *root);
166 static int pg_ext_get_extension(
167 PgExtParser *ext,
168 pool_handle_t *pool,
169 PgRepository *repo,
170 const char *file_path,
171 xmlNode *ext_node);
172
173 static const char* pg_util_xml_get_text(
const xmlNode *elm) {
174 xmlNode *node = elm->children;
175 while(node) {
176 if(node->type ==
XML_TEXT_NODE) {
177 return (
const char*)node->content;
178 }
179 node = node->next;
180 }
181 return NULL;
182 }
183
184
185 static int pg_load_ext_dav_config(
186 ServerConfiguration *cfg,
187 pool_handle_t *pool,
188 PgRepository *repo,
189 const char *file_path)
190 {
191 CxAllocator *a = pool_allocator(pool);
192
193
194 struct stat s;
195 if(stat(file_path, &s)) {
196 if(errno ==
ENOENT) {
197 log_ereport(
LOG_FAILURE,
"pg: config file %s not found", file_path);
198 }
else {
199 log_ereport(
LOG_FAILURE,
"pg: cannot access config file %s", file_path);
200 }
201
202 return 1;
203 }
204
205
206 repo->prop_ext = cxHashMapCreate(a,
CX_STORE_POINTERS,
8);
207 if(!repo->prop_ext) {
208 log_ereport(
LOG_FAILURE,
"pg: cannot load config file: OOM");
209 return 1;
210 }
211
212
213 xmlDoc *doc = xmlReadFile(file_path,
NULL,
0);
214 if(!doc) {
215 log_ereport(
LOG_FAILURE,
"pg: cannot load config file %s", file_path);
216 return 1;
217 }
218
219
220 int ret =
0;
221 xmlNode *xml_root = xmlDocGetRootElement(doc);
222 if(xstreq(xml_root->name,
"repository")) {
223
224 ret = pg_ext_get_config(cfg, pool, repo, file_path, xml_root);
225 }
else {
226 log_ereport(
LOG_MISCONFIG,
"pg: config %s: root element <repository> expected", file_path);
227 ret =
1;
228 }
229 xmlFreeDoc(doc);
230
231 return ret;
232 }
233
234
235
236
237 static int pg_ext_get_config(
238 ServerConfiguration *cfg,
239 pool_handle_t *pool,
240 PgRepository *repo,
241 const char *file_path,
242 xmlNode *root)
243 {
244 xmlNode *node = root->children;
245 int ret =
0;
246
247 PgExtParser parserData;
248 parserData.table_lookup = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
8);
249 parserData.tables = cxLinkedListCreate(cxDefaultAllocator,
NULL,
sizeof(PgExtTable));
250
251 while(node && !ret) {
252
253 if(node->type ==
XML_ELEMENT_NODE) {
254 if(xstreq(node->name,
"extension")) {
255 ret = pg_ext_get_extension(&parserData, pool, repo, file_path, node);
256 }
257 }
258 node = node->next;
259 }
260
261
262 if(!ret) {
263 size_t ntables = parserData.tables->size;
264 repo->ntables = ntables;
265 repo->tables = pool_calloc(pool, ntables,
sizeof(PgExtTable));
266 if(repo->tables) {
267 int i =
0;
268 CxIterator iter = cxListIterator(parserData.tables);
269 cx_foreach(PgExtTable *, tab, iter) {
270 repo->tables[i++] = *tab;
271 }
272 }
else {
273 ret =
1;
274 }
275
276 }
277
278
279 cxListDestroy(parserData.tables);
280 cxMapDestroy(parserData.table_lookup);
281
282 return ret;
283 }
284
285 static int pg_ext_get_extension(
286 PgExtParser *ext,
287 pool_handle_t *pool,
288 PgRepository *repo,
289 const char *file_path,
290 xmlNode *ext_node)
291 {
292 CxAllocator *a = pool_allocator(pool);
293
294 xmlNode *node = ext_node->children;
295
296 const char *table =
NULL;
297 CxList *properties = cxLinkedListCreate(a,
NULL,
CX_STORE_POINTERS);
298
299 while(node) {
300 if(node->type ==
XML_ELEMENT_NODE) {
301 if(xstreq(node->name,
"table")) {
302 const char *value = pg_util_xml_get_text(node);
303 if(!value) {
304 log_ereport(
LOG_MISCONFIG,
"pg: config %s: table: missing value", file_path, table);
305 return 1;
306 }
else if(table) {
307 log_ereport(
LOG_MISCONFIG,
"pg: config %s: table %s already set", file_path, table);
308 return 1;
309 }
310 table = value;
311 }
else if(xstreq(node->name,
"properties")) {
312
313 xmlNode *ps = node->children;
314 while(ps) {
315 if(ps->type ==
XML_ELEMENT_NODE) {
316
317
318 if(!ps->ns || !ps->ns->href) {
319 log_ereport(
LOG_MISCONFIG,
"pg: config %s: property %s: missing namespace", file_path, ps->name);
320 return 1;
321 }
322 const char *value = pg_util_xml_get_text(ps);
323 if(!value) {
324 log_ereport(
LOG_MISCONFIG,
"pg: config %s: no column specified for property %s", file_path, ps->name);
325 return 1;
326 }
327 cxListAdd(properties, ps);
328 }
329 ps = ps->next;
330 }
331 }
332 }
333 node = node->next;
334 }
335
336
337 if(!table) {
338 log_ereport(
LOG_MISCONFIG,
"pg: config %s: missing table value for extension", file_path);
339 return 1;
340 }
341 if(!properties) {
342 log_ereport(
LOG_MISCONFIG,
"pg: config %s: no properties configured for extension", file_path);
343 return 1;
344 }
345
346
347 if(cxMapGet(ext->table_lookup, cx_hash_key_str(table))) {
348 log_ereport(
LOG_MISCONFIG,
"pg: config %s: extension table %s not unique", file_path, table);
349 return 1;
350 }
351
352
353 char *tabname = pool_strdup(pool, table);
354 if(!tabname)
return 1;
355
356
357 PgExtTable exttable;
358 exttable.table = tabname;
359 exttable.isused =
0;
360 int tableindex = (
int)ext->tables->size;
361 cxListAdd(ext->tables, &exttable);
362
363 if(cxMapPut(ext->table_lookup, cx_hash_key_str(table), (
void*)table)) {
364 return 1;
365 }
366
367 CxIterator iter = cxListIterator(properties);
368 cx_foreach(xmlNode *, ps, iter) {
369 const char *value = pg_util_xml_get_text(ps);
370
371 PgPropertyStoreExt *ext_col = pool_malloc(pool,
sizeof(PgPropertyStoreExt));
372 if(!ext_col) {
373 return 1;
374 }
375 ext_col->tableindex = tableindex;
376 ext_col->ns = pool_strdup(pool, (
const char*)ps->ns->href);
377 ext_col->name = pool_strdup(pool, (
const char*)ps->name);
378 ext_col->column = pool_strdup(pool, (
const char*)value);
379 if(!ext_col->ns || !ext_col->name || !ext_col->column) {
380 return 1;
381 }
382
383 CxHashKey key = webdav_property_key(ext_col->ns, ext_col->name);
384 int err = cxMapPut(repo->prop_ext, key, ext_col);
385 free((
void*)key.data);
386 if(err) {
387 return 1;
388 }
389 }
390
391 return 0;
392 }
393