1 /*
2 * ProFTPD: mod_quotatab_sql -- a mod_quotatab sub-module for managing quota
3 * data via SQL-based tables
4 * Copyright (c) 2002-2020 TJ Saunders
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
19 *
20 * As a special exemption, TJ Saunders gives permission to link this program
21 * with OpenSSL, and distribute the resulting executable, without including
22 * the source code for OpenSSL in the source distribution.
23 */
24
25 #include "mod_quotatab.h"
26 #include "mod_sql.h"
27
28 #define QUOTATAB_SQL_VALUE_BUFSZ 20
29
30 module quotatab_sql_module;
31
sqltab_cmd_create(pool * parent_pool,unsigned int argc,...)32 static cmd_rec *sqltab_cmd_create(pool *parent_pool, unsigned int argc, ...) {
33 register unsigned int i = 0;
34 pool *cmd_pool = NULL;
35 cmd_rec *cmd = NULL;
36 va_list argp;
37
38 cmd_pool = make_sub_pool(parent_pool);
39 cmd = (cmd_rec *) pcalloc(cmd_pool, sizeof(cmd_rec));
40 cmd->pool = cmd_pool;
41
42 cmd->argc = argc;
43 cmd->argv = pcalloc(cmd->pool, argc * sizeof(void *));
44
45 /* Hmmm... */
46 cmd->tmp_pool = cmd->pool;
47
48 va_start(argp, argc);
49 for (i = 0; i < argc; i++) {
50 cmd->argv[i] = va_arg(argp, char *);
51 }
52 va_end(argp);
53
54 return cmd;
55 }
56
sqltab_get_name(pool * p,char * name)57 static char *sqltab_get_name(pool *p, char *name) {
58 cmdtable *cmdtab;
59 cmd_rec *cmd;
60 modret_t *res;
61
62 /* Find the cmdtable for the sql_escapestr command. */
63 cmdtab = pr_stash_get_symbol2(PR_SYM_HOOK, "sql_escapestr", NULL, NULL, NULL);
64 if (cmdtab == NULL) {
65 quotatab_log("error: unable to find SQL hook symbol 'sql_escapestr'");
66 return name;
67 }
68
69 if (strlen(name) == 0) {
70 return name;
71 }
72
73 cmd = sqltab_cmd_create(p, 1, pr_str_strip(p, name));
74
75 /* Call the handler. */
76 res = pr_module_call(cmdtab->m, cmdtab->handler, cmd);
77
78 /* Check the results. */
79 if (MODRET_ISDECLINED(res) ||
80 MODRET_ISERROR(res)) {
81 quotatab_log("error executing 'sql_escapestring'");
82 return name;
83 }
84
85 return res->data;
86 }
87
sqltab_close(quota_table_t * sqltab)88 static int sqltab_close(quota_table_t *sqltab) {
89
90 /* Is there really anything that needs to be done here? */
91 return 0;
92 }
93
sqltab_create(quota_table_t * sqltab,void * ptr)94 static int sqltab_create(quota_table_t *sqltab, void *ptr) {
95 pool *tmp_pool = NULL;
96 cmdtable *sql_cmdtab = NULL;
97 cmd_rec *sql_cmd = NULL;
98 modret_t *sql_res = NULL;
99 char *insert_query = NULL, *tally_quota_name = NULL, *tally_quota_type = NULL,
100 *tally_bytes_in = NULL, *tally_bytes_out = NULL, *tally_bytes_xfer = NULL,
101 *tally_files_in = NULL, *tally_files_out = NULL, *tally_files_xfer = NULL;
102 quota_tally_t *tally = ptr;
103
104 /* Allocate a sub pool for use by this function. */
105 tmp_pool = make_sub_pool(sqltab->tab_pool);
106
107 /* Allocate small buffers for stringifying most of the tally struct
108 * members: quota_type, bytes_in_used, bytes_out_used, bytes_xfer_used,
109 * files_in_used, files_out_used, files_xfer_used.
110 */
111 tally_quota_name = pcalloc(tmp_pool, 83 * sizeof(char));
112 tally_quota_type = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
113 tally_bytes_in = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
114 tally_bytes_out = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
115 tally_bytes_xfer = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
116 tally_files_in = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
117 tally_files_out = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
118 tally_files_xfer = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
119
120 /* Execute the INSERT NamedQuery */
121 insert_query = ((char **) sqltab->tab_data)[2];
122
123 /* Populate the INSERT query with the necessary data from the current
124 * limit record. Need to stringify most of the tally struct members:
125 * quota_type, bytes_in_used, bytes_out_used, bytes_xfer_used,
126 * files_in_used, files_out_used, files_xfer_used.
127 */
128
129 /* NOTE: Per Issue #1149, we should NOT be adding the quotes to the
130 * text ourselves here. It also makes the mod_quotatab_sql configuration
131 * inconsistent; the admin must quote these texts in the config for
132 * SELECTs, but not for INSERTs.
133 */
134 pr_snprintf(tally_quota_name, 83, "'%s'",
135 sqltab_get_name(tmp_pool, tally->name));
136 tally_quota_name[82] = '\0';
137
138 if (tally->quota_type == USER_QUOTA) {
139 pr_snprintf(tally_quota_type, QUOTATAB_SQL_VALUE_BUFSZ, "'%s'", "user");
140
141 } else if (tally->quota_type == GROUP_QUOTA) {
142 pr_snprintf(tally_quota_type, QUOTATAB_SQL_VALUE_BUFSZ, "'%s'", "group");
143
144 } else if (tally->quota_type == CLASS_QUOTA) {
145 pr_snprintf(tally_quota_type, QUOTATAB_SQL_VALUE_BUFSZ, "'%s'", "class");
146
147 } else if (tally->quota_type == ALL_QUOTA) {
148 pr_snprintf(tally_quota_type, QUOTATAB_SQL_VALUE_BUFSZ, "'%s'", "all");
149 }
150
151 tally_quota_type[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
152
153 pr_snprintf(tally_bytes_in, QUOTATAB_SQL_VALUE_BUFSZ, "%f",
154 tally->bytes_in_used);
155 tally_bytes_in[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
156
157 pr_snprintf(tally_bytes_out, QUOTATAB_SQL_VALUE_BUFSZ, "%f",
158 tally->bytes_out_used);
159 tally_bytes_out[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
160
161 pr_snprintf(tally_bytes_xfer, QUOTATAB_SQL_VALUE_BUFSZ, "%f",
162 tally->bytes_xfer_used);
163 tally_bytes_xfer[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
164
165 pr_snprintf(tally_files_in, QUOTATAB_SQL_VALUE_BUFSZ, "%u",
166 tally->files_in_used);
167 tally_files_in[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
168
169 pr_snprintf(tally_files_out, QUOTATAB_SQL_VALUE_BUFSZ, "%u",
170 tally->files_out_used);
171 tally_files_out[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
172
173 pr_snprintf(tally_files_xfer, QUOTATAB_SQL_VALUE_BUFSZ, "%u",
174 tally->files_xfer_used);
175 tally_files_xfer[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
176
177 sql_cmd = sqltab_cmd_create(tmp_pool, 10, "sql_change", insert_query,
178 tally_quota_name, tally_quota_type,
179 tally_bytes_in, tally_bytes_out, tally_bytes_xfer,
180 tally_files_in, tally_files_out, tally_files_xfer);
181
182 /* Find the cmdtable for the sql_change command. */
183 sql_cmdtab = pr_stash_get_symbol2(PR_SYM_HOOK, "sql_change", NULL, NULL,
184 NULL);
185 if (sql_cmdtab == NULL) {
186 quotatab_log("error: unable to find SQL hook symbol 'sql_change'");
187 destroy_pool(tmp_pool);
188 return -1;
189 }
190
191 /* Call the handler. */
192 sql_res = pr_module_call(sql_cmdtab->m, sql_cmdtab->handler, sql_cmd);
193
194 /* Check the results. */
195 if (MODRET_ISERROR(sql_res)) {
196 quotatab_log("error executing NamedQuery '%s': %s", insert_query,
197 strerror(errno));
198 destroy_pool(tmp_pool);
199 return -1;
200 }
201
202 destroy_pool(tmp_pool);
203 return 0;
204 }
205
sqltab_lookup(quota_table_t * sqltab,void * ptr,const char * name,quota_type_t quota_type)206 static unsigned char sqltab_lookup(quota_table_t *sqltab, void *ptr,
207 const char *name, quota_type_t quota_type) {
208 pool *tmp_pool = NULL;
209 cmdtable *sql_cmdtab = NULL;
210 cmd_rec *sql_cmd = NULL;
211 modret_t *sql_res = NULL;
212 array_header *sql_data = NULL;
213 char *select_query = NULL;
214
215 /* Allocate a temporary pool for the duration of this lookup. */
216 tmp_pool = make_sub_pool(sqltab->tab_pool);
217
218 /* Handle tally and limit tables differently... */
219 if (sqltab->tab_type == TYPE_TALLY) {
220 select_query = ((char **) sqltab->tab_data)[0];
221
222 } else if (sqltab->tab_type == TYPE_LIMIT) {
223 select_query = (char *) sqltab->tab_data;
224 }
225
226 /* Find the cmdtable for the sql_lookup command. */
227 sql_cmdtab = pr_stash_get_symbol2(PR_SYM_HOOK, "sql_lookup", NULL, NULL,
228 NULL);
229 if (sql_cmdtab == NULL) {
230 quotatab_log("error: unable to find SQL hook symbol 'sql_lookup'");
231 destroy_pool(tmp_pool);
232 return FALSE;
233 }
234
235 /* Prepare the SELECT query. */
236 sql_cmd = sqltab_cmd_create(tmp_pool, 4, "sql_lookup", select_query,
237 name ? sqltab_get_name(tmp_pool, (char *) name) : "",
238 quota_type == USER_QUOTA ? "user" : quota_type == GROUP_QUOTA ? "group" :
239 quota_type == CLASS_QUOTA ? "class" : "all");
240
241 /* Call the handler. */
242 sql_res = pr_module_call(sql_cmdtab->m, sql_cmdtab->handler, sql_cmd);
243
244 /* Check the results. */
245 if (sql_res == NULL ||
246 MODRET_ISERROR(sql_res)) {
247 quotatab_log("error processing NamedQuery '%s'", select_query);
248 destroy_pool(tmp_pool);
249 return FALSE;
250 }
251
252 sql_data = (array_header *) sql_res->data;
253
254 if (sqltab->tab_type == TYPE_TALLY) {
255 quota_tally_t *tally = ptr;
256 char **values = (char **) sql_data->elts;
257
258 /* Update the tally record with the 8 values:
259 * name
260 * quota_type
261 * bytes_{in,out,xfer}_used
262 * files_{in,out,xfer}_used
263 */
264
265 if (sql_data->nelts < 8) {
266 if (sql_data->nelts > 0) {
267 quotatab_log("error: SQLNamedQuery '%s' returned incorrect number of "
268 "values (%d)", select_query, sql_data->nelts);
269 }
270
271 destroy_pool(tmp_pool);
272 return FALSE;
273 }
274
275 /* Process each element returned. */
276 memmove(tally->name, values[0], sizeof(tally->name));
277
278 if (strcasecmp(values[1], "user") == 0) {
279 tally->quota_type = USER_QUOTA;
280
281 } else if (strcasecmp(values[1], "group") == 0) {
282 tally->quota_type = GROUP_QUOTA;
283
284 } else if (strcasecmp(values[1], "class") == 0) {
285 tally->quota_type = CLASS_QUOTA;
286
287 } else if (strcasecmp(values[1], "all") == 0) {
288 tally->quota_type = ALL_QUOTA;
289 }
290
291 /* Check if this is the requested record, now that enough information
292 * is in place.
293 */
294 if (quota_type != tally->quota_type) {
295 destroy_pool(tmp_pool);
296 return FALSE;
297 }
298
299 /* Match names if need be */
300 if (quota_type != ALL_QUOTA &&
301 values[0] &&
302 strlen(values[0]) > 0 &&
303 strcmp(name, tally->name) != 0) {
304 destroy_pool(tmp_pool);
305 return FALSE;
306 }
307
308 tally->bytes_in_used = -1.0;
309 if (values[2]) {
310 tally->bytes_in_used = atof(values[2]);
311 }
312
313 tally->bytes_out_used = -1.0;
314 if (values[3]) {
315 tally->bytes_out_used = atof(values[3]);
316 }
317
318 tally->bytes_xfer_used = -1.0;
319 if (values[4]) {
320 tally->bytes_xfer_used = atof(values[4]);
321 }
322
323 tally->files_in_used = 0;
324 if (values[5]) {
325 tally->files_in_used = atol(values[5]);
326 }
327
328 tally->files_out_used = 0;
329 if (values[6])
330 tally->files_out_used = atol(values[6]);
331
332 tally->files_xfer_used = 0;
333 if (values[7]) {
334 tally->files_xfer_used = atol(values[7]);
335 }
336
337 destroy_pool(tmp_pool);
338 return TRUE;
339 }
340
341 if (sqltab->tab_type == TYPE_LIMIT) {
342 quota_limit_t *limit = ptr;
343 char **values = (char **) sql_data->elts;
344
345 /* Update the limit record with the 10 values:
346 * name
347 * quota_type
348 * per_session
349 * limit_type
350 * bytes_{in,out,xfer}_avail
351 * files_{in,out,xfer}_avail
352 */
353
354 if (sql_data->nelts < 10) {
355 if (sql_data->nelts > 0) {
356 quotatab_log("error: SQLNamedQuery '%s' returned incorrect number of "
357 "values (%d)", select_query, sql_data->nelts);
358 }
359
360 destroy_pool(tmp_pool);
361 return FALSE;
362 }
363
364 /* Process each element returned. */
365 memmove(limit->name, values[0], sizeof(limit->name));
366
367 if (strcasecmp(values[1], "user") == 0) {
368 limit->quota_type = USER_QUOTA;
369
370 } else if (strcasecmp(values[1], "group") == 0) {
371 limit->quota_type = GROUP_QUOTA;
372
373 } else if (strcasecmp(values[1], "class") == 0) {
374 limit->quota_type = CLASS_QUOTA;
375
376 } else if (strcasecmp(values[1], "all") == 0) {
377 limit->quota_type = ALL_QUOTA;
378 }
379
380 /* Check if this is the requested record, now that enough information
381 * is in place.
382 */
383 if (quota_type != limit->quota_type) {
384 destroy_pool(tmp_pool);
385 return FALSE;
386 }
387
388 /* Match names if need be */
389 if (quota_type != ALL_QUOTA &&
390 values[0] &&
391 strlen(values[0]) > 0 &&
392 strcmp(name, limit->name) != 0) {
393 destroy_pool(tmp_pool);
394 return FALSE;
395 }
396
397 if (strcasecmp(values[2], "false") == 0) {
398 limit->quota_per_session = FALSE;
399
400 } else if (strcasecmp(values[2], "true") == 0) {
401 limit->quota_per_session = TRUE;
402 }
403
404 if (strcasecmp(values[3], "soft") == 0) {
405 limit->quota_limit_type = SOFT_LIMIT;
406
407 } else if (strcasecmp(values[3], "hard") == 0) {
408 limit->quota_limit_type = HARD_LIMIT;
409 }
410
411 limit->bytes_in_avail = -1.0;
412 if (values[4]) {
413 limit->bytes_in_avail = atof(values[4]);
414 }
415
416 limit->bytes_out_avail = -1.0;
417 if (values[5]) {
418 limit->bytes_out_avail = atof(values[5]);
419 }
420
421 limit->bytes_xfer_avail = -1.0;
422 if (values[6]) {
423 limit->bytes_xfer_avail = atof(values[6]);
424 }
425
426 limit->files_in_avail = 0;
427 if (values[7]) {
428 limit->files_in_avail = atol(values[7]);
429 }
430
431 limit->files_out_avail = 0;
432 if (values[8]) {
433 limit->files_out_avail = atol(values[8]);
434 }
435
436 limit->files_xfer_avail = 0;
437 if (values[9]) {
438 limit->files_xfer_avail = atol(values[9]);
439 }
440
441 destroy_pool(tmp_pool);
442 return TRUE;
443 }
444
445 destroy_pool(tmp_pool);
446
447 /* default */
448 return FALSE;
449 }
450
sqltab_read(quota_table_t * sqltab,void * ptr)451 static int sqltab_read(quota_table_t *sqltab, void *ptr) {
452 quota_tally_t *tally = ptr;
453
454 return sqltab_lookup(sqltab, ptr, tally->name, tally->quota_type);
455 }
456
sqltab_verify(quota_table_t * sqltab)457 static unsigned char sqltab_verify(quota_table_t *sqltab) {
458
459 /* Always TRUE. */
460 return TRUE;
461 }
462
sqltab_write(quota_table_t * sqltab,void * ptr)463 static int sqltab_write(quota_table_t *sqltab, void *ptr) {
464 pool *tmp_pool = NULL;
465 cmdtable *sql_cmdtab = NULL;
466 cmd_rec *sql_cmd = NULL;
467 modret_t *sql_res = NULL;
468 char *update_query = NULL, *tally_quota_type = NULL,
469 *tally_bytes_in = NULL, *tally_bytes_out = NULL, *tally_bytes_xfer = NULL,
470 *tally_files_in = NULL, *tally_files_out = NULL, *tally_files_xfer = NULL;
471 quota_tally_t *tally = ptr;
472
473 /* Allocate a sub pool for use by this function. */
474 tmp_pool = make_sub_pool(sqltab->tab_pool);
475
476 /* Allocate small buffers for stringifying most of the tally struct
477 * members: quota_type, bytes_in_used, bytes_out_used, bytes_xfer_used,
478 * files_in_used, files_out_used, files_xfer_used.
479 */
480 tally_quota_type = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
481 tally_bytes_in = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
482 tally_bytes_out = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
483 tally_bytes_xfer = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
484 tally_files_in = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
485 tally_files_out = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
486 tally_files_xfer = pcalloc(tmp_pool, QUOTATAB_SQL_VALUE_BUFSZ * sizeof(char));
487
488 /* Retrieve the UPDATE NamedQuery */
489 update_query = ((char **) sqltab->tab_data)[1];
490
491 /* Populate the UPDATE query with the necessary data from the current
492 * limit record. Need to stringify most of the tally struct members:
493 * quota_type, bytes_in_used, bytes_out_used, bytes_xfer_used,
494 * files_in_used, files_out_used, files_xfer_used.
495 */
496
497 if (tally->quota_type == USER_QUOTA) {
498 pr_snprintf(tally_quota_type, QUOTATAB_SQL_VALUE_BUFSZ, "%s", "user");
499
500 } else if (tally->quota_type == GROUP_QUOTA) {
501 pr_snprintf(tally_quota_type, QUOTATAB_SQL_VALUE_BUFSZ, "%s", "group");
502
503 } else if (tally->quota_type == CLASS_QUOTA) {
504 pr_snprintf(tally_quota_type, QUOTATAB_SQL_VALUE_BUFSZ, "%s", "class");
505
506 } else if (tally->quota_type == ALL_QUOTA) {
507 pr_snprintf(tally_quota_type, QUOTATAB_SQL_VALUE_BUFSZ, "%s", "all");
508 }
509
510 tally_quota_type[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
511
512 /* Note: use the deltas data, not the tally members, so that the
513 * UPDATE can do an "atomic" read+update all in one shot.
514 */
515 pr_snprintf(tally_bytes_in, QUOTATAB_SQL_VALUE_BUFSZ, "%f",
516 quotatab_deltas.bytes_in_delta);
517 tally_bytes_in[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
518
519 pr_snprintf(tally_bytes_out, QUOTATAB_SQL_VALUE_BUFSZ, "%f",
520 quotatab_deltas.bytes_out_delta);
521 tally_bytes_out[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
522
523 pr_snprintf(tally_bytes_xfer, QUOTATAB_SQL_VALUE_BUFSZ, "%f",
524 quotatab_deltas.bytes_xfer_delta);
525 tally_bytes_xfer[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
526
527 /* Don't try to prevent underflows here; mod_quotatab already makes
528 * these checks.
529 */
530 pr_snprintf(tally_files_in, QUOTATAB_SQL_VALUE_BUFSZ, "%d",
531 quotatab_deltas.files_in_delta);
532 tally_files_in[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
533
534 pr_snprintf(tally_files_out, QUOTATAB_SQL_VALUE_BUFSZ, "%d",
535 quotatab_deltas.files_out_delta);
536 tally_files_out[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
537
538 pr_snprintf(tally_files_xfer, QUOTATAB_SQL_VALUE_BUFSZ, "%d",
539 quotatab_deltas.files_xfer_delta);
540 tally_files_xfer[QUOTATAB_SQL_VALUE_BUFSZ-1] = '\0';
541
542 sql_cmd = sqltab_cmd_create(tmp_pool, 10, "sql_change", update_query,
543 tally_bytes_in, tally_bytes_out, tally_bytes_xfer,
544 tally_files_in, tally_files_out, tally_files_xfer,
545 sqltab_get_name(tmp_pool, tally->name), tally_quota_type);
546
547 /* Find the cmdtable for the sql_change command. */
548 sql_cmdtab = pr_stash_get_symbol2(PR_SYM_HOOK, "sql_change", NULL, NULL,
549 NULL);
550 if (sql_cmdtab == NULL) {
551 quotatab_log("error: unable to find SQL hook symbol 'sql_change'");
552 destroy_pool(tmp_pool);
553 return -1;
554 }
555
556 /* Call the handler. */
557 sql_res = pr_module_call(sql_cmdtab->m, sql_cmdtab->handler, sql_cmd);
558
559 /* Check the results. */
560 if (MODRET_ISERROR(sql_res)) {
561 quotatab_log("error executing NamedQuery '%s': %s", update_query,
562 strerror(errno));
563 destroy_pool(tmp_pool);
564 return -1;
565 }
566
567 destroy_pool(tmp_pool);
568 return 0;
569 }
570
sqltab_rlock(quota_table_t * sqltab)571 static int sqltab_rlock(quota_table_t *sqltab) {
572
573 /* Check for a configured lock file. */
574 if (sqltab->tab_lockfd > 0) {
575 sqltab->tab_lock.l_type = F_RDLCK;
576 return fcntl(sqltab->tab_lockfd, F_SETLK, &sqltab->tab_lock);
577 }
578
579 return 0;
580 }
581
sqltab_unlock(quota_table_t * sqltab)582 static int sqltab_unlock(quota_table_t *sqltab) {
583
584 /* Check for a configured lock file. */
585 if (sqltab->tab_lockfd > 0) {
586 sqltab->tab_lock.l_type = F_UNLCK;
587 return fcntl(sqltab->tab_lockfd, F_SETLK, &sqltab->tab_lock);
588 }
589
590 return 0;
591 }
592
sqltab_wlock(quota_table_t * sqltab)593 static int sqltab_wlock(quota_table_t *sqltab) {
594
595 /* Check for a configured lock file. */
596 if (sqltab->tab_lockfd > 0) {
597 sqltab->tab_lock.l_type = F_WRLCK;
598 return fcntl(sqltab->tab_lockfd, F_SETLK, &sqltab->tab_lock);
599 }
600
601 return 0;
602 }
603
sqltab_open(pool * parent_pool,quota_tabtype_t tab_type,const char * srcinfo)604 static quota_table_t *sqltab_open(pool *parent_pool, quota_tabtype_t tab_type,
605 const char *srcinfo) {
606
607 quota_table_t *tab = NULL;
608 pool *tab_pool = make_sub_pool(parent_pool),
609 *tmp_pool = make_sub_pool(parent_pool);
610 config_rec *c = NULL;
611 char *named_query = NULL;
612
613 tab = (quota_table_t *) pcalloc(tab_pool, sizeof(quota_table_t));
614 tab->tab_pool = tab_pool;
615 tab->tab_type = tab_type;
616
617 if (tab->tab_type == TYPE_TALLY) {
618 char *start = NULL, *finish = NULL;
619 char *select_query = NULL, *update_query = NULL, *insert_query = NULL;
620
621 /* Parse the SELECT, UPDATE, and INSERT query names out of the srcinfo
622 * string. Lookup and store the queries in the tab_data area, so that
623 * they need not be looked up later.
624 *
625 * The srcinfo string for this case should look like:
626 * "/<select-named-query>/<update-named-query>/<insert-named-query>/"
627 */
628
629 start = strchr(srcinfo, '/');
630 if (start == NULL) {
631 quotatab_log("error: badly formatted source info '%s'", srcinfo);
632 destroy_pool(tmp_pool);
633 errno = EINVAL;
634 return NULL;
635 }
636
637 /* Find the next slash. */
638 finish = strchr(++start, '/');
639 if (finish == NULL) {
640 quotatab_log("error: badly formatted source info '%s'", srcinfo);
641 destroy_pool(tmp_pool);
642 errno = EINVAL;
643 return NULL;
644 }
645
646 *finish = '\0';
647 select_query = pstrdup(tab->tab_pool, start);
648
649 /* Verify that the named query has indeed been defined. This is
650 * based on how mod_sql creates its config_rec names.
651 */
652 named_query = pstrcat(tmp_pool, "SQLNamedQuery_", select_query, NULL);
653
654 c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
655 if (c == NULL) {
656 quotatab_log("error: unable to resolve SQLNamedQuery name '%s'",
657 select_query);
658 destroy_pool(tmp_pool);
659 errno = EINVAL;
660 return NULL;
661 }
662
663 /* Now, find the next slash. */
664 start = finish;
665 finish = strchr(++start, '/');
666 if (finish == NULL) {
667 quotatab_log("error: badly formatted source info '%s'", srcinfo);
668 destroy_pool(tmp_pool);
669 errno = EINVAL;
670 return NULL;
671 }
672
673 *finish = '\0';
674 update_query = pstrdup(tab->tab_pool, start);
675
676 /* Verify that the named query has indeed been defined. This is
677 * based on how mod_sql creates its config_rec names.
678 */
679 named_query = pstrcat(tmp_pool, "SQLNamedQuery_", update_query, NULL);
680
681 c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
682 if (c == NULL) {
683 quotatab_log("error: unable to resolve SQLNamedQuery name '%s'",
684 update_query);
685 destroy_pool(tmp_pool);
686 errno = EINVAL;
687 return NULL;
688 }
689
690 /* Assume the rest of the srcinfo string is the INSERT query (assuming
691 * there *is* a "rest of the string").
692 */
693 if (*(++finish) != '\0') {
694 insert_query = pstrdup(tab->tab_pool, finish);
695
696 } else {
697 quotatab_log("error: badly formatted source info '%s'", srcinfo);
698 destroy_pool(tmp_pool);
699 errno = EINVAL;
700 return NULL;
701 }
702
703 /* Verify that the named query has indeed been defined. This is
704 * based on how mod_sql creates its config_rec names.
705 */
706 named_query = pstrcat(tmp_pool, "SQLNamedQuery_", insert_query, NULL);
707
708 c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
709 if (c == NULL) {
710 quotatab_log("error: unable to resolve SQLNamedQuery name '%s'",
711 insert_query);
712 destroy_pool(tmp_pool);
713 errno = EINVAL;
714 return NULL;
715 }
716
717 tab->tab_data = (char **) pcalloc(tab->tab_pool, 3 * sizeof(char *));
718 ((char **) tab->tab_data)[0] = pstrdup(tab->tab_pool, select_query);
719 ((char **) tab->tab_data)[1] = pstrdup(tab->tab_pool, update_query);
720 ((char **) tab->tab_data)[2] = pstrdup(tab->tab_pool, insert_query);
721
722 } else if (tab->tab_type == TYPE_LIMIT) {
723 char *start = NULL, *select_query = NULL;
724
725 /* Parse the SELECT query name out of the srcinfo string. Lookup and
726 * store the queries in the tab_data area, so that it need not be looked
727 * up later.
728 *
729 * The srcinfo string for this case should look like:
730 * "/<select-named-query>"
731 */
732
733 start = strchr(srcinfo, '/');
734 if (start == NULL) {
735 quotatab_log("error: badly formatted source info '%s'", srcinfo);
736 destroy_pool(tmp_pool);
737 errno = EINVAL;
738 return NULL;
739 }
740 select_query = ++start;
741
742 /* Verify that the named query has indeed been defined. This is
743 * based on how mod_sql creates its config_rec names.
744 */
745 named_query = pstrcat(tmp_pool, "SQLNamedQuery_", select_query, NULL);
746
747 c = find_config(main_server->conf, CONF_PARAM, named_query, FALSE);
748 if (c == NULL) {
749 quotatab_log("error: unable to resolve SQLNamedQuery name '%s'",
750 select_query);
751 destroy_pool(tmp_pool);
752 errno = EINVAL;
753 return NULL;
754 }
755
756 tab->tab_data = (void *) pstrdup(tab->tab_pool, select_query);
757 }
758
759 /* Set all the necessary function pointers. */
760 tab->tab_close = sqltab_close;
761 tab->tab_create = sqltab_create;
762 tab->tab_lookup = sqltab_lookup;
763 tab->tab_read = sqltab_read;
764 tab->tab_verify = sqltab_verify;
765 tab->tab_write = sqltab_write;
766
767 tab->tab_rlock = sqltab_rlock;
768 tab->tab_unlock = sqltab_unlock;
769 tab->tab_wlock = sqltab_wlock;
770
771 /* Prepare the lock structure. */
772 tab->tab_lock.l_whence = SEEK_CUR;
773 tab->tab_lock.l_start = 0;
774 tab->tab_lock.l_len = 0;
775
776 destroy_pool(tmp_pool);
777 return tab;
778 }
779
780 /* Event handlers
781 */
782
783 #if defined(PR_SHARED_MODULE)
sqltab_mod_unload_ev(const void * event_data,void * user_data)784 static void sqltab_mod_unload_ev(const void *event_data, void *user_data) {
785 if (strcmp("mod_quotatab_sql.c", (const char *) event_data) == 0) {
786 pr_event_unregister("atab_sql_module, NULL, NULL);
787 quotatab_unregister_backend("sql", QUOTATAB_LIMIT_SRC|QUOTATAB_TALLY_SRC);
788 }
789 }
790 #endif /* PR_SHARED_MODULE */
791
792 /* Initialization routines
793 */
794
sqltab_init(void)795 static int sqltab_init(void) {
796
797 /* Initialize the quota source objects for type "sql". */
798 quotatab_register_backend("sql", sqltab_open,
799 QUOTATAB_LIMIT_SRC|QUOTATAB_TALLY_SRC);
800
801 #if defined(PR_SHARED_MODULE)
802 pr_event_register("atab_sql_module, "core.module-unload",
803 sqltab_mod_unload_ev, NULL);
804 #endif /* PR_SHARED_MODULE */
805
806 return 0;
807 }
808
809 module quotatab_sql_module = {
810 NULL, NULL,
811
812 /* Module API version 2.0 */
813 0x20,
814
815 /* Module name */
816 "quotatab_sql",
817
818 /* Module configuration handler table */
819 NULL,
820
821 /* Module command handler table */
822 NULL,
823
824 /* Module authentication handler table */
825 NULL,
826
827 /* Module initialization */
828 sqltab_init,
829
830 /* Session initialization */
831 NULL
832 };
833