1    | /***************************************
2    |   $Revision: 1.96 $
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,2000,2001,2002               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 "rip.h"
35   | 
36   | #include <stdio.h>
37   | #include <string.h>
38   | #include <glib.h>
39   | 
40   | /* maximum number of names to query for */
41   | /*   MySQL only allows 31 tables in a single query, so this limits us */
42   | /*   As of 2002-03-04 the most names in a person object are 14 */
43   | #define MAX_NAMES 30
44   | 
45   | /* the TEMPORARY table is only available in 3.23 or later MySQL */
46   | #if MYSQL_VERSION_ID >= 32300
47   | #define USE_TEMPORARY_TABLE 
48   | #endif
49   | 
50   | /* string to stick in CREATE TABLE statments */
51   | #ifdef USE_TEMPORARY_TABLE
52   | #define TEMPORARY "TEMPORARY"
53   | #else
54   | #define TEMPORARY ""
55   | #endif
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(GString *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  |   if (words[0] != NULL) {
213  |     g_string_sprintfa(from_clause, "names N%.2d", 0);
214  |     g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]);
215  | 
216  |     for (i=1; (i < MAX_NAMES) && (words[i] != NULL); i++) {
217  |       g_string_sprintfa(from_clause, ", names N%.2d", i);
218  |       g_string_sprintfa(where_clause, 
219  |           " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id", 
220  |           i, words[i], i);
221  |     }
222  |   }
223  | 
224  |   g_string_sprintf(query_str, sql_query, from_clause->str, where_clause->str);
225  | 
226  |   /* Free up stuff */
227  |   g_strfreev(words);
228  |   g_string_free(where_clause,/* CONSTCOND */ TRUE);
229  |   g_string_free(from_clause, /* CONSTCOND */ TRUE);
230  | 
231  | } /* create_name_query() */
232  | 
233  | 
234  | /*++++++++++++++++++++++++++++++++++++++
235  |   construct a range query for the as_block table
236  |   (a query for an AS block object) given a string like: 
237  |   AS1
238  |   AS1 - AS10
239  |   AS1-AS10
240  | 
241  |   int create_asblock_query    Returns 0 on success, -1 on failure 
242  |                              (search term not an AS# nor range)
243  | 
244  |   char *query_str             buffer for the final query (must be big enough)
245  | 
246  |   const char *sql_query       rest of the sql query (with %d %d formats for
247  |                               AS numbers)
248  | 
249  |   const char *keys            user-supplied search term.
250  | 
251  |   Author:
252  |     marek
253  |   ++++++++++++++++++++++++++++++++++++++*/
254  | static int create_asblock_query(GString *query_str, 
255  | 				const char *sql_query, 
256  | 				const char *keys) {
257  |   char *keycopy = wr_string(keys);
258  |   char *token, *cursor = keycopy;
259  |   int  asnums[2] = {0,0};
260  |   int index = 0; /* index into the asnums array */
261  | 
262  | 
263  |   while( (token = strsep( &cursor, "-" )) != NULL && index < 2) {  
264  |     /* discard the letters (or leading whitespace), take the number */
265  |     if( sscanf(token, "%*[ AS]%d", &asnums[index++]) < 1 ) {
266  |       return -1; /* error */
267  |     }
268  |   }
269  |   /* if only beginning was supplied, copy it as end */
270  |   if( index == 1 ) {
271  |     asnums[1] = asnums[0];
272  |   }
273  |   
274  |   /* now construct the query */
275  |   g_string_sprintf(query_str, sql_query, asnums[0], asnums[1]);
276  | 
277  |   UT_free(keycopy);
278  |   return 0;
279  | }
280  | 
281  | 
282  | /*++++++++++++++++++++++++++++++++++++++
283  |   add_filter(): construct a query to limit the objects returned from the last
284  |   table to predefined types. 
285  | 
286  |   char *query_str           buffer for the final query, containing the initial
287  |                             part of the query (must be big enough)
288  | 
289  |   const Query_command *qc   query command structure with the bitmap of 
290  |                             object types to be included.
291  | 			    
292  |   Author:
293  |     ottrey.
294  |   ++++++++++++++++++++++++++++++++++++++*/
295  | static void add_filter(GString *query_str, const Query_command *qc) 
296  | {
297  |   unsigned i;
298  | /*  int qlen;*/
299  |   char filter_atom[STR_M];
300  | 
301  | #if 0
302  |   /* glib string manipulation - untested yet */
303  |   
304  |     if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) { 
305  |       g_string_sprintfa(query_str, " AND (");
306  |       for (i=0; i < C_END; i++) {
307  | 	if (MA_isset(qc->object_type_bitmap, i)) {
308  | 	  g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
309  | 	}
310  |       }
311  |       g_string_truncate(query_str, query_str->len-3);
312  |       g_string_append_c(query_str, ')');
313  |     }
314  | 
315  | #else /* classic string operations */
316  | 
317  |   /* add filters only if any bits are 0 (the number of 1's is < MAX_MAX */
318  |   if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) { 
319  |     g_string_append(query_str, " AND (");
320  |     for (i=0; i < C_END; i++) {
321  |       if (MA_isset(qc->object_type_bitmap, i)) {
322  |         strcpy(filter_atom, "");
323  |         sprintf(filter_atom, "i.object_type = %d OR ", i);
324  | 	                /* XXX class codes should be used instead:
325  | 			   DF_get_class_dbase_code(i)) 
326  | 			   but currently the tables contain values of enums
327  | 			   (C_IN, etc) and not codes
328  | 			*/
329  |         g_string_append(query_str, filter_atom);
330  |       }
331  |     }
332  |     dieif(query_str->len < 3);  /* this code can only be reached if there is
333  |                                    at least one object here, meaning this 
334  |                                    must end with the string "OR ", which we
335  |                                    then remove */
336  |     g_string_truncate(query_str, query_str->len - 3);
337  |     g_string_append_c(query_str, ')');
338  | /*    qlen = strlen(query_str);
339  |     query_str[qlen-3] = ')';
340  |     query_str[qlen-2] = '\0';
341  |     query_str[qlen-1] = '\0';*/
342  |   }
343  | 
344  | #endif
345  |   
346  | } /* add_filter() */
347  | 
348  | /* create_query() */
349  | /*++++++++++++++++++++++++++++++++++++++
350  |   Create an sql query from the query_command and the matching keytype and the
351  |   selected inverse attributes.
352  |   Note this clears the first inv_attribute it sees, so is called sequentially
353  |   until there are no inv_attributes left.
354  | 
355  |   WK_Type keytype The matching keytype.
356  | 
357  |   const Query_command *qc The query command.
358  | 
359  |   mask_t *inv_attrs_bitmap The selected inverse attributes.
360  |    
361  |   More:
362  |   +html+ <PRE>
363  |   Authors:
364  |         ottrey
365  |   +html+ </PRE>
366  | 
367  |   ++++++++++++++++++++++++++++++++++++++*/
368  | static char *create_query(const Query_t q, const Query_command *qc) 
369  | {
370  |   GString *result_buff;
371  |   char *result;
372  |   Q_Type_t querytype;
373  |   int addquery = 0; /* controls if the query should be added to the list */
374  | 
375  |   result_buff = g_string_sized_new(STR_XL);
376  | 
377  |   if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
378  |     querytype = Q_INVERSE;
379  |   }
380  |   else {
381  |     querytype = Q_LOOKUP;
382  |   }
383  | 
384  |   if ( (q.query != NULL) 
385  |     && (q.querytype == querytype) ) {
386  |     
387  |     /* addquery = 1; */
388  |     /* if it got here, it should be added, unless.(see asblock)*/
389  |     
390  |     if (q.keytype == WK_NAME) { 
391  |       /* Name queries require special treatment. */
392  |       create_name_query(result_buff, q.query, qc->keys);
393  |       addquery = 1;
394  |     }
395  |     else if( q.keytype == WK_IPADDRESS ) {  /* ifaddr sql lookups */
396  |       ip_range_t myrang;
397  |       unsigned   begin, end;
398  |       ip_keytype_t key_type;
399  |       
400  |       /* The only inverse query for IPADDRESS is nserver. */
401  |       /* We need to insure that we don't try to use the numeric values for this
402  |        * query, because the address of the server is stored as a string, and
403  |        * the SQL query is formatted appropriately. */
404  |       if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) {
405  |         if(IP_rang_b2_space(&myrang) == IP_V4 ) {
406  | 	  IP_rang_b2v4(&myrang, &begin, &end);
407  | 	  if (querytype == Q_INVERSE) {
408  | 	      /* for inverse queries, convert number to dotted-quad */
409  | 	      char buf[64];
410  | 	      const char *inet_ntop_ret;
411  | 	      inet_ntop_ret = inet_ntop(AF_INET, &begin, buf, sizeof(buf));
412  | 	      dieif(inet_ntop_ret == NULL);
413  | 	      g_string_sprintf(result_buff, q.query, buf);
414  | 	  } else {
415  | 	      /* otherwise, execute appropriate query on numeric values */
416  | 	      g_string_sprintf(result_buff, q.query, begin, end);
417  | 	  }
418  | 	  addquery = 1;
419  | 	}
420  | 	else {
421  | 	  die;
422  | 	}
423  |       }
424  |     }
425  |     else if( q.keytype == WK_ASRANGE ) {   /* as_block range composition */
426  |       if( create_asblock_query(result_buff, q.query, qc->keys) != 0 ) {
427  | 	addquery = 0; /* ... unless it's not correct */
428  |       }
429  |       else {
430  | 	addquery = 1;
431  |       }
432  |     }
433  |     else {
434  |       g_string_sprintf(result_buff, q.query, qc->keys);
435  |       addquery = 1;
436  |     }
437  | 
438  |     if (q.class == C_ANY && addquery == 1 ) {
439  |       /* It is class type ANY so add the object filtering */
440  |       add_filter(result_buff, qc);
441  |     }
442  |   }
443  |   
444  |   if( addquery == 1 ) {
445  |     result = UT_strdup(result_buff->str);
446  |   } else {
447  |     result = NULL;
448  |   }
449  |   g_string_free(result_buff, TRUE);
450  | 
451  |   return result;
452  | } /* create_query() */
453  | 
454  | /* QI_fast_output() */
455  | /*++++++++++++++++++++++++++++++++++++++
456  |   This is for the '-F' flag.
457  |   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
458  |   Fast isn't fast anymore - it's just there for compatibility reasons.
459  | 
460  |   const char *str        The object to be "fast output'ed".
461  |    
462  |   More:
463  |   +html+ <PRE>
464  |   Authors:
465  |         ottrey,
466  | 	marek - glib strings + small changes
467  |   +html+ </PRE>
468  |   ++++++++++++++++++++++++++++++++++++++*/
469  | char *QI_fast_output(const char *str) 
470  | {
471  |   int i,j;
472  |   char *result;
473  |   GString *result_buff = g_string_sized_new(STR_XL);
474  |   gchar **lines = g_strsplit(str, "\n", 0);
475  |   unsigned char *value, *colon;
476  |   char *attr;
477  | 
478  |   g_string_assign(result_buff, "");
479  |   
480  |   for (j=0; lines[j] != NULL; j++) {
481  | 
482  |     switch (lines[j][0]) {
483  |       /* line continuation */
484  |     case ' ':
485  |     case '\t':
486  |     case '+':
487  |       value = (unsigned char *) lines[j]+1;
488  |       while(*value != '\0' && isspace(*value)) {
489  | 	value++;
490  |       }      
491  |       g_string_append(result_buff, "\n+ ");
492  |       g_string_append(result_buff, (char *)value);
493  |       break;
494  |       
495  |     default:
496  |       /* a line of the form "attribute: value" */
497  |       /* first: close the last line (if there was any, i.e. j>0) */
498  |       if( j > 0 ) { 
499  | 	g_string_append_c(result_buff, '\n');
500  |       }
501  |       
502  |       /* get attribute name */
503  |       attr =  lines[j];
504  |       colon = (unsigned char *) strchr(lines[j], ':');
505  |       /* if there's no colon for whatever reason, dump the object
506  | 	 and report the condition */
507  |       if( colon == NULL ) {
508  | 	ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
509  | 	goto fast_output_cleanup;
510  |       }
511  |       *colon = '\0';
512  |       for(value = colon+1; *value != '\0' && isspace(*value) ; value++) {
513  | 	;
514  |       }
515  | 
516  |       if( (i = DF_attribute_name2type(attr)) == -1 ) {
517  | 	  /* warning! error in the object format */
518  | 	ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
519  | 	goto fast_output_cleanup;
520  | 		
521  |       }
522  |       else {
523  | 	/* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
524  | 	g_string_append_c(result_buff, '*');
525  | 	g_string_append(result_buff, DF_get_attribute_code(i));
526  | 	g_string_append(result_buff, ": ");
527  | 	g_string_append(result_buff, (char *)value);
528  |       }
529  |     } /* switch */  
530  |   } /* for every line */ 
531  | 
532  |  fast_output_cleanup:
533  | 
534  |   g_strfreev(lines);
535  |   
536  |   g_string_append_c(result_buff, '\n');
537  |   result = UT_strdup(result_buff->str);
538  |   dieif(result == NULL);
539  |   
540  |   g_string_free(result_buff,/* CONSTCOND */ TRUE);
541  |   
542  |   return result;
543  | } /* fast_output() */
544  | 
545  | /* filter() */
546  | /*++++++++++++++++++++++++++++++++++++++
547  |   Basically it's for the '-K' flag for non-set (and non-radix) objects.
548  |   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
549  | 
550  |   This could be speed up if there were breaks out of the loops, once it matched something.
551  | 
552  |   const char *string          The string to be filtered.
553  |    
554  |   More:
555  |   +html+ <PRE>
556  |   Authors:
557  |         ottrey
558  |   +html+ </PRE>
559  | 
560  |   ++++++++++++++++++++++++++++++++++++++*/
561  | char *filter(const char *str) {
562  |   int i,j, passed=0;
563  |   char *result;
564  |   GString *result_buff = g_string_sized_new(STR_XL);
565  |   gchar **lines = g_strsplit(str, "\n", 0);
566  |   char * const *filter_names;
567  |   gboolean filtering_an_attribute = FALSE;
568  |   
569  |   filter_names = DF_get_filter_names();
570  | 
571  |   g_string_assign(result_buff, "");
572  |   
573  |   for (i=0; filter_names[i] != NULL; i++) {
574  |     for (j=0; lines[j] != NULL; j++) {
575  |       /* match lines that start with "key-field:" */
576  |       int filter_name_len = strlen(filter_names[i]);
577  |       if ((strncmp(filter_names[i], lines[j], filter_name_len) == 0) &&
578  |           (lines[j][filter_name_len] == ':'))
579  |       {
580  | 
581  |         g_string_sprintfa(result_buff, "%s\n", lines[j]);
582  | 	passed++;
583  | 	
584  | 	/* CONSTCOND */
585  |         filtering_an_attribute = TRUE;
586  |       }
587  |       /* CONSTCOND */
588  |       else if (filtering_an_attribute == TRUE) {
589  |         switch (lines[j][0]) {
590  |           case ' ':
591  |           case '\t':
592  |           case '+':
593  | 
594  |             g_string_sprintfa(result_buff, "%s\n", lines[j]);
595  |             
596  |           break;
597  | 
598  |           default:
599  |             filtering_an_attribute = FALSE;
600  |         }
601  |       }
602  |     }
603  |   }
604  | 
605  |   g_strfreev(lines);
606  | 
607  |   if(passed) {
608  |     g_string_append(result_buff, "\n");
609  |   }
610  |   result = UT_strdup(result_buff->str);
611  |   g_string_free(result_buff,/* CONSTCOND */ TRUE);
612  | 
613  |   return result;
614  | } /* filter() */
615  | 
616  | /* write_results() */
617  | /*++++++++++++++++++++++++++++++++++++++
618  |   Write the results to the client socket.
619  | 
620  |   SQ_result_set_t *result The result set returned from the sql query.
621  |   unsigned filtered       if the objects should go through a filter (-K)
622  |   sk_conn_st *condat      Connection data for the client    
623  | 
624  |   More:
625  |   +html+ <PRE>
626  |   Authors:
627  |         ottrey - initial design
628  | 	marek - rewritten for accounting and cancellation.
629  |   +html+ </PRE>
630  | 
631  |   ++++++++++++++++++++++++++++++++++++++*/
632  | static int write_results(SQ_result_set_t *result, 
633  | 			 unsigned filtered,
634  | 			 unsigned fast,
635  | 			 sk_conn_st *condat,
636  | 			 acc_st    *acc_credit,
637  | 			 acl_st    *acl
638  | 			 ) {
639  |   SQ_row_t *row;
640  |   char *str;
641  |   char *filtrate;
642  |   char *fasted;
643  |   int retrieved_objects=0;
644  |   char *objt;
645  |   int type;
646  | 
647  |   /* Get all the results - one at a time */
648  |   if (result != NULL) {
649  |     /* here we are making use of the mysql_store_result capability
650  |        of interrupting the cycle of reading rows. mysql_use_result
651  |        would not allow that, would have to be read until end */
652  |     
653  |     while ( condat->rtc == 0 
654  | 	    && AC_credit_isdenied( acc_credit ) == 0
655  | 	    && (row = SQ_row_next(result)) != NULL ) {
656  |       
657  |       dieif (  (str = SQ_get_column_string(result, row, 0)) == NULL )
658  |       dieif (  (objt = SQ_get_column_string(result, row, 3)) == NULL );
659  | 
660  |       /* get + add object type */
661  |       type = atoi(objt);
662  | 	
663  |       /* ASP_QI_LAST_DET */
664  |       ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
665  | 	        "Retrieved serial id = %d , type = %s", atoi(str), objt);
666  | 	
667  |       UT_free(str);
668  |       UT_free(objt);
669  |       
670  |       /* decrement credit for accounting purposes */
671  |       AC_count_object( acc_credit, acl, 
672  | 		       type == C_PN || type == C_RO ); /* is private? */
673  | 
674  |       /* break the loop if the credit has just been exceeded and 
675  | 	 further results denied */
676  |       if( AC_credit_isdenied( acc_credit ) ) {
677  | 	continue; 
678  |       }
679  |       
680  |       if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; } 
681  |       else {
682  | 	
683  |         /* The fast output stage */
684  |         if (fast == 1) {
685  |           fasted = QI_fast_output(str);
686  |           UT_free(str);
687  |           str = fasted;
688  |         }
689  | 	
690  |         /* The filtering stage */
691  |         if (filtered == 0) {
692  |           SK_cd_puts(condat, str);
693  | 	  SK_cd_puts(condat, "\n");
694  |         }
695  |         else { 
696  | 	  
697  | 	  /* XXX accounting should be done AFTER filtering, not to count
698  | 	     objects filtered out */
699  | 
700  |           filtrate = filter(str);
701  |           SK_cd_puts(condat, filtrate);
702  |           UT_free(filtrate);
703  |         }
704  |         retrieved_objects++;
705  |       }
706  |       UT_free(str);
707  |     }
708  |   }
709  |   
710  |   return retrieved_objects;
711  | } /* write_results() */
712  | 
713  | /* generic SQL error message - it could be a configurable parameter, but
714  |    it also shouldn't happen! */
715  | static const char *sql_error_text = 
716  |     "% An internal database error has occurred.\n"  
717  |     "% It has been logged, and an adminstrator should look at it shorly.\n"
718  |     "% Please try your query again.\n"
719  |     "\n"
720  |     "\n";
721  | 
722  | /* use a macro so we can get our file and line number */
723  | #define report_sql_error(condat,sql_connection,sql_command) \
724  |   __report_sql_error((condat), (sql_connection), (sql_command), \
725  |                      __FILE__,__LINE__,pthread_self())
726  | 
727  | /* report_sql_error() */
728  | /*++++++++++++++++++++++++++++++++++++++
729  | 
730  |   sk_conn_st *condat                 connection with user
731  |   SQ_connection_t *sql_connection    connection to MySQL
732  |   const char *sql_command            SQL command sent to MySQL
733  | 
734  |   ++++++++++++++++++++++++++++++++++++++*/
735  | 
736  | void
737  | __report_sql_error(sk_conn_st *condat, 
738  |                  SQ_connection_t *sql_connection, 
739  | 		 const char *sql_command,
740  | 		 const char *file,
741  | 		 int line,
742  | 		 pthread_t tid)
743  | {
744  |     /* first, let user know what has happened */
745  |     SK_cd_puts(condat, sql_error_text);
746  | 
747  |     /* next, log this error */
748  |     ER_perror(FAC_QI, QI_SQLERR, "at %s:%d, tid:%lu [%d] %s sql='%s'", 
749  | 		      file, line, (unsigned long)tid,
750  | 		      SQ_errno(sql_connection), 
751  | 		      SQ_error(sql_connection),
752  | 		      sql_command);
753  | }
754  | 
755  | /* write_objects() */
756  | /*++++++++++++++++++++++++++++++++++++++
757  |   
758  |   SQ_connection_t *sql_connection The connection to the database.
759  | 
760  |   char *id_table The id of the temporary table (This is a result of the hacky
761  |                   way we've tried to get MySQL to do sub-selects.)
762  | 
763  |   sk_conn_st *condat  Connection data for the client
764  | 
765  |   More:
766  |   +html+ <PRE>
767  |   Authors:
768  |         ottrey,
769  | 	marek.
770  |   +html+ </PRE>
771  |   ++++++++++++++++++++++++++++++++++++++*/
772  | static int 
773  | write_objects(SQ_connection_t **sql_connection, 
774  | 			  char *id_table, 
775  | 			  unsigned int filtered, 
776  | 			  unsigned int fast, 
777  | 			  sk_conn_st *condat,
778  | 			  acc_st    *acc_credit,
779  | 			  acl_st    *acl
780  | 			  ) 
781  | {
782  |   SQ_result_set_t *result = NULL;
783  |   int retrieved_objects=0;
784  |   char sql_command[STR_XL];  
785  | 
786  |   sprintf(sql_command, Q_OBJECTS, id_table);
787  | 
788  |   if (sql_execute_watched(condat, sql_connection, sql_command, &result) == -1) {
789  |       report_sql_error(condat, *sql_connection, sql_command);
790  |       return SQ_errno(*sql_connection);
791  |   }
792  |   
793  |   /* Problem: if the query was aborted, the result structure does not
794  |      refer to any existing connection anymore. So we check rtc here.
795  |   */
796  |   
797  |   if( condat->rtc == 0) {
798  |     retrieved_objects = write_results(result, filtered, fast, condat, 
799  | 				      acc_credit, acl);
800  |     SQ_free_result(result); 
801  |   }
802  |   return 0;
803  | } /* write_objects() */
804  | 
805  | /* rx_node_has_mnt_irt() */
806  | /*++++++++++++++++++++++++++++++++++++++
807  |   See if the node has an "mnt-irt:" attribute.
808  | 
809  |   condat           connection data for the client
810  |   sql_connection   connection to the database
811  |   rx_data          data node returned from the RX module
812  |   ++++++++++++++++++++++++++++++++++++++*/
813  | gboolean
814  | rx_node_has_mnt_irt (sk_conn_st *condat, 
815  |                      SQ_connection_t *sql_connection, 
816  |                      int object_id)
817  | {
818  |     GString *sql_command;
819  |     SQ_result_set_t *result_ptr;
820  |     int sql_error;
821  |     SQ_row_t *row;
822  |     long num_irt_ref;
823  | 
824  |     /* query database for references from this object to an IRT */
825  |     sql_command = g_string_new("");
826  |     g_string_sprintf(sql_command, 
827  |                      "SELECT COUNT(*) FROM mnt_irt WHERE object_id=%d",
828  |                      object_id);
829  |     if (SQ_execute_query(sql_connection, sql_command->str, &result_ptr) == -1) {
830  |         sql_error = SQ_errno(sql_connection);
831  |         report_sql_error(condat, sql_connection, sql_command->str);
832  |         g_string_free(sql_command, TRUE);
833  |         return FALSE;
834  |     }
835  |     g_string_free(sql_command, TRUE);
836  | 
837  |     /* get number of references */
838  |     row = SQ_row_next(result_ptr);
839  |     if (row == NULL) {
840  |         /* if the object is deleted after we have gotten a reference to it,
841  |            the lookup will fail - in such a case treat it the same as 
842  |            an object without any mnt_irt attributes */
843  |         num_irt_ref = 0;
844  |     } else {
845  |         if (SQ_get_column_int(result_ptr, row, 0, &num_irt_ref) != 0) {
846  |             ER_perror(FAC_QI, QI_SQLERR, "bad column at %s:%d", 
847  |                       __FILE__, __LINE__);
848  |             num_irt_ref = 0;
849  |         }
850  |     }
851  |     SQ_free_result(result_ptr);
852  | 
853  |     /* return appropriate result */
854  |     if (num_irt_ref > 0) {
855  |         return TRUE;
856  |     } else {
857  |         return FALSE;
858  |     }
859  | }
860  | 
861  | 
862  | /* mnt_irt_filter() */
863  | /*++++++++++++++++++++++++++++++++++++++
864  |   Find the most specific entry in the list of network nodes passed
865  |   and remove all entries but that one from the list.
866  | 
867  |   condat           connection data for the client
868  |   sql_connection   connection to the database
869  |   datlist          list of data from the radix tree (nodes of *rx_datcpy_t)
870  | 
871  |   Note:  The "-c" query flag specifies that the most specific inetnum or
872  |          inet6num with an "mnt-irt:" attribute should be returned.  
873  |          
874  |          To do this, we get a list of encompassing networks by sending the 
875  |          same options to the RP/RX module that the '-L' query does.  Then
876  |          we call this function, mnt_irt_filter(), and search the list for 
877  |          the first entry that has an "mnt-irt:" attribute, by looking in 
878  |          the "mnt_irt" table in MySQL for a reference (see the 
879  |          rx_node_has_mnt_irt() for details).
880  | 
881  |          If a reference is found, the list is replaced with a list 
882  |          containing the single entry.  If no reference is found, the list
883  |          is deleted.
884  |   ++++++++++++++++++++++++++++++++++++++*/
885  | void 
886  | mnt_irt_filter (sk_conn_st *condat, 
887  |                 SQ_connection_t *sql_connection, 
888  |                 GList **datlist)
889  | {
890  |     GList *p;
891  |     GList *new_datlist;
892  |     rx_datcpy_t *rx_data;
893  |     int object_id;
894  | 
895  |     /* empty datlist */
896  |     new_datlist = NULL;
897  | 
898  |     /* search for node with "mnt-irt:" attribute */
899  |     for (p=g_list_last(*datlist); p != NULL; p=g_list_previous(p)) {
900  |         /* grab the data for this node */
901  |         rx_data = (rx_datcpy_t *)p->data;
902  |         object_id = rx_data->leafcpy.data_key;
903  | 
904  |         /* see if this is the node we are looking for */
905  |         if (rx_node_has_mnt_irt(condat, sql_connection, object_id)) {
906  |             /* use this entry, and remove from old list */
907  |             new_datlist = g_list_append(NULL, rx_data);
908  |             *datlist = g_list_remove_link(*datlist, p);
909  |             g_list_free_1(p);
910  |             break;
911  |         } 
912  |     }
913  | 
914  |     /* free our old datlist */
915  |     for (p=*datlist; p != NULL; p = g_list_next(p)) {
916  |         rx_data = (rx_datcpy_t *)p->data;
917  |         UT_free(rx_data->leafcpy.data_ptr);
918  |         p = g_list_next(p);
919  |     }
920  |     wr_clear_list(datlist);
921  | 
922  |     /* use our new datlist */
923  |     *datlist = new_datlist;
924  | }
925  | 
926  | 
927  | /* insert_radix_serials() */
928  | /*++++++++++++++++++++++++++++++++++++++
929  |   Insert the radix serial numbers into a temporary table in the database.
930  | 
931  |   mask_t bitmap The bitmap of attribute to be converted.
932  |    
933  |   SQ_connection_t *sql_connection The connection to the database.
934  | 
935  |   char *id_table The id of the temporary table (This is a result of the hacky
936  |                   way we've tried to get MySQL to do sub-selects.)
937  |   
938  |   GList *datlist The list of data from the radix tree.
939  | 
940  |   More:
941  |   +html+ <PRE>
942  |   Authors:
943  |         ottrey,
944  | 	marek
945  |   +html+ </PRE>
946  | 
947  |   ++++++++++++++++++++++++++++++++++++++*/
948  | static int insert_radix_serials(sk_conn_st *condat,
949  | 				 SQ_connection_t *sql_connection, 
950  | 				 char *id_table, GList *datlist) {
951  |   GList *qitem;
952  |   GString *sql_command;
953  |   int serial;
954  |   int sql_error;
955  | 
956  |   sql_error = 0;
957  |   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
958  |     rx_datcpy_t *datcpy = qitem->data;
959  | 
960  |     serial = datcpy->leafcpy.data_key;
961  | 
962  |     /* don't bother to insert values into our temporary table */
963  |     /* if we've lost the client connection */
964  |     if ((condat->rtc == 0) && !sql_error) {
965  |         sql_command = g_string_sized_new(STR_S);
966  |         g_string_sprintf(sql_command, 
967  |                          "INSERT INTO %s values (%d)", id_table, serial);
968  |         if (SQ_execute_query(sql_connection, sql_command->str, NULL) == -1) {
969  | 	    sql_error = SQ_errno(sql_connection);
970  | 	    report_sql_error(condat, sql_connection, sql_command->str);
971  | 	}
972  |         g_string_free(sql_command, TRUE);
973  |     }
974  | 
975  |     UT_free(datcpy->leafcpy.data_ptr);
976  |   }
977  | 
978  |   wr_clear_list( &datlist );
979  | 
980  |   /* return error, if any */
981  |   return sql_error;
982  | 
983  | } /* insert_radix_serials() */
984  | 
985  | 
986  | /* write_radix_immediate() */
987  | /*++++++++++++++++++++++++++++++++++++++
988  |   Display the immediate data carried with the objects returned by the
989  |   radix tree.
990  | 
991  |   GList *datlist      The linked list of dataleaf copies
992  | 
993  |   sk_conn_st *condat  Connection data for the client
994  | 
995  |   acc_st  *acc_credit Accounting struct
996  | 
997  | More:
998  |   +html+ <PRE>
999  |   Authors:
1000 |         marek
1001 |   +html+ </PRE>
1002 | 
1003 |   Also free the list of answers.
1004 | ++++++++++++++++++++++++++++++++++++++*/
1005 | static void write_radix_immediate(GList *datlist, 
1006 | 				  sk_conn_st *condat,
1007 | 				  acc_st    *acc_credit,
1008 | 				  acl_st    *acl) 
1009 | {
1010 |   GList    *qitem;
1011 |   
1012 |   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
1013 |     rx_datcpy_t *datcpy = qitem->data;
1014 | 
1015 |     SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
1016 |     SK_cd_puts(condat, "\n");
1017 |     
1018 |     UT_free(datcpy->leafcpy.data_ptr);
1019 |     
1020 |     AC_count_object(acc_credit, acl, 0 /* public object (private=0) */ );
1021 | 
1022 |     if(condat->rtc != 0) {
1023 |       break;
1024 |     }
1025 |   }
1026 |   
1027 |   wr_clear_list( &datlist );
1028 | } /* write_radix_immediate() */
1029 | 
1030 | 
1031 | /* map_qc2rx() */
1032 | /*++++++++++++++++++++++++++++++++++++++
1033 |   The mapping between a query_command and a radix query.
1034 | 
1035 |   Query_instruction *qi The Query Instruction to be created from the mapping
1036 |                         of the query command.
1037 | 
1038 |   const Query_command *qc The query command to be mapped.
1039 | 
1040 |   More:
1041 |   +html+ <PRE>
1042 |   Authors:
1043 |         ottrey,
1044 | 	marek - simplified the logic, added stealth -S option
1045 |   +html+ </PRE>
1046 | 
1047 |   ++++++++++++++++++++++++++++++++++++++*/
1048 | static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
1049 |   int result=1;
1050 |   int allflags = (qc->L == 1) + (qc->M == 1) + (qc->l == 1) 
1051 |                + (qc->m == 1) + (qc->x == 1);
1052 | 
1053 |   allflags += qc->c_irt_search;
1054 | 
1055 |   qi->rx_keys = qc->keys;
1056 | 
1057 |   /* only one option can be active at a time */
1058 | 
1059 |   if( allflags > 1 ) {
1060 |       /* user error  (this should have been checked before) */
1061 |       
1062 |       ER_dbg_va(FAC_QI, ASP_QI_SKIP, 
1063 | 		"ERROR in qc2rx mapping: bad combination of flags");
1064 |       result = 0;
1065 |   }
1066 |   if( allflags == 0 ) { 
1067 |       /* no options active - default search */
1068 |       qi->rx_srch_mode = RX_SRCH_EXLESS;
1069 |       qi->rx_par_a = 0;
1070 |   }
1071 |   else if (qc->c_irt_search) {
1072 |       qi->rx_srch_mode = RX_SRCH_LESS;
1073 |       qi->rx_par_a = RX_ALL_DEPTHS;
1074 |   }
1075 |   else if ( qc->L == 1 ) {
1076 |       qi->rx_srch_mode = RX_SRCH_LESS;
1077 |       qi->rx_par_a = RX_ALL_DEPTHS;
1078 |   }
1079 |   else if (qc->M == 1) {
1080 |       qi->rx_srch_mode = RX_SRCH_MORE;
1081 |       qi->rx_par_a = RX_ALL_DEPTHS;
1082 |   }
1083 |   else if (qc->l == 1) {
1084 |       qi->rx_srch_mode = RX_SRCH_LESS;
1085 |       qi->rx_par_a = 1;
1086 |   }
1087 |   else if (qc->m == 1) {
1088 |       qi->rx_srch_mode = RX_SRCH_MORE;
1089 |       qi->rx_par_a = 1;
1090 |   }
1091 |   else if (qc->x == 1) {
1092 |       qi->rx_srch_mode = RX_SRCH_EXACT;
1093 |       qi->rx_par_a = 0;
1094 |   }
1095 |   
1096 |   if( qi->rx_srch_mode == RX_SRCH_MORE && (qc->S == 1) ) {
1097 |       qi->rx_srch_mode = RX_SRCH_DBLS;
1098 |   }
1099 |   
1100 |   return result;
1101 |   
1102 | } /* map_qc2rx() */
1103 | 
1104 | 
1105 | /* run_referral() */
1106 | /*++++++++++++++++++++++++++++++++++++++
1107 |   
1108 |    invoked when no such domain found. Goes through the domain table
1109 |    and searches for shorter domains, then if it finds one with referral 
1110 |    it performs it, otherwise it just returns nothing.
1111 | 
1112 |    to perform referral, it actually composes the referral query 
1113 |    for a given host/port/type and calls the whois query function.
1114 | 
1115 |    Well, it returns nothing anyway (void). It just prints to the socket.
1116 | 
1117 |   char *ref_host           referral server host name
1118 | 
1119 |   unsigned ref_port_int    referral server port number
1120 | 
1121 |   char *qry                query to be run
1122 | 
1123 |   Author:
1124 |     marek
1125 |   ++++++++++++++++++++++++++++++++++++++*/
1126 | void run_referral(Query_environ *qe, 
1127 | 		  char *ref_host,
1128 | 		  unsigned ref_port_int,
1129 | 		  char *qry)
1130 | {
1131 |   
1132 | #if 1 /* switch off for testing */
1133 |   er_ret_t err;
1134 |   char *rep;
1135 | 
1136 |         /* WH_sock(sock, host, port, query, maxlines, timeout)) */
1137 |   err= WH_cd_sock(&(qe->condat), ref_host, ref_port_int, qry, 
1138 | 		  ca_get_referralmaxlines, ca_get_referraltimeout
1139 | 		  );
1140 | 
1141 |   switch( err ) {
1142 |   case SK_OK:
1143 |     /* OK */
1144 |     break;
1145 |   case SK_TIMEOUT:
1146 |     /* Referral timeout */
1147 |     rep = ca_get_qi_ref_tmout ;
1148 |     SK_cd_puts(&(qe->condat), rep);
1149 |     UT_free(rep);
1150 |     break;
1151 | 	    
1152 |   case SK_BADHOST:
1153 |     /* Referral host not found */
1154 |     rep = ca_get_qi_ref_badhost ;
1155 |     SK_cd_puts(&(qe->condat), rep);
1156 |     UT_free(rep);
1157 |     break;
1158 | 
1159 |   case SK_CONNECT:
1160 |     /* Referral host not responding */
1161 |     rep = ca_get_qi_ref_hostnottresp ;
1162 |     SK_cd_puts(&(qe->condat), rep);
1163 |     UT_free(rep);
1164 |     break;
1165 | 
1166 |   case SK_BIND:
1167 |   case SK_SOCKET:
1168 |     /* XXX internal server problem...  */
1169 |     die; 
1170 | 
1171 |   case WH_MAXLINES:
1172 |     /* Referral reply line limit exceeded */
1173 |     rep = ca_get_qi_ref_overmaxlin ;
1174 |     SK_cd_puts(&(qe->condat), rep);
1175 |     UT_free(rep);
1176 |     break;
1177 |     
1178 |   default: /* any other errors ? */
1179 |     die;
1180 |     ;
1181 |   } /*switch WH_sock */
1182 | #endif
1183 |   
1184 | }/*run_referral*/
1185 | 
1186 | 
1187 | 
1188 | 
1189 | 
1190 | /*++++++++++++++++++++++++++++++++++++++
1191 |    
1192 |    prepare and run the referral, displaying the results directly to the
1193 |    client's connection. 
1194 |    
1195 |    XXX still missing protection against a referral loop
1196 |    XXX handling inverse flag not needed, to be removed
1197 | 
1198 |   char *domain               domain being looked up
1199 |  
1200 |   Query_instructions *qis    original query instructions structure
1201 | 
1202 |   Query_environ *qe          original query environment structure
1203 | 
1204 |   Query_instruction *qi      specific query instruction triggered
1205 | 
1206 |   SQ_result_set_t *result    result of the lookup containing referral details
1207 | 
1208 |   SQ_row_t *row              first row (should be only 1) of the result
1209 |                              this should contain columns: type, port, host
1210 | 
1211 |   char *sourcename           name of the database "source"
1212 | 
1213 |   Author: 
1214 |      marek
1215 |   ++++++++++++++++++++++++++++++++++++++*/
1216 | static
1217 | void qi_prep_run_refer(char *domain, 
1218 | 		       Query_instructions *qis,   
1219 | 		       Query_environ *qe, 
1220 | 		       Query_instruction *qi,
1221 | 		       SQ_result_set_t *result, SQ_row_t *row, 
1222 | 		       char *sourcename )
1223 | {
1224 |     int err;
1225 |     long ref_type;
1226 |     long ref_port;
1227 |     char *ref_host; 
1228 |     GString *querystr;
1229 | 
1230 |     /* get values from SQL query */
1231 |     err = SQ_get_column_int(result, row, 0, &ref_type);
1232 |     dieif(err);
1233 |     err = SQ_get_column_int(result, row, 1, &ref_port);
1234 |     dieif(err);
1235 |     ref_host = SQ_get_column_string(result, row, 2);
1236 |     dieif(ref_host == NULL);
1237 |       
1238 |     querystr = g_string_sized_new(STR_L);
1239 |       
1240 |     /* put -r if the reftype is RIPE and -r or -i were used */
1241 |     if( (ref_type == RF_RIPE) 
1242 | 	&& ( Query[qi->queryindex].querytype == Q_INVERSE       
1243 | 	     || qis->recursive > 0  )   ) 
1244 |     {
1245 |         g_string_append(querystr, "-r ");
1246 |     }
1247 |       
1248 |     /* prepend with -Vversion,IP for type CLIENTADDRESS */
1249 |     if( ref_type == RF_CLIENTADDRESS ) {
1250 |         g_string_sprintf(querystr, "-V%s,%s ", VERSION, qe->condat.ip);
1251 |     }
1252 | 
1253 |       
1254 |     /* now set the search term - set to the stripped down version 
1255 |        for inverse query, full-length otherwise */
1256 |     if( Query[qi->queryindex].querytype == Q_INVERSE ) {
1257 |         g_string_append(querystr, domain);
1258 |     }
1259 |     else {
1260 |         g_string_append(querystr, qis->qc->keys);
1261 |     }
1262 | 
1263 |     {
1264 | 	/* the object is not from %s, 
1265 | 	   it comes from %s %d, use -R to see %s */
1266 | 	char *rep = ca_get_qi_fmt_refheader ;
1267 | 	SK_cd_printf(&(qe->condat), rep, 
1268 | 		     sourcename, 
1269 | 		     ref_host, ref_port,
1270 | 		     sourcename );
1271 | 	UT_free(rep);
1272 |     }
1273 | 	    
1274 |     /* do the referral */
1275 |     ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host); 
1276 |       
1277 |     run_referral( qe, ref_host, ref_port, querystr->str);
1278 |       
1279 |     { /* End of referred query result */
1280 | 	char *rep = ca_get_qi_reftrailer ; 
1281 | 	SK_cd_puts(&(qe->condat), rep);
1282 | 	UT_free(rep);
1283 |     }
1284 |     SK_cd_puts(&(qe->condat), "\n");
1285 |     
1286 |     g_string_free(querystr, TRUE);
1287 |     UT_free(ref_host);
1288 | }
1289 | 
1290 | 
1291 | /*++++++++++++++++++++++++++++++++++++++
1292 |   
1293 |   specific case of the object ID collection: the domains.
1294 |   Checks to see if the domain exists, and runs the referral if it is defined
1295 |   and the domain is missing.
1296 | 
1297 |   Arguments:
1298 | 
1299 |   char *sourcename                     name of the database "source"
1300 | 
1301 |   SQ_connection_t *sql_connection      sql connection dedicated to this thread
1302 |   
1303 |   char *id_table                       name of the temporary table to be used
1304 |   
1305 |   char *sub_table                      name of the temporary subtable
1306 |   
1307 |   Query_instructions *qis    original query instructions structure
1308 | 
1309 |   Query_environ *qe          original query environment structure
1310 | 
1311 |   Query_instruction *qi      specific query instruction triggered
1312 | 
1313 |   acc_st *acc_credit         credit for this client 
1314 |  
1315 |   Author:
1316 |      marek.
1317 |   ++++++++++++++++++++++++++++++++++++++*/
1318 | 
1319 | static int
1320 | qi_collect_domain(char *sourcename,
1321 | 		  SQ_connection_t *sql_connection, 
1322 | 		  char *id_table,
1323 | 		  char *sub_table,
1324 | 		  Query_instructions *qis,   
1325 | 		  Query_environ *qe, 
1326 | 		  Query_instruction *qi,
1327 | 		  acc_st *acc_credit,
1328 | 		  int *sql_error)
1329 | {
1330 |   char *domain = qis->qc->keys;
1331 |   char *dot = domain;
1332 |   int subcount = 0;
1333 |   int foundcount = 0;
1334 |   GString *sql_command;
1335 | 
1336 |   /* we MUST NOT have a diconnection from the server here */
1337 |   dieif(qe->condat.rtc != 0);
1338 | 
1339 |   /* create a string for our queries */
1340 |   sql_command = g_string_sized_new(STR_XL);
1341 | 
1342 |   /* while nothing found and still some pieces of the name left */
1343 |   while( dot != NULL && subcount == 0 ) { 
1344 |     int refcount = 0;
1345 |     SQ_row_t *row;
1346 |     SQ_result_set_t *result_referrals = NULL;
1347 | 
1348 |     ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot);
1349 | 
1350 |     /* domain lookup -- query into the _S table */
1351 |     g_string_sprintf(sql_command, 
1352 |                      "INSERT INTO %s SELECT object_id "
1353 |                          "FROM domain "
1354 |                          "WHERE domain = '%s'",
1355 |                      sub_table, dot);
1356 |     if (SQ_execute_query(sql_connection, sql_command->str, NULL) != 0) {
1357 |         *sql_error = SQ_errno(sql_connection);
1358 | 	report_sql_error(&qe->condat, sql_connection, sql_command->str);
1359 |         foundcount = 0;
1360 |         goto exit_qi_collect_domain;
1361 |     }
1362 |     subcount = SQ_get_affected_rows(sql_connection); 
1363 | 
1364 |     if( subcount != 0 ) { /* domain exists in the database */
1365 | 
1366 | 	/* referral check. Always done except for -R and INVERSE queries */  
1367 | 	if( qis->qc->R == 0 && 
1368 | 	    Query[qi->queryindex].querytype != Q_INVERSE ) {
1369 |             g_string_sprintf(sql_command, 
1370 |                              "SELECT type, port, host "
1371 |                                  "FROM %s ID, refer "
1372 |                                  "WHERE ID.id = refer.object_id", 
1373 |                              sub_table);
1374 | 
1375 | 	    if( SQ_execute_query(sql_connection, sql_command->str, 
1376 | 				    &result_referrals) == -1) 
1377 |             {
1378 | 	        *sql_error = SQ_errno(sql_connection);
1379 | 	        report_sql_error(&qe->condat, sql_connection, sql_command->str);
1380 |                 foundcount = 0;
1381 |                 goto exit_qi_collect_domain;
1382 |             }
1383 | 	    refcount = SQ_num_rows(result_referrals);
1384 | 	}
1385 | 
1386 | 	/* if referral allowed and defined, even if domain was found but 
1387 | 	   contained referral - refer the query */
1388 | 	if( refcount != 0 ) { 
1389 | 	    /* get the referral parameters from the first row
1390 | 	       and perform it 
1391 | 	    */
1392 | 
1393 | 	    row = SQ_row_next(result_referrals);
1394 | 	    /* now: query for the original domain */
1395 | 	    qi_prep_run_refer(domain,
1396 | 			      qis, qe, qi, result_referrals, row, sourcename);
1397 | 	    
1398 | 	    acc_credit->referrals -= 1;
1399 | 	}
1400 | 	else {
1401 | 	    /* domain found 
1402 | 	       and (referral undefined  or  disabled by -R or inverse)
1403 | 	       two possible outcomes depending on whether 'dot' is:
1404 | 	       * the original search term -> pass what's in _S and quit 
1405 | 	       * a 'stripped' domain name -> return no result and quit
1406 | 	    */
1407 | 	    if( dot == domain ) {
1408 | 		g_string_sprintf(sql_command, 
1409 |                                  "INSERT INTO %s SELECT id FROM %s", 
1410 | 			         id_table, sub_table);
1411 | 		if (SQ_execute_query(sql_connection, 
1412 |                                      sql_command->str, NULL) == -1) 
1413 |                 {
1414 | 		    *sql_error = SQ_errno(sql_connection);
1415 | 	            report_sql_error(&qe->condat, sql_connection, 
1416 |                                      sql_command->str);
1417 |                     foundcount = 0;
1418 |                     goto exit_qi_collect_domain;
1419 | 		}
1420 | 		foundcount = SQ_get_affected_rows(sql_connection); 
1421 | 	    }
1422 | 	} 
1423 | 	dot = NULL; /* don't make another round */
1424 |     } /* a domain was found */
1425 | 
1426 |     if( result_referrals != NULL ) {
1427 | 	SQ_free_result(result_referrals);
1428 | 	result_referrals = NULL;
1429 |     }
1430 |     
1431 |     if( dot != NULL && (dot=index(dot,'.')) != NULL) {
1432 |       dot++;
1433 |     }
1434 |   }
1435 | 
1436 | /* unified success/failure exit point to perform cleanup */
1437 | exit_qi_collect_domain:
1438 | 
1439 |   /* free the string we used for queries */
1440 |   g_string_free(sql_command, TRUE);
1441 |     
1442 |   return foundcount;
1443 | } /* qi_collect_domain */
1444 | 
1445 | 
1446 | /* add_ref_name */
1447 | /*++++++++++++++++++++++++++++++++++++++
1448 | 
1449 |   Creates a SQL query for a reference-by-name lookup. Uses standard name
1450 |   lookup query generator (create_name_query), so the order of the names
1451 |   doesn't matter.
1452 | 
1453 |   SQ_connection_t *sql_connection   sql connection dedicated to this thread
1454 | 
1455 |   char *rectable       table in which to look up
1456 |   
1457 |   char *allnames       all name words to be looked up, space delimited.
1458 | 
1459 | ++++++++++++++++++++++++++++++++++++++*/
1460 | static
1461 | int 
1462 | add_ref_name(SQ_connection_t *sql_connection, 
1463 | 	     char *rectable,
1464 | 	     char *allnames,
1465 | 	     sk_conn_st *condat
1466 | 	     )
1467 | {
1468 |   int error;
1469 | 
1470 |   error = 0;
1471 | 
1472 |   /* construct the query, allow zero-length list */
1473 |   if( strlen(allnames) > 0 ) {
1474 |     GString *final_query;
1475 |     GString *select_query;
1476 | 
1477 |     final_query = g_string_sized_new(STR_XL);
1478 |     select_query = g_string_sized_new(STR_XL);
1479 | 
1480 |     create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s "
1481 | 		      "AND N00.object_type != 100 AND N00.thread_id = 0", 
1482 | 		      allnames);
1483 |     
1484 |     g_string_sprintf(final_query, "INSERT INTO %s %s",
1485 | 	             rectable, select_query->str);
1486 | 
1487 |     allnames[0]=0;
1488 |     
1489 |     if (SQ_execute_query(sql_connection, final_query->str, NULL) == -1 ) {
1490 |         report_sql_error(condat, sql_connection, final_query->str);
1491 |         error = SQ_errno(sql_connection);
1492 |     }
1493 | 
1494 |     g_string_free(select_query, TRUE);
1495 |     g_string_free(final_query, TRUE);
1496 |   }
1497 | 
1498 |   return error;
1499 | }/* add_ref_name */
1500 | 
1501 | 
1502 | 
1503 | /* qi_collect_ids */
1504 | /*++++++++++++++++++++++++++++++++++++++ 
1505 |   
1506 |   collects object ID's from all queries defined in the Query_instructions 
1507 |   array. The results from RADIX trees are maintained in a linked list, the 
1508 |   results from SQL lookups are kept in a temporary table. For domains,
1509 |   a specific function is invoked that may run the referral. 
1510 |   Any sql lookup will be limited to the maximum number of objects allowed
1511 |   for the client (acl and credit are checked for this).
1512 |   The routine uses its own temporary _S table, destroyed at exit.
1513 |   
1514 |   ca_dbSource_t *dbhdl              source-specific identifier (defined in CA)
1515 |   
1516 |   char *sourcename                  name of the database "source"
1517 |   
1518 |   SQ_connection_t **sql_connection  sql connection dedicated to this thread
1519 |                                     (replaced on cancel)
1520 |  
1521 |   Query_instructions *qis           original query instructions structure
1522 | 
1523 |   Query_environ *qe                 original query environment structure
1524 |  
1525 |   char *id_table                    the table to store the ID's found
1526 |   
1527 |   GList **datlist                   the list  to store the Radix leaves found
1528 |   
1529 |   acc_st *acc_credit                credit for this client 
1530 |   
1531 |   acl_st *acl                       acl for this client 
1532 |   
1533 |   ++++++++++++++++++++++++++++++++++++++*/
1534 | static
1535 | int
1536 | qi_collect_ids(ca_dbSource_t *dbhdl,
1537 | 	       char *sourcename,
1538 | 	       SQ_connection_t **sql_connection,
1539 | 	       Query_instructions *qis,
1540 | 	       Query_environ *qe,	
1541 | 	       char *id_table,
1542 | 	       GList **datlist,
1543 | 	       acc_st *acc_credit,
1544 | 	       acl_st *acl
1545 | 	       )
1546 | {
1547 |   Query_instruction **ins=NULL;
1548 |   int i;
1549 |   int  count, errors=0;
1550 |   GString *sql_command;
1551 |   er_ret_t err;
1552 |   char sub_table[64];
1553 |   int limit ;
1554 |              /* a limit on the max number of objects to be returned
1555 | 		from a single search. For some queries the object types
1556 | 		are not known at this stage, so the limit must be
1557 | 		the higher number of the two: private / public,
1558 | 		or unlimited if any of them is 'unlimited'.
1559 | 	     */
1560 |   char limit_str[32];
1561 |   int sql_error;
1562 | 
1563 |   /* use a nice resizing GString for our command */
1564 |   sql_command = g_string_sized_new(STR_XL);
1565 | 
1566 |   if( (limit = AC_get_higher_limit(acc_credit,acl)) == -1) {
1567 |     strcpy(limit_str,"");
1568 |   } else {
1569 |     sprintf(limit_str," LIMIT %d", limit+1); /* make sure we collect more
1570 | 						so that the client hits
1571 | 						the limit */
1572 |   }
1573 | 
1574 |   sprintf(sub_table, "%s_S ", id_table);
1575 |   
1576 |   /* see if there was a leftover table from a crashed session 
1577 |    * (assume the ID cannot be currently in use)
1578 |    *
1579 |    * update: this can't ever happen with TEMPORARY tables, but we're going to
1580 |    *         check for it anyway  - shane
1581 |    */
1582 |   g_string_sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1583 |   if( SQ_execute_query(*sql_connection, sql_command->str, NULL) == -1 ) {
1584 |       report_sql_error(&qe->condat, *sql_connection, sql_command->str);
1585 |       return SQ_errno(*sql_connection);
1586 |   }
1587 | 
1588 |   /* create a table for special subqueries (domain only for now) */
1589 |   g_string_sprintf(sql_command, 
1590 |                    "CREATE " TEMPORARY " TABLE %s ( id int ) TYPE=HEAP", 
1591 |                    sub_table);
1592 |   if( SQ_execute_query(*sql_connection, sql_command->str, NULL) == -1 ) {
1593 |       report_sql_error(&qe->condat, *sql_connection, sql_command->str);
1594 |       return SQ_errno(*sql_connection);
1595 |   }
1596 |   
1597 |   /* Iterate through query instructions */
1598 |   ins = qis->instruction;
1599 |   sql_error = 0;
1600 |   for (i=0; ins[i] != NULL && errors == 0; i++) {
1601 |     Query_instruction *qi = ins[i];
1602 |     
1603 |     /* check if the client is still there */
1604 |     if( qe->condat.rtc ) {
1605 |       break;
1606 |     }
1607 | 
1608 |     switch ( qi->search_type ) {
1609 |     case R_SQL:
1610 |       count = 0;
1611 |       if ( qi->query_str != NULL ) {
1612 | 
1613 | 	/* handle special cases first */
1614 | 	if( Query[qi->queryindex].class == C_DN 
1615 | 	    && Query[qi->queryindex].querytype == Q_LOOKUP ) {
1616 | 	  
1617 | 	  /* if any more cases than just domain appear, we will be
1618 | 	     cleaning the _S table from the previous query here 
1619 | 	     
1620 | 	     "DELETE FROM %s_S"
1621 | 	  */
1622 | 	  
1623 | 	  count = qi_collect_domain(sourcename, *sql_connection, id_table, 
1624 | 				    sub_table, qis, qe, qi, acc_credit, 
1625 | 				    &sql_error);
1626 | 	} /* if class DN and Straight lookup */
1627 | 	else {
1628 | 	  /* any other class of query */
1629 | 
1630 | 	  g_string_sprintf(sql_command, "INSERT INTO %s %s %s", 
1631 | 		  id_table, qi->query_str, limit_str);
1632 | 
1633 | 	  if(sql_execute_watched( &(qe->condat), sql_connection, 
1634 | 				  sql_command->str, NULL) == -1 ) {
1635 | 	    errors++;
1636 | 	    sql_error = SQ_errno(*sql_connection);
1637 | 	    report_sql_error(&qe->condat, *sql_connection, sql_command->str);
1638 | 	  }
1639 | 	  count = SQ_get_affected_rows(*sql_connection);
1640 | 	} /* not DN */
1641 |       } /* if SQL query not NULL */
1642 |       
1643 |       ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1644 | 		"%d entries added in %s query for %s",
1645 | 		count, Query[qi->queryindex].descr, qis->qc->keys
1646 | 		);
1647 |       break;
1648 |       
1649 |     case R_RADIX:
1650 | 
1651 |      
1652 |       err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, 
1653 | 			  qi->rx_keys, dbhdl, 
1654 | 			  Query[qi->queryindex].attribute, 
1655 | 			  datlist, limit);
1656 |      
1657 | 
1658 |       if( NOERR(err)) {
1659 | 	if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) {
1660 | 	  /* prevent unnecessary g_list_length call */
1661 | 	  
1662 | 	  ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1663 | 		    "%d entries after %s (mode %d par %d reg %d) query for %s",
1664 | 		    g_list_length(*datlist),
1665 | 		    Query[qi->queryindex].descr,
1666 | 		    qi->rx_srch_mode, qi->rx_par_a, 
1667 | 		    dbhdl,
1668 | 		    qi->rx_keys);
1669 | 	}
1670 |       }
1671 |       else {
1672 | 	ER_inf_va(FAC_QI, ASP_QI_COLL_DET,
1673 | 		  "RP_asc_search returned %x ", err);
1674 |       }
1675 |       break;
1676 |       
1677 |     default: die;
1678 |     } /* switch */
1679 |     
1680 |   } /* for <every instruction> */
1681 | 
1682 |   /* Now drop the _S table */
1683 |   g_string_sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1684 |   if(SQ_execute_query(*sql_connection, sql_command->str, NULL) == -1 ) {
1685 |       report_sql_error(&qe->condat, *sql_connection, sql_command->str);
1686 |       sql_error = SQ_errno(*sql_connection);
1687 |   }
1688 | 
1689 |   /* free our command buffer */
1690 |   g_string_free(sql_command, TRUE);
1691 | 
1692 |   /* return success */
1693 |   return sql_error;
1694 | }
1695 | 
1696 | /* qi_fetch_references */ 
1697 | /*++++++++++++++++++++++++++++++++++++++
1698 |   
1699 |   given the list of object ID's collects the references from these objects 
1700 |   to person and role objects. Uses its own temporary SQL table (_R)
1701 |   and upon completion transfers the results from it to the main
1702 |   temporary table. Runs queries in watched mode, to be able to cancel them.
1703 |   
1704 |   SQ_connection_t **sql_connection  sql connection dedicated to this thread
1705 |                                     (replaced on cancel)
1706 |   
1707 |   Query_environ *qe                 original query environment structure
1708 |   
1709 |   char *id_table                    the table with the ID's found
1710 |   
1711 |   acc_st *acc_credit                credit for this client 
1712 |   
1713 |   acl_st *acl                       acl for this client 
1714 | 
1715 | ++++++++++++++++++++++++++++++++++++++*/
1716 | static
1717 | int
1718 | qi_fetch_references(SQ_connection_t **sql_connection,
1719 | 		    Query_environ *qe,
1720 | 		    char *id_table,
1721 | 		    acc_st *acc_credit,
1722 | 		    acl_st *acl
1723 | 		    )
1724 | {
1725 |     char rec_table[64];
1726 |     SQ_result_set_t *result = NULL;
1727 |     SQ_row_t *row;
1728 |     int thisid = 0;
1729 |     int oldid = 0;
1730 |     char allnames[STR_L];
1731 |     char sql_command[STR_XL];
1732 |     int sql_error;
1733 | 
1734 |     /* use sql_error to flag errors */
1735 |     sql_error = 0;
1736 |  
1737 |     sprintf(rec_table, "%s_R", id_table);
1738 | 
1739 |     /* see if there was a leftover table from a crashed session 
1740 |      * (assume the ID cannot be currently in use)
1741 |      */
1742 |     sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table);
1743 |     if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1) {
1744 |         report_sql_error(&qe->condat, *sql_connection, sql_command);
1745 |         return SQ_errno(*sql_connection);
1746 |     }
1747 | 
1748 |     /* a temporary table for recursive data must be created, because
1749 |        a query using the same table as a source and target is illegal
1750 |        ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... )
1751 |     */
1752 |     sprintf(sql_command, 
1753 |      "CREATE " TEMPORARY " TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", 
1754 |      rec_table);
1755 |     if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1) {
1756 |         report_sql_error(&qe->condat, *sql_connection, sql_command);
1757 |         return SQ_errno(*sql_connection);
1758 |     }
1759 | 
1760 |     /* from this point on, we can't just return on error, because 
1761 |        we need to insure the table we just created gets dropped */
1762 |     
1763 |     /* find the contacts */      
1764 |     sprintf(sql_command, Q_REC, rec_table, id_table, "author");
1765 |     if (sql_execute_watched( &(qe->condat), sql_connection, sql_command, NULL) 
1766 |         == -1)
1767 |     {
1768 |         sql_error = SQ_errno(*sql_connection);
1769 | 	report_sql_error(&qe->condat, *sql_connection, sql_command);
1770 |     }
1771 |     
1772 |     if (!sql_error) {
1773 |         sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c");
1774 |         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1775 | 	    NULL) == -1) 
1776 |         {
1777 | 	    sql_error = SQ_errno(*sql_connection);
1778 | 	    report_sql_error(&qe->condat, *sql_connection, sql_command);
1779 | 	}
1780 |     }
1781 |     
1782 |     if (!sql_error) {
1783 |         sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c");
1784 |         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1785 | 	    NULL) == -1) 
1786 |         {
1787 | 	    sql_error = SQ_errno(*sql_connection);
1788 | 	    report_sql_error(&qe->condat, *sql_connection, sql_command);
1789 | 	}
1790 |     }
1791 | 
1792 |     if (!sql_error) {
1793 |         sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c");
1794 |         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1795 | 	    NULL) == -1) 
1796 |         {
1797 | 	    sql_error = SQ_errno(*sql_connection);
1798 | 	    report_sql_error(&qe->condat, *sql_connection, sql_command);
1799 | 	}
1800 |     }
1801 |     
1802 |     /* replace references to dummies by references by name */
1803 |     if (!sql_error) {
1804 |         sprintf(sql_command, 
1805 | 	    " SELECT id, name    FROM %s IDS STRAIGHT_JOIN names "
1806 | 	    " WHERE IDS.id = names.object_id "
1807 | 	    "      AND names.object_type = 100"
1808 | 	    " ORDER BY id",
1809 | 	    rec_table);
1810 |         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1811 | 	    &result) == -1) 
1812 |         {
1813 | 	    sql_error = SQ_errno(*sql_connection);
1814 | 	    report_sql_error(&qe->condat, *sql_connection, sql_command);
1815 | 	}
1816 |     }
1817 |     
1818 |     /* well, it might not be -1, but if the watchdog worked then the
1819 |        result is NULL */
1820 |     if (!sql_error && (result != NULL)) {
1821 |       
1822 |       allnames[0]=0;
1823 |       /* now go through the results and collect names */
1824 |       while ( !sql_error &&
1825 |               (qe->condat.rtc == 0) && 
1826 | 	      (row = SQ_row_next(result)) != NULL ) 
1827 |       {
1828 | 	char *id   = SQ_get_column_string(result, row, 0);
1829 | 	char *name = SQ_get_column_string(result, row, 1);
1830 | 	
1831 | 	thisid = atoi(id);
1832 | 	
1833 | 	/* when the id changes, the name is complete */
1834 | 	if( thisid != oldid && oldid != 0 ) {
1835 | 	    sql_error = add_ref_name( *sql_connection, rec_table, allnames,
1836 |  	                              &qe->condat);
1837 | 	}
1838 | 	
1839 | 	strcat(allnames, name);
1840 | 	strcat(allnames, " ");
1841 | 	oldid = thisid;
1842 | 	UT_free(id);
1843 | 	UT_free(name);
1844 |       }
1845 |       /* also do the last name */
1846 |       if (!sql_error) {
1847 |           sql_error = add_ref_name( *sql_connection, rec_table, allnames,
1848 | 	                            &qe->condat);
1849 |       }
1850 |       
1851 |       SQ_free_result(result); /* we can do it only because the watchdog */
1852 |       /* has not started between the check for non-NULL result and here */
1853 |     }
1854 |     
1855 |     /* if we've lost connection, don't bother with this extra work */
1856 |     if (!sql_error && (qe->condat.rtc == 0)) {
1857 |         /* now copy things back to the main temporary table   */
1858 |         sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s", 
1859 | 	       id_table, rec_table);
1860 |         if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1861 | 	    sql_error = SQ_errno(*sql_connection);
1862 | 	    report_sql_error(&qe->condat, *sql_connection, sql_command);
1863 | 	}
1864 |     }
1865 | 
1866 |     /* Now drop the IDS recursive table (try to do this even if
1867 |        we had an SQL error, to avoid leaving extra tables lying around) */
1868 |     sprintf(sql_command, "DROP TABLE IF EXISTS %s", rec_table);
1869 |     if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1870 |         sql_error = SQ_errno(*sql_connection);
1871 | 	report_sql_error(&qe->condat, *sql_connection, sql_command);
1872 |     }
1873 | 
1874 |     /* return error, if any */
1875 |     return sql_error;
1876 | }
1877 | /* qi_fetch_references */ 
1878 | 
1879 | 
1880 | /* QI_execute() */
1881 | /*++++++++++++++++++++++++++++++++++++++
1882 |   Execute the query instructions.  This is called for each source.
1883 |   This is linked into MySQL by the fact that MySQL doesn't have sub selects
1884 |  (yet).  The queries are done in two stages.  Make some temporary tables and
1885 |   insert into them.  Then use them in the next select.
1886 | 
1887 |    
1888 |   ca_dbSource_t *dbhdl            source-specific identifier (defined in CA)
1889 | 
1890 |   Query_instructions *qis         query instructions.
1891 | 				 
1892 |   Query_environ *qe               query environment.
1893 | 				 
1894 |   acc_st *acc_credit              object display credit 
1895 | 				 
1896 |   acl_st *acl                     copy of the original acl for this client 
1897 | 
1898 |   More:
1899 |   +html+ <PRE>
1900 |   Authors:
1901 |         ottrey - original version,
1902 | 	marek - the rest.
1903 |   +html+ </PRE>
1904 | ++++++++++++++++++++++++++++++++++++++*/
1905 | er_ret_t QI_execute(ca_dbSource_t *dbhdl,
1906 | 		    Query_instructions *qis, 
1907 | 		    Query_environ *qe,	
1908 | 		    acc_st *acc_credit,
1909 | 		    acl_st *acl
1910 | 		    ) 
1911 | {
1912 |   /* those things must be freed after use! */
1913 |   char *dbhost = ca_get_srcdbmachine(dbhdl);
1914 |   char *dbname = ca_get_srcdbname(dbhdl);
1915 |   char *dbuser = ca_get_srcdbuser(dbhdl);
1916 |   char *dbpass = ca_get_srcdbpassword(dbhdl);
1917 |   char *srcnam;
1918 |   unsigned dbport = ca_get_srcdbport(dbhdl);
1919 |   char id_table[64];
1920 |   char sql_command[STR_XL];
1921 |   GList *datlist=NULL;
1922 |   SQ_connection_t *sql_connection=NULL;
1923 |   int sql_error;
1924 | 
1925 |   sql_connection = SQ_get_connection( dbhost, dbport,
1926 | 				      dbname, dbuser, dbpass );
1927 |   /* free parameters when done */
1928 |   UT_free(dbhost);
1929 |   UT_free(dbuser);
1930 |   UT_free(dbpass);
1931 | 
1932 |   /* return error if occurred */
1933 |   if (sql_connection == NULL) {
1934 |     ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s", 
1935 | 	      dbname, SQ_errno(sql_connection), SQ_error(sql_connection));
1936 |     UT_free(dbname);
1937 |     return QI_CANTDB;
1938 |   }
1939 |   UT_free(dbname);
1940 | 
1941 |   /* from here on out, we use the sql_error flag to verify our 
1942 |      connection to the SQL database is still good */
1943 |   sql_error = 0;
1944 | 
1945 |   sprintf(id_table, "ID_%lu_%u",   mysql_thread_id(sql_connection),
1946 | 	  pthread_self());
1947 | 
1948 |   /* see if there was a leftover table from a crashed session 
1949 |    * (assume the ID cannot be currently in use)
1950 |    */
1951 |   sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1952 |   if (SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
1953 |       sql_error = SQ_errno(sql_connection);
1954 |       report_sql_error(&qe->condat, sql_connection, sql_command);
1955 |   }
1956 |   
1957 |   /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */
1958 |   if (!sql_error) {
1959 |       sprintf(sql_command, 
1960 |               "CREATE " TEMPORARY 
1961 | 	      " TABLE %s ( id INT PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table);
1962 |       if (SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
1963 |           sql_error = SQ_errno(sql_connection);
1964 |           report_sql_error(&qe->condat, sql_connection, sql_command);
1965 |       }
1966 |   }
1967 | 
1968 |   if (!sql_error) { 
1969 |       srcnam = ca_get_srcname(dbhdl);
1970 |       sql_error = qi_collect_ids(dbhdl, srcnam, &sql_connection, qis, qe, 
1971 |                                  id_table, &datlist, acc_credit, acl);
1972 |       UT_free(srcnam);
1973 |   }
1974 | 
1975 |   /* post-processing */
1976 |   if (!sql_error && (qis->filtered == 0)) {
1977 |       /* start the watchdog just to set the rtc flag */
1978 |       SK_watch_setclear(&(qe->condat));
1979 |       SK_watchstart(&(qe->condat));
1980 | 
1981 |       /* add radix results (only if -K is not active and still connected) */
1982 |       if (qe->condat.rtc == 0) {
1983 | 
1984 |           if (qis->qc->c_irt_search) {
1985 |               mnt_irt_filter(&(qe->condat), sql_connection, &datlist);
1986 |           }
1987 | 
1988 |           sql_error = insert_radix_serials(&(qe->condat), 
1989 | 	                                   sql_connection, 
1990 | 	                                   id_table, 
1991 | 					   datlist);
1992 |       }
1993 | 
1994 |       SK_watchstop(&(qe->condat));
1995 |   }
1996 | 
1997 |   /* fetch recursive objects (ac,tc,zc,ah) */
1998 |   if (!sql_error && qis->recursive && (qe->condat.rtc == 0)) {
1999 |       sql_error = qi_fetch_references(&sql_connection, 
2000 |                                       qe, 
2001 | 				      id_table, 
2002 | 				      acc_credit, 
2003 | 				      acl);
2004 |   } /* if recursive */
2005 |   
2006 |   /* display */
2007 |   /* -K filtering: 
2008 |    * right now only filtering, no expanding sets like write_set_objects() 
2009 |    */
2010 |   
2011 |   /* display the immediate data from the radix tree */
2012 |   if (!sql_error && (qis->filtered == 1)) {
2013 |     write_radix_immediate(datlist, &(qe->condat), acc_credit, acl );
2014 |   }
2015 | 
2016 |   /* display objects from the IDs table */
2017 |   if (!sql_error) {
2018 |       sql_error = write_objects( &sql_connection, id_table, qis->filtered,
2019 | 		                qis->fast, &(qe->condat), acc_credit, acl);
2020 |   }
2021 | 
2022 |   /* drop the table */
2023 |   /* try to do this, even if there is an SQL error */
2024 |   sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
2025 |   if (SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
2026 |       sql_error = SQ_errno(sql_connection);
2027 |       report_sql_error(&qe->condat, sql_connection, sql_command);
2028 |   }
2029 | 
2030 |   /* Now disconnect (temporary tables get dropped automatically) */
2031 |   SQ_close_connection(sql_connection);  
2032 | 
2033 |   /* return appropriate value */
2034 |   if (sql_error) {
2035 |      return QI_SQLERR;
2036 |   } else {
2037 |      return QI_OK;
2038 |   }
2039 | } /* QI_execute() */
2040 | 
2041 | 
2042 | /* instruction_free() */
2043 | /*++++++++++++++++++++++++++++++++++++++
2044 |   Free the instruction.
2045 | 
2046 |   Query_instruction *qi query_instruction to be freed.
2047 |    
2048 |   More:
2049 |   +html+ <PRE>
2050 |   Authors:
2051 |         ottrey
2052 |   +html+ </PRE>
2053 |   ++++++++++++++++++++++++++++++++++++++*/
2054 | static void instruction_free(Query_instruction *qi) {
2055 |   if (qi != NULL) {
2056 |     if (qi->query_str != NULL) {
2057 |       UT_free(qi->query_str);
2058 |     }
2059 |     UT_free(qi);
2060 |   }
2061 | } /* instruction_free() */
2062 | 
2063 | /* QI_free() */
2064 | /*++++++++++++++++++++++++++++++++++++++
2065 |   Free the query_instructions.
2066 | 
2067 |   Query_instructions *qis Query_instructions to be freed.
2068 |    
2069 |   More:
2070 |   +html+ <PRE>
2071 |   Authors:
2072 |         ottrey, marek
2073 |   +html+ </PRE>
2074 |   ++++++++++++++++++++++++++++++++++++++*/
2075 | void QI_free(Query_instructions *qis) {
2076 |   int i;
2077 | 
2078 |   for (i=0; qis->instruction[i] != NULL; i++) {
2079 |     instruction_free(qis->instruction[i]);
2080 |   } 
2081 | 
2082 |   if (qis != NULL) {
2083 |     UT_free(qis);
2084 |   }
2085 | 
2086 | } /* QI_free() */
2087 | 
2088 | /*++++++++++++++++++++++++++++++++++++++
2089 |   Determine if this query should be conducted or not.
2090 | 
2091 |   If it was an inverse query - if the attribute appears in the query command's bitmap.
2092 |   If it was a lookup query - if the attribute appears in the object type bitmap or
2093 |                              disregard if there is no object_type bitmap (Ie object filter).
2094 | 
2095 |   mask_t bitmap The bitmap of attribute to be converted.
2096 |    
2097 |   const Query_command *qc  The query_command that the instructions are created
2098 |                            from.
2099 |   
2100 |   const Query_t q          The query being considered.
2101 |   +html+ <PRE>
2102 |   Authors:
2103 |         ottrey,
2104 | 	marek.
2105 |   +html+ </PRE>
2106 |   ++++++++++++++++++++++++++++++++++++++*/
2107 | static int valid_query(const Query_command *qc, const Query_t q) {
2108 |   int result=0;
2109 | 
2110 |   if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
2111 |     if (q.query != NULL) {
2112 |       switch (q.querytype) {
2113 |         case Q_INVERSE:
2114 |           if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
2115 |             result = 1;
2116 |           }
2117 |         break;
2118 | 
2119 |         case Q_LOOKUP:
2120 | 	  if (q.class == C_ANY 
2121 | 	      || MA_isset(qc->object_type_bitmap, (unsigned) q.class)) {
2122 |             result=1;
2123 |           }
2124 |         break;
2125 | 
2126 |         default:
2127 |           /* XXX */fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
2128 |       }
2129 |     }
2130 |   }
2131 | 
2132 |   return result;
2133 | } /* valid_query() */
2134 | 
2135 | /* QI_new() */
2136 | /*++++++++++++++++++++++++++++++++++++++
2137 |   Create a new set of query_instructions. Returns an allocated structure which
2138 |   must be freed after use with QI_free().
2139 | 
2140 |   const Query_command *qc The query_command that the instructions are created
2141 |                           from.
2142 | 
2143 |   const Query_environ *qe The environmental variables that they query is being
2144 |                           performed under.
2145 |   
2146 |   +html+ <PRE>
2147 |   Authors:
2148 |         ottrey,
2149 | 	marek.
2150 |   +html+ </PRE>
2151 |   ++++++++++++++++++++++++++++++++++++++*/
2152 | Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
2153 |   Query_instructions *qis=NULL;
2154 |   Query_instruction *qi=NULL;
2155 |   int i_no=0;
2156 |   int i;
2157 |   char *query_str;
2158 | 
2159 |   qis = (Query_instructions *)UT_calloc(1, sizeof(Query_instructions));
2160 | 
2161 |   qis->filtered = qc->filtered;
2162 |   qis->fast = qc->fast;
2163 |   qis->recursive = qc->recursive;
2164 |   qis->qc = (qc);
2165 | 
2166 |   
2167 |   for (i=0; Query[i].query != NULL; i++) {
2168 | 
2169 |     /* If a valid query. */
2170 |     if ( valid_query(qc, Query[i]) == 1) {
2171 | 
2172 |       qi = (Query_instruction *)UT_calloc(1, sizeof(Query_instruction));
2173 | 
2174 |       qi->queryindex = i;
2175 | 
2176 |       /* SQL Query */
2177 |       if ( Query[i].refer == R_SQL) {
2178 |         qi->search_type = R_SQL;
2179 |         query_str = create_query(Query[i], qc);
2180 | 
2181 |         if (query_str!= NULL) {
2182 |           qi->query_str = query_str;
2183 |           qis->instruction[i_no++] = qi;
2184 |         }
2185 |       }
2186 |       /* Radix Query */
2187 |       else if (Query[i].refer == R_RADIX) {
2188 | 
2189 |          /* no inverse queries should use the RADIX tree */
2190 |         if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
2191 |             continue;
2192 |         }
2193 | 
2194 |         qi->search_type = R_RADIX;
2195 | 	
2196 |         if (map_qc2rx(qi, qc) == 1) {
2197 | 	  int j;
2198 | 	  int found=0;
2199 | 	  
2200 |           /* check that there is no such query yet, for example if
2201 | 	     more than one keytype (wk) matched */
2202 | 	  for (j=0; j<i_no; j++) {
2203 | 	    Query_instruction *qij = qis->instruction[j];
2204 | 	    
2205 | 	    if(    qij->search_type == R_RADIX
2206 | 		   && Query[qij->queryindex].attribute 
2207 | 		   == Query[qi ->queryindex].attribute) {
2208 | 	      
2209 |               found=1;
2210 |               break;
2211 |             }
2212 |           }
2213 | 	  
2214 |           if ( found ) {
2215 |             /* Discard the Query Instruction */
2216 |             UT_free(qi);
2217 |           } 
2218 |           else {
2219 |             /* Add the query_instruction to the array */
2220 |             qis->instruction[i_no++] = qi;
2221 |           }
2222 |         }
2223 |       }
2224 |       else {
2225 | 	  /* ERROR: bad search_type */
2226 | 	  die;
2227 |       }
2228 |     }
2229 |   }
2230 |   qis->instruction[i_no++] = NULL;
2231 | 
2232 | 
2233 |   {  /* tracing */
2234 |       char *descrstr = QI_queries_to_string(qis);
2235 | 
2236 |       ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr );
2237 |       UT_free( descrstr );
2238 |   }
2239 | 
2240 |   return qis;
2241 | 
2242 | } /* QI_new() */
2243 | 
2244 | 
2245 |  
2246 | 
2247 | 
2248 | /*++++++++++++++++++++++++++++++++++++++
2249 |   
2250 |   char *QI_queries_to_string    returns a list of descriptions for queries 
2251 |                                 that will be performed (debugging only).
2252 | 				Allocated text, must be freed after use.
2253 | 
2254 |   Query_instructions *qis       query instructions structure
2255 | 
2256 |   Author:
2257 |      marek.
2258 |   ++++++++++++++++++++++++++++++++++++++*/
2259 | 
2260 | char *QI_queries_to_string(Query_instructions *qis)
2261 | {
2262 |    Query_instruction *qi;
2263 |    int i;
2264 |    char *resstr;
2265 | 
2266 |    resstr = (char *)UT_malloc(2);
2267 |    strcpy(resstr, "{");
2268 | 
2269 |    for( i = 0; ( qi=qis->instruction[i] ) != NULL;  i++ ) {
2270 |        char *descr = Query[qi->queryindex].descr;
2271 |        int oldres = strlen( resstr );
2272 |        
2273 |        resstr = (char *)UT_realloc(resstr, oldres+strlen(descr)+2);
2274 |        strcat(resstr, descr);
2275 |        strcat(resstr, ",");
2276 |    }
2277 |    if( i>0 ) {
2278 |        /* cancel the last comma */
2279 |        resstr[strlen(resstr)-1] = 0;
2280 |    }
2281 | 
2282 |    resstr = (char *)UT_realloc(resstr, strlen(resstr) + 2);
2283 |    strcat(resstr, "}");
2284 |    
2285 |    return resstr;
2286 | }