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