1 | /***************************************
2 | $Revision: 1.75 $
3 |
4 | Query instructions (qi). This is where the queries are executed.
5 |
6 | Status: NOT REVUED, TESTED
7 |
8 | ******************/ /******************
9 | Filename : query_instructions.c
10 | Authors : ottrey@ripe.net - framework and draft implementation
11 | marek@ripe.net - cleaned and extended, added referral,
12 | accounting support and watchdog cancellation.
13 | OSs Tested : Solaris
14 | ******************/ /******************
15 | Copyright (c) 1999 RIPE NCC
16 |
17 | All Rights Reserved
18 |
19 | Permission to use, copy, modify, and distribute this software and its
20 | documentation for any purpose and without fee is hereby granted,
21 | provided that the above copyright notice appear in all copies and that
22 | both that copyright notice and this permission notice appear in
23 | supporting documentation, and that the name of the author not be
24 | used in advertising or publicity pertaining to distribution of the
25 | software without specific, written prior permission.
26 |
27 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
28 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
29 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
30 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
31 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
32 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33 | ***************************************/
34 | #include <stdio.h>
35 | #include <string.h>
36 | #include <glib.h>
37 |
38 | #include "which_keytypes.h"
39 | #include "query_instructions.h"
40 | #include "mysql_driver.h"
41 | #include "rp.h"
42 | #include "stubs.h"
43 | #include "constants.h"
44 | #include "memwrap.h"
45 | #include "wh_queries.h"
46 |
47 | #include "defs.h"
48 |
49 | /*+ String sizes +*/
50 | #define STR_S 63
51 | #define STR_M 255
52 | #define STR_L 1023
53 | #define STR_XL 4095
54 | #define STR_XXL 16383
55 |
56 |
57 | /*++++++++++++++++++++++++++++++++++++++
58 | Function invoked on query cancellation by the watchdog,
59 | used from the sql_execute_watched() function.
60 |
61 | It aborts the running query (the abort function in sq kills and
62 | reestablished the connection).
63 |
64 | void *qi_kill_body result of sq_execute_query, int cast to (void*)
65 |
66 | void *arg pointer to sql connection
67 |
68 | Author:
69 | marek.
70 |
71 | ++++++++++++++++++++++++++++++++++++++*/
72 | static
73 | void *qi_kill_body(void *arg)
74 | {
75 | SQ_connection_t *sql_connection = arg;
76 | ER_dbg_va(FAC_QI, ASP_QI_WATCH,
77 | "rtc: killing SQL connection %d", (sql_connection)->thread_id);
78 | /* abort the running query */
79 | SQ_abort_query(sql_connection);
80 |
81 | return NULL;
82 | }
83 |
84 |
85 |
86 | /*++++++++++++++++++++++++++++++++++++++
87 | wrapper around sq_execute_query: starts a query
88 | in a separate thread and starts the socket watchdog to cancel the query
89 | if the socket is closed. If the socket has problems already (its
90 | reason-to-close flag is set) no query is attempted.
91 |
92 | The execution of the query or watchdog is not guaranteed at all!
93 |
94 | int sql_execute_watched Returns the return code of SQ_execute_query,
95 | Returns 0 for cancelled queries.
96 |
97 | sk_conn_st *condat connection to watch
98 |
99 | SQ_connection_t **sql_connection sql connection
100 |
101 | const char *query sql query to execute
102 |
103 | SQ_result_set_t **result_ptr storage for the query result structure
104 | (passed to SQ_execute_query). Must either
105 | be NULL, or the pointer it points to must
106 | be NULL - in that case the result struct.
107 | will be allocated in SQ.
108 |
109 | Author:
110 | marek.
111 | ++++++++++++++++++++++++++++++++++++++*/
112 | int sql_execute_watched(sk_conn_st *condat, SQ_connection_t **sql_connection,
113 | const char *query, SQ_result_set_t **result_ptr)
114 | {
115 | int retval = 0; /* return value of sq_execute_query */
116 | SQ_connection_t *tempcon;
117 |
118 | /* assert that, if defined, result_ptr is initialised to NULL
119 | prior to calling this function */
120 | if( result_ptr != NULL ) {
121 | dieif( *result_ptr != NULL );
122 | }
123 |
124 | /* don't even try to perform the query/fire up watchdog
125 | if rtc is already set. Do this only if not set yet. */
126 | if( condat->rtc == 0 ) {
127 |
128 | /* make clean */
129 | SK_watch_setclear(condat);
130 |
131 | /* set watchdog to execute the abort function */
132 | SK_watch_setexec(condat, qi_kill_body, *sql_connection);
133 |
134 | /* start the watchdog */
135 | SK_watchstart(condat);
136 |
137 | /* start query. An error may be returned if the query is aborted */
138 | retval = SQ_execute_query(*sql_connection, query, result_ptr);
139 |
140 | /* but short queries will complete before the watchdog kills the
141 | connection */
142 |
143 | SK_watchstop(condat);
144 |
145 |
146 | /* if the watchdog triggered, then it is guaranteed that
147 | the kill_body function was invoked and therefore the sql-connection
148 | is now unusable...
149 | Close and reopen it for cleanup, use temporary connection
150 | to keep the login details */
151 | if( condat->rtc != 0 ) {
152 | /* can't rely on the error code from mysql!
153 | */
154 |
155 | /* one thing: this code must be entered ONLY if the kill_body
156 | thing was invoked by the watchdog.
157 | */
158 |
159 | /* if result is defined, free it here before destroying the
160 | associated connection */
161 | if( retval == 0 && result_ptr && *result_ptr ) {
162 | SQ_free_result( *result_ptr );
163 | *result_ptr = NULL;
164 | }
165 |
166 | tempcon = SQ_duplicate_connection(*sql_connection);
167 |
168 | ER_dbg_va(FAC_QI, ASP_QI_WATCH,
169 | "rtc: closing SQL thread %d", (*sql_connection)->thread_id);
170 | SQ_close_connection(*sql_connection);
171 |
172 | *sql_connection = tempcon;
173 | ER_dbg_va(FAC_QI, ASP_QI_WATCH,
174 | "rtc: reopened as thread %d", (*sql_connection)->thread_id);
175 |
176 | /* make it look as if there was no error and
177 | the result is empty */
178 | retval = 0;
179 | } /* if watchdog set rtc */
180 |
181 | } /* if rtc not set before */
182 |
183 | return retval;
184 | }
185 |
186 | /* create_name_query() */
187 | /*++++++++++++++++++++++++++++++++++++++
188 | Create an sql query for the names table.
189 |
190 | char *query_str
191 |
192 | const char *sql_query
193 |
194 | const char *keys
195 |
196 | More:
197 | +html+ <PRE>
198 | Authors:
199 | ottrey
200 | +html+ </PRE>
201 | ++++++++++++++++++++++++++++++++++++++*/
202 | static void create_name_query(char *query_str, const char *sql_query, const char *keys) {
203 | int i;
204 | /* Allocate stuff - use dynamic strings (initialised to some length) */
205 | GString *from_clause = g_string_sized_new(STR_L);
206 | GString *where_clause = g_string_sized_new(STR_L);
207 | gchar **words = g_strsplit(keys, " ", 0);
208 |
209 | /* double quotes " are used in queries to allow querying for
210 | names like O'Hara */
211 |
212 | g_string_sprintfa(from_clause, "names N%.2d", 0);
213 | g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]);
214 |
215 | for (i=1; words[i] != NULL; i++) {
216 | g_string_sprintfa(from_clause, ", names N%.2d", i);
217 | g_string_sprintfa(where_clause, " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id", i, words[i], i);
218 | }
219 |
220 | sprintf(query_str, sql_query, from_clause->str, where_clause->str);
221 |
222 | /* Free up stuff */
223 | g_strfreev(words);
224 | g_string_free(where_clause,/* CONSTCOND */ TRUE);
225 | g_string_free(from_clause, /* CONSTCOND */ TRUE);
226 |
227 | } /* create_name_query() */
228 |
229 |
230 | /*++++++++++++++++++++++++++++++++++++++
231 | construct a range query for the as_block table
232 | (a query for an AS block object) given a string like:
233 | AS1
234 | AS1 - AS10
235 | AS1-AS10
236 |
237 | int create_asblock_query Returns 0 on success, -1 on failure
238 | (search term not an AS# nor range)
239 |
240 | char *query_str buffer for the final query (must be big enough)
241 |
242 | const char *sql_query rest of the sql query (with %d %d formats for
243 | AS numbers)
244 |
245 | const char *keys user-supplied search term.
246 |
247 | Author:
248 | marek
249 | ++++++++++++++++++++++++++++++++++++++*/
250 | static int create_asblock_query(char *query_str,
251 | const char *sql_query,
252 | const char *keys) {
253 | char *keycopy = wr_string(keys);
254 | char *token, *cursor = keycopy;
255 | int asnums[2] = {0,0};
256 | int index = 0; /* index into the asnums array */
257 |
258 |
259 | while( (token = strsep( &cursor, "-" )) != NULL && index < 2) {
260 | /* discard the letters (or leading whitespace), take the number */
261 | if( sscanf(token, "%*[ AS]%d", &asnums[index++]) < 1 ) {
262 | return -1; /* error */
263 | }
264 | }
265 | /* if only beginning was supplied, copy it as end */
266 | if( index == 1 ) {
267 | asnums[1] = asnums[0];
268 | }
269 |
270 | /* now construct the query */
271 | sprintf(query_str, sql_query, asnums[0], asnums[1]);
272 |
273 | wr_free(keycopy);
274 | return 0;
275 | }
276 |
277 |
278 | /*++++++++++++++++++++++++++++++++++++++
279 | add_filter(): construct a query to limit the objects returned from the last
280 | table to predefined types.
281 |
282 | char *query_str buffer for the final query, containing the initial
283 | part of the query (must be big enough)
284 |
285 | const Query_command *qc query command structure with the bitmap of
286 | object types to be included.
287 |
288 | Author:
289 | ottrey.
290 | ++++++++++++++++++++++++++++++++++++++*/
291 | static void add_filter(char *query_str, const Query_command *qc) {
292 | unsigned i;
293 | int qlen;
294 | char filter_atom[STR_M];
295 |
296 | #if 0
297 | /* glib string manipulation - untested yet */
298 |
299 | if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) {
300 | g_string_sprintfa(query_str, " AND (");
301 | for (i=0; i < C_END; i++) {
302 | if (MA_isset(qc->object_type_bitmap, i)) {
303 | g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
304 | }
305 | }
306 | g_string_truncate(query_str, query_str->len-3);
307 | g_string_append_c(query_str, ')');
308 | }
309 |
310 | #else /* classic string operations */
311 |
312 | /* add filters only if any bits are 0 (the number of 1's is < MAX_MAX */
313 | if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) {
314 | strcat(query_str, " AND (");
315 | for (i=0; i < C_END; i++) {
316 | if (MA_isset(qc->object_type_bitmap, i)) {
317 | strcpy(filter_atom, "");
318 | sprintf(filter_atom, "i.object_type = %d OR ", i);
319 | /* XXX class codes should be used instead:
320 | DF_get_class_dbase_code(i))
321 | but currently the tables contain values of enums
322 | (C_IN, etc) and not codes
323 | */
324 | strcat(query_str, filter_atom);
325 | }
326 | }
327 | qlen = strlen(query_str);
328 | query_str[qlen-3] = ')';
329 | query_str[qlen-2] = '\0';
330 | query_str[qlen-1] = '\0';
331 | }
332 |
333 | #endif
334 |
335 | } /* add_filter() */
336 |
337 | /* create_query() */
338 | /*++++++++++++++++++++++++++++++++++++++
339 | Create an sql query from the query_command and the matching keytype and the
340 | selected inverse attributes.
341 | Note this clears the first inv_attribute it sees, so is called sequentially
342 | until there are no inv_attributes left.
343 |
344 | WK_Type keytype The matching keytype.
345 |
346 | const Query_command *qc The query command.
347 |
348 | mask_t *inv_attrs_bitmap The selected inverse attributes.
349 |
350 | More:
351 | +html+ <PRE>
352 | Authors:
353 | ottrey
354 | +html+ </PRE>
355 |
356 | ++++++++++++++++++++++++++++++++++++++*/
357 | static char *create_query(const Query_t q, const Query_command *qc) {
358 | char *result=NULL;
359 | char result_buff[STR_XL];
360 | Q_Type_t querytype;
361 | int addquery = 0; /* controls if the query should be added to the list */
362 |
363 | if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
364 | querytype = Q_INVERSE;
365 | }
366 | else {
367 | querytype = Q_LOOKUP;
368 | }
369 |
370 | if ( (q.query != NULL)
371 | && (q.querytype == querytype) ) {
372 |
373 | /* addquery = 1; */
374 | /* if it got here, it should be added, unless.(see asblock)*/
375 |
376 | if (q.keytype == WK_NAME) {
377 | /* Name queries require special treatment. */
378 | create_name_query(result_buff, q.query, qc->keys);
379 | addquery = 1;
380 | }
381 | else if( q.keytype == WK_IPADDRESS ) { /* ifaddr sql lookups */
382 | ip_range_t myrang;
383 | unsigned begin, end;
384 | ip_keytype_t key_type;
385 |
386 | if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) {
387 | if(IP_rang_b2_space(&myrang) == IP_V4 ) {
388 | IP_rang_b2v4(&myrang, &begin, &end);
389 | sprintf(result_buff, q.query, begin, end);
390 | addquery = 1;
391 | }
392 | else {
393 | die;
394 | }
395 | }
396 | }
397 | else if( q.keytype == WK_ASRANGE ) { /* as_block range composition */
398 | if( create_asblock_query(result_buff, q.query, qc->keys) != 0 ) {
399 | addquery = 0; /* ... unless it's not correct */
400 | }
401 | else {
402 | addquery = 1;
403 | }
404 | }
405 | else {
406 | sprintf(result_buff, q.query, qc->keys);
407 | addquery = 1;
408 | }
409 |
410 | if (q.class == C_ANY && addquery == 1 ) {
411 | /* It is class type ANY so add the object filtering */
412 | add_filter(result_buff, qc);
413 | }
414 | }
415 |
416 | if( addquery == 1 ) {
417 | dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
418 | strcpy(result, result_buff);
419 | return result;
420 | }
421 | else {
422 | return NULL;
423 | }
424 | } /* create_query() */
425 |
426 | /* QI_fast_output() */
427 | /*++++++++++++++++++++++++++++++++++++++
428 | This is for the '-F' flag.
429 | It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
430 | Fast isn't fast anymore - it's just there for compatibility reasons.
431 |
432 | const char *str The object to be "fast output'ed".
433 |
434 | More:
435 | +html+ <PRE>
436 | Authors:
437 | ottrey,
438 | marek - glib strings + small changes
439 | +html+ </PRE>
440 | ++++++++++++++++++++++++++++++++++++++*/
441 | char *QI_fast_output(const char *str)
442 | {
443 | int i,j;
444 | char *result;
445 | GString *result_buff = g_string_sized_new(STR_XL);
446 | gchar **lines = g_strsplit(str, "\n", 0);
447 | unsigned char *value, *colon;
448 | char *attr;
449 |
450 | g_string_assign(result_buff, "");
451 |
452 | for (j=0; lines[j] != NULL; j++) {
453 |
454 | switch (lines[j][0]) {
455 | /* line continuation */
456 | case ' ':
457 | case '\t':
458 | case '+':
459 | value = (unsigned char *) lines[j]+1;
460 | while(*value != '\0' && isspace(*value)) {
461 | value++;
462 | }
463 | g_string_append(result_buff, "\n+ ");
464 | g_string_append(result_buff, (char *)value);
465 | break;
466 |
467 | default:
468 | /* a line of the form "attribute: value" */
469 | /* first: close the last line (if there was any, i.e. j>0) */
470 | if( j > 0 ) {
471 | g_string_append_c(result_buff, '\n');
472 | }
473 |
474 | /* get attribute name */
475 | attr = lines[j];
476 | colon = (unsigned char *) strchr(lines[j], ':');
477 | /* if there's no colon for whatever reason, dump the object
478 | and report the condition */
479 | if( colon == NULL ) {
480 | ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
481 | goto fast_output_cleanup;
482 | }
483 | *colon = '\0';
484 | for(value = colon+1; *value != '\0' && isspace(*value) ; value++) {
485 | ;
486 | }
487 |
488 | if( (i = DF_attribute_name2type(attr)) == -1 ) {
489 | /* warning! error in the object format */
490 | ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
491 | goto fast_output_cleanup;
492 |
493 | }
494 | else {
495 | /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
496 | g_string_append_c(result_buff, '*');
497 | g_string_append(result_buff, DF_get_attribute_code(i));
498 | g_string_append(result_buff, ": ");
499 | g_string_append(result_buff, (char *)value);
500 | }
501 | } /* switch */
502 | } /* for every line */
503 |
504 | fast_output_cleanup:
505 |
506 | g_strfreev(lines);
507 |
508 | g_string_append_c(result_buff, '\n');
509 | result = strdup(result_buff->str);
510 | dieif(result == NULL);
511 |
512 | g_string_free(result_buff,/* CONSTCOND */ TRUE);
513 |
514 | return result;
515 | } /* fast_output() */
516 |
517 | /* filter() */
518 | /*++++++++++++++++++++++++++++++++++++++
519 | Basically it's for the '-K' flag for non-set (and non-radix) objects.
520 | It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
521 |
522 | This could be speed up if there were breaks out of the loops, once it matched something.
523 |
524 | const char *string The string to be filtered.
525 |
526 | More:
527 | +html+ <PRE>
528 | Authors:
529 | ottrey
530 | +html+ </PRE>
531 |
532 | ++++++++++++++++++++++++++++++++++++++*/
533 | char *filter(const char *str) {
534 | int i,j, passed=0;
535 | char *result;
536 | GString *result_buff = g_string_sized_new(STR_XL);
537 | gchar **lines = g_strsplit(str, "\n", 0);
538 | char * const *filter_names;
539 | gboolean filtering_an_attribute = FALSE;
540 |
541 | filter_names = DF_get_filter_names();
542 |
543 | g_string_assign(result_buff, "");
544 |
545 | for (i=0; filter_names[i] != NULL; i++) {
546 | for (j=0; lines[j] != NULL; j++) {
547 | if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) {
548 |
549 | g_string_sprintfa(result_buff, "%s\n", lines[j]);
550 | passed++;
551 |
552 | /* CONSTCOND */
553 | filtering_an_attribute = TRUE;
554 | }
555 | /* CONSTCOND */
556 | else if (filtering_an_attribute == TRUE) {
557 | switch (lines[j][0]) {
558 | case ' ':
559 | case '\t':
560 | case '+':
561 |
562 | g_string_sprintfa(result_buff, "%s\n", lines[j]);
563 |
564 | break;
565 |
566 | default:
567 | filtering_an_attribute = FALSE;
568 | }
569 | }
570 | }
571 | }
572 |
573 | g_strfreev(lines);
574 |
575 | if(passed) {
576 | g_string_append(result_buff, "\n");
577 | }
578 | result = strdup(result_buff->str);
579 | g_string_free(result_buff,/* CONSTCOND */ TRUE);
580 |
581 | return result;
582 | } /* filter() */
583 |
584 | /* write_results() */
585 | /*++++++++++++++++++++++++++++++++++++++
586 | Write the results to the client socket.
587 |
588 | SQ_result_set_t *result The result set returned from the sql query.
589 | unsigned filtered if the objects should go through a filter (-K)
590 | sk_conn_st *condat Connection data for the client
591 |
592 | More:
593 | +html+ <PRE>
594 | Authors:
595 | ottrey - initial design
596 | marek - rewritten for accounting and cancellation.
597 | +html+ </PRE>
598 |
599 | ++++++++++++++++++++++++++++++++++++++*/
600 | static int write_results(SQ_result_set_t *result,
601 | unsigned filtered,
602 | unsigned fast,
603 | sk_conn_st *condat,
604 | acc_st *acc_credit,
605 | acl_st *acl
606 | ) {
607 | SQ_row_t *row;
608 | char *str;
609 | char *filtrate;
610 | char *fasted;
611 | int retrieved_objects=0;
612 | char *objt;
613 | int type;
614 |
615 | /* Get all the results - one at a time */
616 | if (result != NULL) {
617 | /* here we are making use of the mysql_store_result capability
618 | of interrupting the cycle of reading rows. mysql_use_result
619 | would not allow that, would have to be read until end */
620 |
621 | while ( condat->rtc == 0
622 | && AC_credit_isdenied( acc_credit ) == 0
623 | && (row = SQ_row_next(result)) != NULL ) {
624 |
625 | if ( (str = SQ_get_column_string(result, row, 0)) == NULL
626 | || (objt = SQ_get_column_string(result, row, 3)) == NULL ) {
627 | /* handle it somehow ? */
628 | die;
629 | }
630 | else {
631 | /* get + add object type */
632 | type = atoi(objt);
633 |
634 | /* ASP_QI_LAST_DET */
635 | ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
636 | "Retrieved serial id = %d , type = %s", atoi(str), objt);
637 |
638 | wr_free(str);
639 | wr_free(objt);
640 | }
641 |
642 | /* decrement credit for accounting purposes */
643 | AC_count_object( acc_credit, acl,
644 | type == C_PN || type == C_RO ); /* is private? */
645 |
646 | /* break the loop if the credit has just been exceeded and
647 | further results denied */
648 | if( AC_credit_isdenied( acc_credit ) ) {
649 | continue;
650 | }
651 |
652 | if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; }
653 | else {
654 |
655 | /* The fast output stage */
656 | if (fast == 1) {
657 | fasted = QI_fast_output(str);
658 | wr_free(str);
659 | str = fasted;
660 | }
661 |
662 | /* The filtering stage */
663 | if (filtered == 0) {
664 | SK_cd_puts(condat, str);
665 | SK_cd_puts(condat, "\n");
666 | }
667 | else {
668 |
669 | /* XXX accounting should be done AFTER filtering, not to count
670 | objects filtered out */
671 |
672 | filtrate = filter(str);
673 | SK_cd_puts(condat, filtrate);
674 | wr_free(filtrate);
675 | }
676 | retrieved_objects++;
677 | }
678 | wr_free(str);
679 | }
680 | }
681 |
682 | return retrieved_objects;
683 | } /* write_results() */
684 |
685 |
686 | /* write_objects() */
687 | /*++++++++++++++++++++++++++++++++++++++
688 |
689 | SQ_connection_t *sql_connection The connection to the database.
690 |
691 | char *id_table The id of the temporary table (This is a result of the hacky
692 | way we've tried to get MySQL to do sub-selects.)
693 |
694 | sk_conn_st *condat Connection data for the client
695 |
696 | More:
697 | +html+ <PRE>
698 | Authors:
699 | ottrey,
700 | marek.
701 | +html+ </PRE>
702 | ++++++++++++++++++++++++++++++++++++++*/
703 | static void write_objects(SQ_connection_t **sql_connection,
704 | char *id_table,
705 | unsigned int filtered,
706 | unsigned int fast,
707 | sk_conn_st *condat,
708 | acc_st *acc_credit,
709 | acl_st *acl
710 | )
711 | {
712 | SQ_result_set_t *result = NULL;
713 | int retrieved_objects=0;
714 | char sql_command[STR_XL];
715 |
716 | sprintf(sql_command, Q_OBJECTS, id_table);
717 |
718 | dieif(sql_execute_watched(condat, sql_connection, sql_command, &result) == -1 );
719 |
720 | /* Problem: if the query was aborted, the result structure does not
721 | refer to any existing connection anymore. So we check rtc here.
722 | */
723 |
724 | if( condat->rtc == 0) {
725 | retrieved_objects = write_results(result, filtered, fast, condat,
726 | acc_credit, acl);
727 | SQ_free_result(result);
728 | }
729 | } /* write_objects() */
730 |
731 | /* insert_radix_serials() */
732 | /*++++++++++++++++++++++++++++++++++++++
733 | Insert the radix serial numbers into a temporary table in the database.
734 |
735 | mask_t bitmap The bitmap of attribute to be converted.
736 |
737 | SQ_connection_t *sql_connection The connection to the database.
738 |
739 | char *id_table The id of the temporary table (This is a result of the hacky
740 | way we've tried to get MySQL to do sub-selects.)
741 |
742 | GList *datlist The list of data from the radix tree.
743 |
744 | More:
745 | +html+ <PRE>
746 | Authors:
747 | ottrey,
748 | marek
749 | +html+ </PRE>
750 |
751 | ++++++++++++++++++++++++++++++++++++++*/
752 | static void insert_radix_serials(sk_conn_st *condat,
753 | SQ_connection_t *sql_connection,
754 | char *id_table, GList *datlist) {
755 | GList *qitem;
756 | char sql_command[STR_XL];
757 | int serial;
758 |
759 | for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
760 | rx_datcpy_t *datcpy = qitem->data;
761 |
762 | serial = datcpy->leafcpy.data_key;
763 |
764 | sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial);
765 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1);
766 |
767 | wr_free(datcpy->leafcpy.data_ptr);
768 |
769 | if(condat->rtc != 0) {
770 | break;
771 | }
772 | }
773 |
774 | wr_clear_list( &datlist );
775 |
776 | } /* insert_radix_serials() */
777 |
778 |
779 | /* write_radix_immediate() */
780 | /*++++++++++++++++++++++++++++++++++++++
781 | Display the immediate data carried with the objects returned by the
782 | radix tree.
783 |
784 | GList *datlist The linked list of dataleaf copies
785 |
786 | sk_conn_st *condat Connection data for the client
787 |
788 | acc_st *acc_credit Accounting struct
789 |
790 | More:
791 | +html+ <PRE>
792 | Authors:
793 | marek
794 | +html+ </PRE>
795 |
796 | Also free the list of answers.
797 | ++++++++++++++++++++++++++++++++++++++*/
798 | static void write_radix_immediate(GList *datlist,
799 | sk_conn_st *condat,
800 | acc_st *acc_credit,
801 | acl_st *acl)
802 | {
803 | GList *qitem;
804 |
805 | for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
806 | rx_datcpy_t *datcpy = qitem->data;
807 |
808 | SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
809 | SK_cd_puts(condat, "\n");
810 |
811 | wr_free(datcpy->leafcpy.data_ptr);
812 |
813 | AC_count_object(acc_credit, acl, 0 /* public object (private=0) */ );
814 |
815 | if(condat->rtc != 0) {
816 | break;
817 | }
818 | }
819 |
820 | wr_clear_list( &datlist );
821 | } /* write_radix_immediate() */
822 |
823 |
824 | /* map_qc2rx() */
825 | /*++++++++++++++++++++++++++++++++++++++
826 | The mapping between a query_command and a radix query.
827 |
828 | Query_instruction *qi The Query Instruction to be created from the mapping
829 | of the query command.
830 |
831 | const Query_command *qc The query command to be mapped.
832 |
833 | More:
834 | +html+ <PRE>
835 | Authors:
836 | ottrey,
837 | marek - simplified the logic, added stealth -S option
838 | +html+ </PRE>
839 |
840 | ++++++++++++++++++++++++++++++++++++++*/
841 | static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
842 | int result=1;
843 | int allflags = (qc->L == 1) + (qc->M == 1) + (qc->l == 1)
844 | + (qc->m == 1) + (qc->x == 1);
845 |
846 | qi->rx_keys = qc->keys;
847 |
848 | /* only one option can be active at a time */
849 |
850 | if( allflags > 1 ) {
851 | /* user error (this should have been checked before) */
852 |
853 | ER_dbg_va(FAC_QI, ASP_QI_SKIP,
854 | "ERROR in qc2rx mapping: bad combination of flags");
855 | result = 0;
856 | }
857 | if( allflags == 0 ) {
858 | /* no options active - default search */
859 | qi->rx_srch_mode = RX_SRCH_EXLESS;
860 | qi->rx_par_a = 0;
861 | }
862 | else if ( qc->L == 1 ) {
863 | qi->rx_srch_mode = RX_SRCH_LESS;
864 | qi->rx_par_a = RX_ALL_DEPTHS;
865 | }
866 | else if (qc->M == 1) {
867 | qi->rx_srch_mode = RX_SRCH_MORE;
868 | qi->rx_par_a = RX_ALL_DEPTHS;
869 | }
870 | else if (qc->l == 1) {
871 | qi->rx_srch_mode = RX_SRCH_LESS;
872 | qi->rx_par_a = 1;
873 | }
874 | else if (qc->m == 1) {
875 | qi->rx_srch_mode = RX_SRCH_MORE;
876 | qi->rx_par_a = 1;
877 | }
878 | else if (qc->x == 1) {
879 | qi->rx_srch_mode = RX_SRCH_EXACT;
880 | qi->rx_par_a = 0;
881 | }
882 |
883 | if( qi->rx_srch_mode == RX_SRCH_MORE && (qc->S == 1) ) {
884 | qi->rx_srch_mode = RX_SRCH_DBLS;
885 | }
886 |
887 | return result;
888 |
889 | } /* map_qc2rx() */
890 |
891 |
892 | /* run_referral() */
893 | /*++++++++++++++++++++++++++++++++++++++
894 |
895 | invoked when no such domain found. Goes through the domain table
896 | and searches for shorter domains, then if it finds one with referral
897 | it performs it, otherwise it just returns nothing.
898 |
899 | to perform referral, it actually composes the referral query
900 | for a given host/port/type and calls the whois query function.
901 |
902 | Well, it returns nothing anyway (void). It just prints to the socket.
903 |
904 | char *ref_host referral server host name
905 |
906 | unsigned ref_port_int referral server port number
907 |
908 | char *ref_type referral type name
909 |
910 | char *qry query to be run
911 |
912 | Author:
913 | marek
914 | ++++++++++++++++++++++++++++++++++++++*/
915 | void run_referral(Query_environ *qe,
916 | char *ref_host,
917 | unsigned ref_port_int,
918 | char *ref_type,
919 | char *qry)
920 | {
921 |
922 | #if 1 /* switch off for testing */
923 | er_ret_t err;
924 | char *rep;
925 |
926 | /* WH_sock(sock, host, port, query, maxlines, timeout)) */
927 | err= WH_cd_sock(&(qe->condat), ref_host, ref_port_int, qry,
928 | ca_get_referralmaxlines, ca_get_referraltimeout
929 | );
930 |
931 | switch( err ) {
932 | case SK_OK:
933 | /* OK */
934 | break;
935 | case SK_TIMEOUT:
936 | /* Referral timeout */
937 | rep = ca_get_qi_ref_tmout ;
938 | SK_cd_puts(&(qe->condat), rep);
939 | wr_free(rep);
940 | break;
941 |
942 | case SK_BADHOST:
943 | /* Referral host not found */
944 | rep = ca_get_qi_ref_badhost ;
945 | SK_cd_puts(&(qe->condat), rep);
946 | wr_free(rep);
947 | break;
948 |
949 | case SK_CONNECT:
950 | /* Referral host not responding */
951 | rep = ca_get_qi_ref_hostnottresp ;
952 | SK_cd_puts(&(qe->condat), rep);
953 | wr_free(rep);
954 | break;
955 |
956 | case SK_BIND:
957 | case SK_SOCKET:
958 | /* XXX internal server problem... */
959 | die;
960 |
961 | case WH_MAXLINES:
962 | /* Referral reply line limit exceeded */
963 | rep = ca_get_qi_ref_overmaxlin ;
964 | SK_cd_puts(&(qe->condat), rep);
965 | wr_free(rep);
966 | break;
967 |
968 | default: /* any other errors ? */
969 | die;
970 | ;
971 | } /*switch WH_sock */
972 | #endif
973 |
974 | }/*run_referral*/
975 |
976 |
977 |
978 |
979 |
980 | /*++++++++++++++++++++++++++++++++++++++
981 |
982 | prepare and run the referral, displaying the results directly to the
983 | client's connection.
984 |
985 | XXX still missing protection against a referral loop
986 | XXX handling inverse flag not needed, to be removed
987 |
988 | char *domain domain being looked up
989 |
990 | Query_instructions *qis original query instructions structure
991 |
992 | Query_environ *qe original query environment structure
993 |
994 | Query_instruction *qi specific query instruction triggered
995 |
996 | SQ_result_set_t *result result of the lookup containing referral details
997 |
998 | SQ_row_t *row first row (should be only 1) of the result
999 |
1000 | char *sourcename name of the database "source"
1001 |
1002 | Author:
1003 | marek
1004 | ++++++++++++++++++++++++++++++++++++++*/
1005 | static
1006 | void qi_prep_run_refer(char *domain,
1007 | Query_instructions *qis,
1008 | Query_environ *qe,
1009 | Query_instruction *qi,
1010 | SQ_result_set_t *result, SQ_row_t *row,
1011 | char *sourcename )
1012 | {
1013 | char *ref_host = SQ_get_column_string(result, row, 2);
1014 | char *ref_type = SQ_get_column_string(result, row, 0);
1015 | char *ref_port = SQ_get_column_string(result, row, 1);
1016 | unsigned ref_port_int;
1017 | char querystr[STR_L];
1018 |
1019 | /* get the integer value, it should be correct */
1020 | if( sscanf( ref_port, "%d",&ref_port_int) < 1 ) {
1021 | die;
1022 | }
1023 |
1024 | strcpy(querystr,"");
1025 |
1026 | /* put -r if the reftype is RIPE and -r or -i were used */
1027 | if( strcmp(ref_type,"RIPE") == 0
1028 | && ( Query[qi->queryindex].querytype == Q_INVERSE
1029 | || qis->recursive > 0 ) ) {
1030 | strcat(querystr," -r ");
1031 | }
1032 |
1033 | /* prepend with -Vversion,IP for type CLIENTADDRESS */
1034 | if( strcmp(ref_type,"CLIENTADDRESS") == 0 ) {
1035 | char optv[STR_M];
1036 |
1037 | snprintf(optv,STR_M," -V%s,%s ",VERSION, qe->condat.ip);
1038 | strcat(querystr,optv);
1039 | }
1040 |
1041 | /* now set the search term - set to the stripped down version
1042 | for inverse query, full-length otherwise */
1043 | if( Query[qi->queryindex].querytype == Q_INVERSE ) {
1044 | strcat(querystr, domain);
1045 | }
1046 | else {
1047 | strcat(querystr, qis->qc->keys);
1048 | }
1049 |
1050 | {
1051 | /* the object is not from %s,
1052 | it comes from %s %d, use -R to see %s */
1053 | char *rep = ca_get_qi_fmt_refheader ;
1054 | SK_cd_printf(&(qe->condat), rep,
1055 | sourcename,
1056 | ref_host, ref_port_int,
1057 | sourcename );
1058 | wr_free(rep);
1059 | }
1060 |
1061 | /* do the referral */
1062 | ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host);
1063 |
1064 | run_referral( qe, ref_host, ref_port_int, ref_type, querystr);
1065 |
1066 | { /* End of referred query result */
1067 | char *rep = ca_get_qi_reftrailer ;
1068 | SK_cd_puts(&(qe->condat), rep);
1069 | wr_free(rep);
1070 | }
1071 |
1072 | wr_free(ref_host);
1073 | wr_free(ref_type);
1074 | wr_free(ref_port);
1075 | }
1076 |
1077 |
1078 | /*++++++++++++++++++++++++++++++++++++++
1079 |
1080 | specific case of the object ID collection: the domains.
1081 | Checks to see if the domain exists, and runs the referral if it is defined
1082 | and the domain is missing.
1083 |
1084 | Arguments:
1085 |
1086 | char *sourcename name of the database "source"
1087 |
1088 | SQ_connection_t *sql_connection sql connection dedicated to this thread
1089 |
1090 | char *id_table name of the temporary table to be used
1091 |
1092 | char *sub_table name of the temporary subtable
1093 |
1094 | Query_instructions *qis original query instructions structure
1095 |
1096 | Query_environ *qe original query environment structure
1097 |
1098 | Query_instruction *qi specific query instruction triggered
1099 |
1100 | acc_st *acc_credit credit for this client
1101 |
1102 | Author:
1103 | marek.
1104 | ++++++++++++++++++++++++++++++++++++++*/
1105 |
1106 | static int
1107 | qi_collect_domain(char *sourcename,
1108 | SQ_connection_t *sql_connection,
1109 | char *id_table,
1110 | char *sub_table,
1111 | Query_instructions *qis,
1112 | Query_environ *qe,
1113 | Query_instruction *qi,
1114 | acc_st *acc_credit)
1115 | {
1116 | char *domain = qis->qc->keys;
1117 | char *dot = domain;
1118 | int subcount = 0;
1119 | int foundcount = 0;
1120 |
1121 | /* while nothing found and still some pieces of the name left */
1122 | while( dot != NULL && subcount == 0 ) {
1123 | int refcount = 0;
1124 | SQ_row_t *row;
1125 | SQ_result_set_t *result_referrals = NULL;
1126 | char sql_command[STR_XL];
1127 |
1128 | ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot);
1129 |
1130 | /* domain lookup -- query into the _S table */
1131 | sprintf(sql_command, "INSERT INTO %s SELECT object_id FROM domain WHERE domain = '%s'", sub_table, dot);
1132 |
1133 | dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1);
1134 | subcount = SQ_get_affected_rows(sql_connection);
1135 |
1136 | if( subcount != 0 ) { /* domain exists in the database */
1137 |
1138 | /* referral check. Always done except for -R and INVERSE queries */
1139 | if( qis->qc->R == 0 &&
1140 | Query[qi->queryindex].querytype != Q_INVERSE ) {
1141 | sprintf(sql_command, "SELECT type, port, host FROM %s ID, refer WHERE ID.id = refer.object_id", sub_table);
1142 | dieif( SQ_execute_query(sql_connection, sql_command,
1143 | &result_referrals) == -1);
1144 | refcount = SQ_num_rows(result_referrals);
1145 | }
1146 |
1147 | /* if referral allowed and defined, even if domain was found but
1148 | contained referral - refer the query */
1149 | if( refcount != 0 ) {
1150 | /* get the referral parameters from the first row
1151 | and perform it
1152 | */
1153 |
1154 | row = SQ_row_next(result_referrals);
1155 | /* now: query for the original domain */
1156 | qi_prep_run_refer(domain,
1157 | qis, qe, qi, result_referrals, row, sourcename);
1158 |
1159 | acc_credit->referrals -= 1;
1160 | }
1161 | else {
1162 | /* domain found
1163 | and (referral undefined or disabled by -R or inverse)
1164 | two possible outcomes depending on whether 'dot' is:
1165 | * the original search term -> pass what's in _S and quit
1166 | * a 'stripped' domain name -> return no result and quit
1167 | */
1168 | if( dot == domain ) {
1169 | sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s",
1170 | id_table, sub_table);
1171 | dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1);
1172 | foundcount = SQ_get_affected_rows(sql_connection);
1173 | }
1174 | }
1175 | dot = NULL; /* don't make another round */
1176 | } /* a domain was found */
1177 |
1178 | if( result_referrals != NULL ) {
1179 | SQ_free_result(result_referrals);
1180 | result_referrals = NULL;
1181 | }
1182 |
1183 | if( dot != NULL && (dot=index(dot,'.')) != NULL) {
1184 | dot++;
1185 | }
1186 | }
1187 |
1188 | return foundcount;
1189 | } /* check_domain */
1190 |
1191 |
1192 | /* add_ref_name */
1193 | /*++++++++++++++++++++++++++++++++++++++
1194 |
1195 | Creates a SQL query for a reference-by-name lookup. Uses standard name
1196 | lookup query generator (create_name_query), so the order of the names
1197 | doesn't matter.
1198 |
1199 | SQ_connection_t *sql_connection sql connection dedicated to this thread
1200 |
1201 | char *rectable table in which to look up
1202 |
1203 | char *allnames all name words to be looked up, space delimited.
1204 |
1205 | ++++++++++++++++++++++++++++++++++++++*/
1206 | static
1207 | void
1208 | add_ref_name(SQ_connection_t *sql_connection,
1209 | char *rectable,
1210 | char *allnames
1211 | )
1212 | {
1213 | /* construct the query, allow zero-length list */
1214 | if( strlen(allnames) > 0 ) {
1215 | char final_query[STR_XL];
1216 | char select_query[STR_XL];
1217 |
1218 | create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s "
1219 | "AND N00.object_type != 100 AND N00.thread_id = 0",
1220 | allnames);
1221 |
1222 | sprintf(final_query, "INSERT INTO %s %s",
1223 | rectable,
1224 | select_query);
1225 |
1226 | dieif(SQ_execute_query(sql_connection, final_query, NULL) == -1 );
1227 |
1228 | allnames[0]=0;
1229 | }
1230 | }/* add_ref_name */
1231 |
1232 |
1233 |
1234 | /* qi_collect_ids */
1235 | /*++++++++++++++++++++++++++++++++++++++
1236 |
1237 | collects object ID's from all queries defined in the Query_instructions
1238 | array. The results from RADIX trees are maintained in a linked list, the
1239 | results from SQL lookups are kept in a temporary table. For domains,
1240 | a specific function is invoked that may run the referral.
1241 | Any sql lookup will be limited to the maximum number of objects allowed
1242 | for the client (acl and credit are checked for this).
1243 | The routine uses its own temporary _S table, destroyed at exit.
1244 |
1245 | ca_dbSource_t *dbhdl source-specific identifier (defined in CA)
1246 |
1247 | char *sourcename name of the database "source"
1248 |
1249 | SQ_connection_t **sql_connection sql connection dedicated to this thread
1250 | (replaced on cancel)
1251 |
1252 | Query_instructions *qis original query instructions structure
1253 |
1254 | Query_environ *qe original query environment structure
1255 |
1256 | char *id_table the table to store the ID's found
1257 |
1258 | GList **datlist the list to store the Radix leaves found
1259 |
1260 | acc_st *acc_credit credit for this client
1261 |
1262 | acl_st *acl acl for this client
1263 |
1264 | ++++++++++++++++++++++++++++++++++++++*/
1265 | static
1266 | void
1267 | qi_collect_ids(ca_dbSource_t *dbhdl,
1268 | char *sourcename,
1269 | SQ_connection_t **sql_connection,
1270 | Query_instructions *qis,
1271 | Query_environ *qe,
1272 | char *id_table,
1273 | GList **datlist,
1274 | acc_st *acc_credit,
1275 | acl_st *acl
1276 | )
1277 | {
1278 | Query_instruction **ins=NULL;
1279 | int i;
1280 | int count, errors=0;
1281 | char sql_command[STR_XL];
1282 | er_ret_t err;
1283 | char sub_table[32];
1284 | int limit ;
1285 | /* a limit on the max number of objects to be returned
1286 | from a single search. For some queries the object types
1287 | are not known at this stage, so the limit must be
1288 | the higher number of the two: private / public,
1289 | or unlimited if any of them is 'unlimited'.
1290 | */
1291 | char limit_str[32];
1292 |
1293 | if( (limit = AC_get_higher_limit(acc_credit,acl)) == -1) {
1294 | strcpy(limit_str,"");
1295 | } else {
1296 | sprintf(limit_str," LIMIT %d", limit+1); /* make sure we collect more
1297 | so that the client hits
1298 | the limit */
1299 | }
1300 |
1301 | sprintf(sub_table, "%s_S ", id_table);
1302 |
1303 | /* see if there was a leftover table from a crashed session
1304 | * (assume the ID cannot be currently in use)
1305 | */
1306 | sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1307 | dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1308 |
1309 | /* create a table for special subqueries (domain only for now) */
1310 | sprintf(sql_command, "CREATE TABLE %s ( id int ) TYPE=HEAP", sub_table);
1311 | dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1312 |
1313 | /* Iterate through query instructions */
1314 | ins = qis->instruction;
1315 | for (i=0; ins[i] != NULL && errors == 0; i++) {
1316 | Query_instruction *qi = ins[i];
1317 |
1318 | /* check if the client is still there */
1319 | if( qe->condat.rtc ) {
1320 | break;
1321 | }
1322 |
1323 | switch ( qi->search_type ) {
1324 | case R_SQL:
1325 | if ( qi->query_str != NULL ) {
1326 |
1327 | /* handle special cases first */
1328 | if( Query[qi->queryindex].class == C_DN
1329 | && Query[qi->queryindex].querytype == Q_LOOKUP ) {
1330 |
1331 | /* if any more cases than just domain appear, we will be
1332 | cleaning the _S table from the previous query here
1333 |
1334 | "DELETE FROM %s_S"
1335 | */
1336 |
1337 | count = qi_collect_domain(sourcename, *sql_connection, id_table,
1338 | sub_table, qis, qe, qi, acc_credit);
1339 | } /* if class DN and Straight lookup */
1340 | else {
1341 | /* any other class of query */
1342 |
1343 | sprintf(sql_command, "INSERT INTO %s %s %s",
1344 | id_table, qi->query_str, limit_str);
1345 |
1346 | if(sql_execute_watched( &(qe->condat), sql_connection,
1347 | sql_command, NULL) == -1 ) {
1348 |
1349 | ER_perror(FAC_QI, QI_SQLERR," query='%s' [%d] %s",
1350 | sql_command,
1351 | SQ_errno(*sql_connection), SQ_error(*sql_connection));
1352 | errors++;
1353 | }
1354 | count = SQ_get_affected_rows(*sql_connection);
1355 | } /* not DN */
1356 | } /* if SQL query not NULL */
1357 |
1358 | ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1359 | "%d entries added in %s query for %s",
1360 | count, Query[qi->queryindex].descr, qis->qc->keys
1361 | );
1362 | break;
1363 |
1364 | case R_RADIX:
1365 |
1366 |
1367 | err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0,
1368 | qi->rx_keys, dbhdl,
1369 | Query[qi->queryindex].attribute,
1370 | datlist, limit);
1371 |
1372 |
1373 | if( NOERR(err)) {
1374 | if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) {
1375 | /* prevent unnecessary g_list_length call */
1376 |
1377 | ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1378 | "%d entries after %s (mode %d par %d reg %d) query for %s",
1379 | g_list_length(*datlist),
1380 | Query[qi->queryindex].descr,
1381 | qi->rx_srch_mode, qi->rx_par_a,
1382 | dbhdl,
1383 | qi->rx_keys);
1384 | }
1385 | }
1386 | else {
1387 | ER_inf_va(FAC_QI, ASP_QI_COLL_DET,
1388 | "RP_asc_search returned %x ", err);
1389 | }
1390 | break;
1391 |
1392 | default: die;
1393 | } /* switch */
1394 |
1395 | } /* for <every instruction> */
1396 |
1397 | /* Now drop the _S table */
1398 | sprintf(sql_command, "DROP TABLE %s", sub_table);
1399 | dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1400 |
1401 | }
1402 |
1403 | /* qi_fetch_references */
1404 | /*++++++++++++++++++++++++++++++++++++++
1405 |
1406 | given the list of object ID's collects the references from these objects
1407 | to person and role objects. Uses its own temporary SQL table (_R)
1408 | and upon completion transfers the results from it to the main
1409 | temporary table. Runs queries in watched mode, to be able to cancel them.
1410 |
1411 | SQ_connection_t **sql_connection sql connection dedicated to this thread
1412 | (replaced on cancel)
1413 |
1414 | Query_environ *qe original query environment structure
1415 |
1416 | char *id_table the table with the ID's found
1417 |
1418 | acc_st *acc_credit credit for this client
1419 |
1420 | acl_st *acl acl for this client
1421 |
1422 | ++++++++++++++++++++++++++++++++++++++*/
1423 | static
1424 | void
1425 | qi_fetch_references(SQ_connection_t **sql_connection,
1426 | Query_environ *qe,
1427 | char *id_table,
1428 | acc_st *acc_credit,
1429 | acl_st *acl
1430 | )
1431 | {
1432 | char rec_table[32];
1433 | SQ_result_set_t *result = NULL;
1434 | SQ_row_t *row;
1435 | int thisid = 0;
1436 | int oldid = 0;
1437 | char allnames[STR_L];
1438 | char sql_command[STR_XL];
1439 |
1440 | sprintf(rec_table, "%s_R", id_table);
1441 |
1442 | /* see if there was a leftover table from a crashed session
1443 | * (assume the ID cannot be currently in use)
1444 | */
1445 | sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table);
1446 | dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1447 |
1448 | /* a temporary table for recursive data must be created, because
1449 | a query using the same table as a source and target is illegal
1450 | ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... )
1451 | */
1452 | sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", rec_table);
1453 | dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1454 |
1455 | /* find the contacts */
1456 | sprintf(sql_command, Q_REC, rec_table, id_table, "author");
1457 | dieif(sql_execute_watched( &(qe->condat), sql_connection, sql_command, NULL) == -1 );
1458 |
1459 | sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c");
1460 | dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
1461 |
1462 | sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c" );
1463 | dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
1464 |
1465 | sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c" );
1466 | dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
1467 |
1468 |
1469 | /* replace references to dummies by references by name */
1470 | sprintf(sql_command,
1471 | " SELECT id, name FROM %s IDS STRAIGHT_JOIN names "
1472 | " WHERE IDS.id = names.object_id "
1473 | " AND names.object_type = 100"
1474 | " ORDER BY id",
1475 | rec_table);
1476 |
1477 | dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command,
1478 | &result) == -1 );
1479 | /* well, it might not be -1, but if the watchdog worked then the
1480 | result is NULL */
1481 | if( result != NULL ) {
1482 |
1483 | allnames[0]=0;
1484 | /* now go through the results and collect names */
1485 | while ( (qe->condat.rtc == 0)
1486 | && (row = SQ_row_next(result)) != NULL ) {
1487 | char *id = SQ_get_column_string(result, row, 0);
1488 | char *name = SQ_get_column_string(result, row, 1);
1489 |
1490 | thisid = atoi(id);
1491 |
1492 | /* when the id changes, the name is complete */
1493 | if( thisid != oldid && oldid != 0 ) {
1494 | add_ref_name( *sql_connection, rec_table, allnames);
1495 | }
1496 |
1497 | strcat(allnames, name);
1498 | strcat(allnames, " ");
1499 | oldid = thisid;
1500 | wr_free(id);
1501 | wr_free(name);
1502 | }
1503 | /* also do the last name */
1504 | add_ref_name( *sql_connection, rec_table, allnames);
1505 |
1506 | SQ_free_result(result); /* we can do it only because the watchdog */
1507 | /* has not started between the check for non-NULL result and here */
1508 | }
1509 |
1510 | /* now copy things back to the main temporary table */
1511 | sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s",
1512 | id_table, rec_table);
1513 | dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1514 |
1515 | /* Now drop the IDS recursive table */
1516 | sprintf(sql_command, "DROP TABLE %s", rec_table);
1517 | dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1518 | }
1519 | /* qi_fetch_references */
1520 |
1521 |
1522 | /* QI_execute() */
1523 | /*++++++++++++++++++++++++++++++++++++++
1524 | Execute the query instructions. This is called for each source.
1525 | This is linked into MySQL by the fact that MySQL doesn't have sub selects
1526 | (yet). The queries are done in two stages. Make some temporary tables and
1527 | insert into them. Then use them in the next select.
1528 |
1529 |
1530 | ca_dbSource_t *dbhdl source-specific identifier (defined in CA)
1531 |
1532 | Query_instructions *qis query instructions.
1533 |
1534 | Query_environ *qe query environment.
1535 |
1536 | acc_st *acc_credit object display credit
1537 |
1538 | acl_st *acl copy of the original acl for this client
1539 |
1540 | More:
1541 | +html+ <PRE>
1542 | Authors:
1543 | ottrey - original version,
1544 | marek - the rest.
1545 | +html+ </PRE>
1546 | ++++++++++++++++++++++++++++++++++++++*/
1547 | er_ret_t QI_execute(ca_dbSource_t *dbhdl,
1548 | Query_instructions *qis,
1549 | Query_environ *qe,
1550 | acc_st *acc_credit,
1551 | acl_st *acl
1552 | )
1553 | {
1554 | /* those things must be freed after use! */
1555 | char *dbhost = ca_get_srcdbmachine(dbhdl);
1556 | char *dbname = ca_get_srcdbname(dbhdl);
1557 | char *dbuser = ca_get_srcdbuser(dbhdl);
1558 | char *dbpass = ca_get_srcdbpassword(dbhdl);
1559 | char *srcnam = ca_get_srcname(dbhdl);
1560 | unsigned dbport = ca_get_srcdbport(dbhdl);
1561 | char id_table[STR_S];
1562 | char sql_command[STR_XL];
1563 | GList *datlist=NULL;
1564 | SQ_connection_t *sql_connection=NULL;
1565 |
1566 | sql_connection = SQ_get_connection( dbhost, dbport,
1567 | dbname, dbuser, dbpass );
1568 | if (sql_connection == NULL) {
1569 | ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s",
1570 | dbname, SQ_errno(sql_connection), SQ_error(sql_connection));
1571 | return QI_CANTDB;
1572 | }
1573 |
1574 | sprintf(id_table, "ID_%ld_%d", mysql_thread_id(sql_connection),
1575 | pthread_self());
1576 |
1577 | /* see if there was a leftover table from a crashed session
1578 | * (assume the ID cannot be currently in use)
1579 | */
1580 | sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1581 | dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
1582 |
1583 | /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */
1584 | sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table);
1585 | dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
1586 |
1587 | qi_collect_ids(dbhdl, srcnam, &sql_connection, qis, qe, id_table,
1588 | &datlist, acc_credit, acl);
1589 |
1590 | /* post-processing */
1591 | if( qis->filtered == 0 ) {
1592 | /* start the watchdog just to set the rtc flag */
1593 | SK_watch_setclear(&(qe->condat));
1594 | SK_watchstart(&(qe->condat));
1595 |
1596 | /* add radix results (only if -K is not active) */
1597 | insert_radix_serials(&(qe->condat), sql_connection, id_table, datlist);
1598 |
1599 | SK_watchstop(&(qe->condat));
1600 | }
1601 |
1602 | /* fetch recursive objects (ac,tc,zc,ah) */
1603 | if ( qis->recursive ) {
1604 | qi_fetch_references( &sql_connection, qe, id_table, acc_credit, acl);
1605 | } /* if recursive */
1606 |
1607 | /* display */
1608 | /* -K filtering:
1609 | * right now only filtering, no expanding sets like write_set_objects()
1610 | */
1611 |
1612 | /* display the immediate data from the radix tree */
1613 | if( qis->filtered == 1 ) {
1614 | write_radix_immediate(datlist, &(qe->condat), acc_credit, acl );
1615 | }
1616 |
1617 | /* display objects from the IDs table */
1618 | write_objects( &sql_connection, id_table, qis->filtered,
1619 | qis->fast, &(qe->condat), acc_credit, acl);
1620 |
1621 | /* Now drop the IDS table */
1622 | sprintf(sql_command, "DROP TABLE %s", id_table);
1623 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
1624 | SQ_close_connection(sql_connection);
1625 |
1626 | /* free allocated parameters */
1627 | wr_free(dbhost);
1628 | wr_free(dbname);
1629 | wr_free(dbuser);
1630 | wr_free(dbpass);
1631 | wr_free(srcnam);
1632 |
1633 | return QI_OK;
1634 | } /* QI_execute() */
1635 |
1636 |
1637 | /* instruction_free() */
1638 | /*++++++++++++++++++++++++++++++++++++++
1639 | Free the instruction.
1640 |
1641 | Query_instruction *qi query_instruction to be freed.
1642 |
1643 | More:
1644 | +html+ <PRE>
1645 | Authors:
1646 | ottrey
1647 | +html+ </PRE>
1648 | ++++++++++++++++++++++++++++++++++++++*/
1649 | static void instruction_free(Query_instruction *qi) {
1650 | if (qi != NULL) {
1651 | if (qi->query_str != NULL) {
1652 | wr_free(qi->query_str);
1653 | }
1654 | wr_free(qi);
1655 | }
1656 | } /* instruction_free() */
1657 |
1658 | /* QI_free() */
1659 | /*++++++++++++++++++++++++++++++++++++++
1660 | Free the query_instructions.
1661 |
1662 | Query_instructions *qis Query_instructions to be freed.
1663 |
1664 | More:
1665 | +html+ <PRE>
1666 | Authors:
1667 | ottrey, marek
1668 | +html+ </PRE>
1669 | ++++++++++++++++++++++++++++++++++++++*/
1670 | void QI_free(Query_instructions *qis) {
1671 | int i;
1672 |
1673 | for (i=0; qis->instruction[i] != NULL; i++) {
1674 | instruction_free(qis->instruction[i]);
1675 | }
1676 |
1677 | if (qis != NULL) {
1678 | wr_free(qis);
1679 | }
1680 |
1681 | } /* QI_free() */
1682 |
1683 | /*++++++++++++++++++++++++++++++++++++++
1684 | Determine if this query should be conducted or not.
1685 |
1686 | If it was an inverse query - if the attribute appears in the query command's bitmap.
1687 | If it was a lookup query - if the attribute appears in the object type bitmap or
1688 | disregard if there is no object_type bitmap (Ie object filter).
1689 |
1690 | mask_t bitmap The bitmap of attribute to be converted.
1691 |
1692 | const Query_command *qc The query_command that the instructions are created
1693 | from.
1694 |
1695 | const Query_t q The query being considered.
1696 | +html+ <PRE>
1697 | Authors:
1698 | ottrey,
1699 | marek.
1700 | +html+ </PRE>
1701 | ++++++++++++++++++++++++++++++++++++++*/
1702 | static int valid_query(const Query_command *qc, const Query_t q) {
1703 | int result=0;
1704 |
1705 | if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
1706 | if (q.query != NULL) {
1707 | switch (q.querytype) {
1708 | case Q_INVERSE:
1709 | if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
1710 | result = 1;
1711 | }
1712 | break;
1713 |
1714 | case Q_LOOKUP:
1715 | if (q.class == C_ANY
1716 | || MA_isset(qc->object_type_bitmap, (unsigned) q.class)) {
1717 | result=1;
1718 | }
1719 | break;
1720 |
1721 | default:
1722 | /* XXX */fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
1723 | }
1724 | }
1725 | }
1726 |
1727 | return result;
1728 | } /* valid_query() */
1729 |
1730 | /* QI_new() */
1731 | /*++++++++++++++++++++++++++++++++++++++
1732 | Create a new set of query_instructions. Returns an allocated structure which
1733 | must be freed after use with QI_free().
1734 |
1735 | const Query_command *qc The query_command that the instructions are created
1736 | from.
1737 |
1738 | const Query_environ *qe The environmental variables that they query is being
1739 | performed under.
1740 |
1741 | +html+ <PRE>
1742 | Authors:
1743 | ottrey,
1744 | marek.
1745 | +html+ </PRE>
1746 | ++++++++++++++++++++++++++++++++++++++*/
1747 | Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
1748 | Query_instructions *qis=NULL;
1749 | Query_instruction *qi=NULL;
1750 | int i_no=0;
1751 | int i;
1752 | char *query_str;
1753 |
1754 | dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK);
1755 |
1756 | qis->filtered = qc->filtered;
1757 | qis->fast = qc->fast;
1758 | qis->recursive = qc->recursive;
1759 | qis->qc = (qc);
1760 |
1761 |
1762 | for (i=0; Query[i].query != NULL; i++) {
1763 |
1764 | /* If a valid query. */
1765 | if ( valid_query(qc, Query[i]) == 1) {
1766 |
1767 | dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK);
1768 |
1769 | qi->queryindex = i;
1770 |
1771 | /* SQL Query */
1772 | if ( Query[i].refer == R_SQL) {
1773 | qi->search_type = R_SQL;
1774 | query_str = create_query(Query[i], qc);
1775 |
1776 | if (query_str!= NULL) {
1777 | qi->query_str = query_str;
1778 | qis->instruction[i_no++] = qi;
1779 | }
1780 | }
1781 | /* Radix Query */
1782 | else if (Query[i].refer == R_RADIX) {
1783 | qi->search_type = R_RADIX;
1784 |
1785 | if (map_qc2rx(qi, qc) == 1) {
1786 | int j;
1787 | int found=0;
1788 |
1789 | /* check that there is no such query yet, for example if
1790 | more than one keytype (wk) matched */
1791 | for (j=0; j<i_no; j++) {
1792 | Query_instruction *qij = qis->instruction[j];
1793 |
1794 | if( qij->search_type == R_RADIX
1795 | && Query[qij->queryindex].attribute
1796 | == Query[qi ->queryindex].attribute) {
1797 |
1798 | found=1;
1799 | break;
1800 | }
1801 | }
1802 |
1803 | if ( found ) {
1804 | /* Discard the Query Instruction */
1805 | wr_free(qi);
1806 | }
1807 | else {
1808 | /* Add the query_instruction to the array */
1809 | qis->instruction[i_no++] = qi;
1810 | }
1811 | }
1812 | }
1813 | else {
1814 | /* ERROR: bad search_type */
1815 | die;
1816 | }
1817 | }
1818 | }
1819 | qis->instruction[i_no++] = NULL;
1820 |
1821 |
1822 | { /* tracing */
1823 | char *descrstr = QI_queries_to_string(qis);
1824 |
1825 | ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr );
1826 | wr_free( descrstr );
1827 | }
1828 |
1829 | return qis;
1830 |
1831 | } /* QI_new() */
1832 |
1833 |
1834 |
1835 |
1836 |
1837 | /*++++++++++++++++++++++++++++++++++++++
1838 |
1839 | char *QI_queries_to_string returns a list of descriptions for queries
1840 | that will be performed (debugging only).
1841 | Allocated text, must be freed after use.
1842 |
1843 | Query_instructions *qis query instructions structure
1844 |
1845 | Author:
1846 | marek.
1847 | ++++++++++++++++++++++++++++++++++++++*/
1848 |
1849 | char *QI_queries_to_string(Query_instructions *qis)
1850 | {
1851 | Query_instruction *qi;
1852 | int i;
1853 | char *resstr = NULL;
1854 |
1855 | dieif( wr_realloc((void **)&resstr, 2 ) != UT_OK);
1856 | strcpy(resstr, "{");
1857 |
1858 | for( i = 0; ( qi=qis->instruction[i] ) != NULL; i++ ) {
1859 | char *descr = Query[qi->queryindex].descr;
1860 | int oldres = strlen( resstr );
1861 |
1862 | dieif( wr_realloc((void **)&resstr, oldres+strlen(descr)+2) != UT_OK);
1863 | strcat(resstr, descr);
1864 | strcat(resstr, ",");
1865 | }
1866 | if( i>0 ) {
1867 | /* cancel the last comma */
1868 | resstr[strlen(resstr)-1] = 0;
1869 | }
1870 |
1871 | dieif( wr_realloc((void **)&resstr, strlen( resstr ) + 2 )
1872 | != UT_OK);
1873 | strcat(resstr, "}");
1874 |
1875 | return resstr;
1876 | }