1    | /***************************************
2    |   $Revision: 1.30 $
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 "rxroutines.h"
48   | #include "stubs.h"
49   | #include "constants.h"
50   | #include "memwrap.h"
51   | 
52   | /*+ String sizes +*/
53   | #define STR_S   63
54   | #define STR_M   255
55   | #define STR_L   1023
56   | #define STR_XL  4095
57   | #define STR_XXL 16383
58   | 
59   | 
60   | #include "QI_queries.def"
61   | 
62   | /* log_inst_print() */
63   | /*++++++++++++++++++++++++++++++++++++++
64   |   Log the instruction.
65   | 
66   |   char *str instruction to be logged.
67   |    
68   |   More:
69   |   +html+ <PRE>
70   |   Authors:
71   |         ottrey
72   |   +html+ </PRE><DL COMPACT>
73   |   +html+ <DT>Online References:
74   |   +html+ <DD><UL>
75   |   +html+ </UL></DL>
76   | 
77   |   ++++++++++++++++++++++++++++++++++++++*/
78   | void log_inst_print(char *str) {
79   |   FILE *logf;
80   | 
81   |   if (CO_get_instr_logging() == 1) {
82   |     if (strcmp(CO_get_instr_logfile(), "stdout") == 0) {
83   |       printf("%s", str);
84   |     }
85   |     else {
86   |       logf = fopen(CO_get_instr_logfile(), "a");
87   |       fprintf(logf, "%s", str);
88   |       fclose(logf);
89   |     }
90   |   }
91   | 
92   | } /* log_inst_print() */
93   | 
94   | /* create_name_query() */
95   | /*++++++++++++++++++++++++++++++++++++++
96   |   Create an sql query for the names table. 
97   | 
98   |   char *query_str
99   | 
100  |   const char *sql_query
101  | 
102  |   const char *keys
103  |    
104  |   More:
105  |   +html+ <PRE>
106  |   Authors:
107  |   ottrey
108  |   +html+ </PRE><DL COMPACT>
109  |   +html+ <DT>Online References:
110  |   +html+ <DD><UL>
111  |   +html+ </UL></DL>
112  | 
113  |   ++++++++++++++++++++++++++++++++++++++*/
114  | static void create_name_query(char *query_str, const char *sql_query, const char *keys) {
115  |   int i;
116  |   /* Allocate stuff */
117  |   GString *from_clause = g_string_sized_new(STR_XL);
118  |   GString *where_clause = g_string_sized_new(STR_XL);
119  |   gchar **words = g_strsplit(keys, " ", 0);
120  | 
121  |   g_string_sprintfa(from_clause, "names N%.2d", 0);
122  |   g_string_sprintfa(where_clause, "N%.2d.name='%s'", 0, words[0]);
123  | 
124  |   for (i=1; words[i] != NULL; i++) {
125  |     g_string_sprintfa(from_clause, ", names N%.2d", i);
126  |     g_string_sprintfa(where_clause, " AND N%.2d.name='%s' AND N00.object_id = N%.2d.object_id", i, words[i], i);
127  |   }
128  | 
129  |   sprintf(query_str, sql_query, from_clause->str, where_clause->str);
130  | 
131  |   /* Free up stuff */
132  |   g_strfreev(words);
133  |   g_string_free(where_clause, TRUE);
134  |   g_string_free(from_clause, TRUE);
135  | 
136  | } /* create_name_query() */
137  | 
138  | static void add_filter(char *query_str, const Query_command *qc) {
139  |   int i;
140  |   int qlen;
141  |   char filter_atom[STR_M];
142  | 
143  | /*
144  |   if (MA_bitcount(qc->object_type_bitmap) > 0) { 
145  |     g_string_sprintfa(query_str, " AND (");
146  |     for (i=0; i < C_END; i++) {
147  |       if (MA_isset(qc->object_type_bitmap, i)) {
148  |         g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
149  |       }
150  |     }
151  |     g_string_truncate(query_str, query_str->len-3);
152  |     g_string_append_c(query_str, ')');
153  |   }
154  | */
155  |   if (MA_bitcount(qc->object_type_bitmap) > 0) { 
156  |     strcat(query_str, " AND (");
157  |     for (i=0; i < C_END; i++) {
158  |       if (MA_isset(qc->object_type_bitmap, i)) {
159  |         strcpy(filter_atom, "");
160  |         sprintf(filter_atom, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
161  |         strcat(query_str, filter_atom);
162  |       }
163  |     }
164  |     qlen = strlen(query_str);
165  |     query_str[qlen-3] = ')';
166  |     query_str[qlen-2] = '\0';
167  |     query_str[qlen-1] = '\0';
168  |   }
169  |   
170  | } /* add_filter() */
171  | 
172  | /* create_query() */
173  | /*++++++++++++++++++++++++++++++++++++++
174  |   Create an sql query from the query_command and the matching keytype and the
175  |   selected inverse attributes.
176  |   Note this clears the first inv_attribute it sees, so is called sequentially
177  |   until there are no inv_attributes left.
178  | 
179  |   WK_Type keytype The matching keytype.
180  | 
181  |   const Query_command *qc The query command.
182  | 
183  |   mask_t *inv_attrs_bitmap The selected inverse attributes.
184  |    
185  |   More:
186  |   +html+ <PRE>
187  |   Authors:
188  |         ottrey
189  |   +html+ </PRE><DL COMPACT>
190  |   +html+ <DT>Online References:
191  |   +html+ <DD><UL>
192  |   +html+ </UL></DL>
193  | 
194  |   ++++++++++++++++++++++++++++++++++++++*/
195  | static char *create_query(const Query_t q, const Query_command *qc) {
196  |   char *result=NULL;
197  |   char result_buff[STR_XL];
198  |   Q_Type_t querytype;
199  |   int conduct_test = 0;
200  | 
201  |   if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
202  |     querytype = Q_INVERSE;
203  |   }
204  |   else {
205  |     querytype = Q_LOOKUP;
206  |   }
207  | 
208  |   if ( (q.query != NULL) 
209  |     && (q.querytype == querytype) ) {
210  |     conduct_test=1;
211  |   }
212  | 
213  |   if (conduct_test == 1) {
214  | 
215  |     if (q.keytype == WK_NAME) { 
216  |       /* Name queries require special treatment. */
217  |        create_name_query(result_buff, q.query, qc->keys);
218  |     }
219  |     else {
220  |       sprintf(result_buff, q.query, qc->keys);
221  |     }
222  | 
223  |     if (q.class == -1) {
224  |       /* It is class type ANY so add the object filtering */
225  |       add_filter(result_buff, qc);
226  |     }
227  | 
228  |     //result = (char *)malloc(strlen(result_buff)+1);
229  |     dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);  
230  |     strcpy(result, result_buff);
231  |   }
232  |   
233  |   return result;
234  | } /* create_query() */
235  | 
236  | /* fast_output() */
237  | /*++++++++++++++++++++++++++++++++++++++
238  |   This is for the '-F' flag.
239  |   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
240  | 
241  |   Fast isn't fast anymore - it's just there for compatibility reasons.
242  |   This could be speed up if there were breaks out of the loops, once it matched something.
243  |   (Wanna add a goto Marek?  :-) ).
244  | 
245  |   const char *string The string to be "fast outputed".
246  |    
247  |   More:
248  |   +html+ <PRE>
249  |   Authors:
250  |         ottrey
251  |   +html+ </PRE><DL COMPACT>
252  |   +html+ <DT>Online References:
253  |   +html+ <DD><UL>
254  |   +html+ </UL></DL>
255  | 
256  |   ++++++++++++++++++++++++++++++++++++++*/
257  | 
258  | char *fast_output(const char *str) 
259  | {
260  | int i,j;
261  | char *result;
262  | char result_bit[STR_L];
263  | char result_buff[STR_XL];
264  | gchar **lines = g_strsplit(str, "\n", 0);
265  | char * const *attribute_names;
266  | gboolean filtering_an_attribute = FALSE;
267  | char *value;
268  | 
269  | attribute_names = DF_get_attribute_names();
270  | 
271  | strcpy(result_buff, "");
272  |  
273  |  for(i=0; attribute_names[i] != NULL; i++) {
274  |    for (j=0; lines[j] != NULL; j++) {
275  |      if (strncmp(attribute_names[i], lines[j], strlen(attribute_names[i])) == 0) {
276  |        strcpy(result_bit, "");
277  |        /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
278  |        value = strchr(lines[j], ':');
279  |        value++;
280  |        /* Now get rid of whitespace. */
281  |        while (*value == ' ' || *value == '\t') {
282  | 	 value++;
283  |        }
284  |        sprintf(result_bit, "*%s: %s\n", DF_get_attribute_code(i), value);
285  |        strcat(result_buff, result_bit);
286  |      }
287  |      else if (filtering_an_attribute == TRUE) {
288  |        switch (lines[j][0]) {
289  |        case ' ':
290  |        case '\t':
291  |        case '+':
292  | 	 strcpy(result_bit, "");
293  | 	 sprintf(result_bit, "%s\n", lines[j]);
294  | 	 strcat(result_buff, result_bit);
295  | 	 break;
296  | 	 
297  |        default:
298  | 	 filtering_an_attribute = FALSE;
299  |        }
300  |      }
301  |    }
302  |  }
303  |  
304  |  //  result = (char *)malloc(strlen(result_buff)+1);
305  |  dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
306  | 
307  |  strcpy(result, result_buff);
308  |  
309  |  return result;
310  | } /* fast_output() */
311  | 
312  | /* filter() */
313  | /*++++++++++++++++++++++++++++++++++++++
314  |   Basically it's for the '-K' flag for non-set (and non-radix) objects.
315  |   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
316  | 
317  |   This could be speed up if there were breaks out of the loops, once it matched something.
318  |   (Wanna add a goto Marek?  :-) ).
319  | 
320  |   const char *string The string to be filtered.
321  |    
322  |   More:
323  |   +html+ <PRE>
324  |   Authors:
325  |         ottrey
326  |   +html+ </PRE><DL COMPACT>
327  |   +html+ <DT>Online References:
328  |   +html+ <DD><UL>
329  |   +html+ </UL></DL>
330  | 
331  |   ++++++++++++++++++++++++++++++++++++++*/
332  | char *filter(const char *str) {
333  |   int i,j;
334  |   char *result;
335  |   char result_bit[STR_L];
336  |   char result_buff[STR_XL];
337  |   gchar **lines = g_strsplit(str, "\n", 0);
338  |   char * const *filter_names;
339  |   gboolean filtering_an_attribute = FALSE;
340  |   
341  |   filter_names = DF_get_filter_names();
342  | 
343  |   strcpy(result_buff, "");
344  |   for (i=0; filter_names[i] != NULL; i++) {
345  |     for (j=0; lines[j] != NULL; j++) {
346  |       if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) {
347  |         strcpy(result_bit, "");
348  |         sprintf(result_bit, "%s\n", lines[j]);
349  |         strcat(result_buff, result_bit);
350  |         filtering_an_attribute = TRUE;
351  |       }
352  |       else if (filtering_an_attribute == TRUE) {
353  |         switch (lines[j][0]) {
354  |           case ' ':
355  |           case '\t':
356  |           case '+':
357  |             strcpy(result_bit, "");
358  |             sprintf(result_bit, "%s\n", lines[j]);
359  |             strcat(result_buff, result_bit);
360  |           break;
361  | 
362  |           default:
363  |             filtering_an_attribute = FALSE;
364  |         }
365  |       }
366  |     }
367  |   }
368  | 
369  |   //result = (char *)malloc(strlen(result_buff)+1);
370  |   dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
371  |   strcpy(result, result_buff);
372  | 
373  |   return result;
374  | } /* filter() */
375  | 
376  | /* write_results() */
377  | /*++++++++++++++++++++++++++++++++++++++
378  |   Write the results to the client socket.
379  | 
380  |   SQ_result_set_t *result The result set returned from the sql query.
381  |   unsigned filtered       if the objects should go through a filter (-K)
382  |   sk_conn_st *condat      Connection data for the client    
383  |   int maxobjects          max # of objects to write
384  | 
385  |   XXX NB. this is very dependendant on what rows are returned in the result!!!
386  |    
387  |   More:
388  |   +html+ <PRE>
389  |   Authors:
390  |         ottrey
391  |   +html+ </PRE><DL COMPACT>
392  |   +html+ <DT>Online References:
393  |   +html+ <DD><UL>
394  |   +html+ </UL></DL>
395  | 
396  |   ++++++++++++++++++++++++++++++++++++++*/
397  | static int write_results(SQ_result_set_t *result, 
398  | 			 unsigned filtered,
399  | 			 unsigned fast,
400  | 			 sk_conn_st *condat,
401  | 			 acc_st    *acc_credit,
402  | 			 acl_st    *acl
403  | 			 ) {
404  |   SQ_row_t *row;
405  |   char *str;
406  |   char *filtrate;
407  |   char *fasted;
408  |   char log_str[STR_L];
409  |   int retrieved_objects=0;
410  |   char *objt;
411  |   int type;
412  | 
413  |   /* Get all the results - one at a time */
414  |   if (result != NULL) {
415  |     /* here we are making use of the mysql_store_result capability
416  |        of interrupting the cycle of reading rows. mysql_use_result
417  |        does not allow that, must be read until end */
418  |     
419  |     while ( (row = SQ_row_next(result)) != NULL  &&  acc_credit->denials == 0 ) {
420  |       
421  |       if ((str = SQ_get_column_string(result, row, 0)) == NULL) { die; }
422  |       else
423  | 	{ 
424  | 	  sprintf(log_str, "Retrieved serial id = %d , type = ", atoi(str));
425  | 	  wr_free(str);
426  | 	}
427  | 
428  |       /* get + add object type */
429  | 
430  |       if( (objt = SQ_get_column_string(result, row, 3)) == NULL ) { die; }
431  |       else {
432  | 	type = atoi(objt);
433  | 	
434  |         strcat(log_str, objt);
435  | 	strcat(log_str, "\n");
436  |         log_inst_print(log_str);
437  | 	wr_free(objt);
438  |       }
439  | 
440  |       /* decrement credit for accounting purposes */
441  |       
442  |       /* XXX the definition of private/public should go into the defs (xml) */
443  |       switch( type ) {
444  |       case C_PN:
445  |       case C_RO: 
446  | 	if( acc_credit->private_objects <= 0 && acl->maxbonus != -1 ) {
447  | 	  /* must be negative, will be subtracted */
448  | 	  acc_credit->denials = -1;
449  | 	  continue; /* go to the head of the loop */
450  | 	}
451  | 	acc_credit->private_objects --;
452  | 	break;
453  |       default:
454  | 	if( acc_credit->public_objects <= 0 && acl->maxpublic != -1 ) {
455  | 	  acc_credit->denials = -1;
456  | 	  continue; /* go to the head of the loop */
457  | 	}
458  | 	acc_credit->public_objects --;
459  |       }
460  |       
461  |       if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; } 
462  |       else {
463  | 	
464  |         /* The fast output stage */
465  |         if (fast == 1) {
466  |           fasted = fast_output(str);
467  |           wr_free(str);
468  |           str = fasted;
469  |         }
470  | 	
471  |         /* The filtering stage */
472  |         if (filtered == 0) {
473  |           SK_cd_puts(condat, str);
474  |         }
475  |         else {
476  |           filtrate = filter(str);
477  |           SK_cd_puts(condat, filtrate);
478  |           wr_free(filtrate);
479  |         }
480  |         SK_cd_puts(condat, "\n");
481  |         retrieved_objects++;
482  |       }
483  |       wr_free(str);
484  |     }
485  |   }
486  |   
487  |   return retrieved_objects;
488  | } /* write_results() */
489  | 
490  | /* write_objects() */
491  | /*++++++++++++++++++++++++++++++++++++++
492  |   This is linked into MySQL by the fact that MySQL doesn't have sub selects
493  |   (yet).  The queries are done in two stages.  Make some temporary tables and
494  |   insert into them.  Then use them in the next select.
495  | 
496  |   SQ_connection_t *sql_connection The connection to the database.
497  | 
498  |   char *id_table The id of the temporary table (This is a result of the hacky
499  |                   way we've tried to get MySQL to do sub-selects.)
500  |   
501  |   unsigned int recursive A recursive query.
502  | 
503  |   sk_conn_st *condat  Connection data for the client
504  | 
505  |   More:
506  |   +html+ <PRE>
507  |   Authors:
508  |         ottrey
509  |   +html+ </PRE><DL COMPACT>
510  |   ++++++++++++++++++++++++++++++++++++++*/
511  | static void write_objects(SQ_connection_t *sql_connection, 
512  | 			  char *id_table, 
513  | 			  unsigned int recursive, 
514  | 			  unsigned int filtered, 
515  | 			  unsigned int fast, 
516  | 			  sk_conn_st *condat,
517  | 			  acc_st    *acc_credit,
518  | 			  acl_st    *acl
519  | 			  ) 
520  | {
521  |   /* XXX This should really return a linked list of the objects */
522  | 
523  |   SQ_result_set_t *result;
524  |   int retrieved_objects=0;
525  |   char sql_command[STR_XL];
526  |   char log_str[STR_L];
527  |     
528  |   /* XXX These may and should change a lot. */
529  |   sprintf(sql_command, Q_OBJECTS, id_table, id_table);
530  |   result = SQ_execute_query(SQ_STORE, sql_connection, sql_command);
531  | 
532  |   retrieved_objects = write_results(result, filtered, fast, condat, acc_credit, acl);
533  |   SQ_free_result(result);
534  |   
535  |   /* Now for recursive queries (unless limit exceeded already) */
536  |   if (recursive == 1 && acc_credit->denials == 0) {
537  |     
538  |     /* create a table for recursive data */
539  |     sprintf(sql_command, "CREATE TABLE %s_R ( id int ) TYPE=HEAP", id_table);
540  |     SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
541  | 
542  |     /* find the contacts */
543  |     sprintf(sql_command, Q_REC, id_table, "admin_c", id_table, id_table);
544  |     SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
545  | 
546  |     sprintf(sql_command, Q_REC, id_table, "tech_c", id_table, id_table);
547  |     SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
548  |     
549  |     sprintf(sql_command, Q_REC, id_table, "zone_c", id_table, id_table);
550  |     SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
551  | 
552  |     /* XXX These may and should change a lot. */
553  |     sprintf(sql_command, Q_REC_OBJECTS, id_table, id_table);
554  |     result = SQ_execute_query(SQ_STORE, sql_connection, sql_command);
555  |     
556  |     retrieved_objects = write_results(result, filtered, fast, condat, acc_credit, acl);
557  |     SQ_free_result(result);
558  | 
559  |     /* Now drop the IDS recursive table */
560  |     sprintf(sql_command, "DROP TABLE %s_R", id_table);
561  |     SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
562  |   }
563  | 
564  |   if( acc_credit->denials != 0 ) {
565  |     SK_cd_puts(condat,
566  |        "% You have reached the limit of returned contact information objects.\n"
567  |        "% This connection will be terminated now.\n"
568  |        "% This is a mechanism to prevent abusive use of contact data in the RIPE Database.\n"
569  |        "% You will not be allowed to query for more CONTACT information for a while.\n"
570  |        "% Continued attempts to return excessive amounts of contact\n"
571  |        "% information will result in permanent denial of service.\n"
572  |        "% Please do not try to use CONTACT information information in the\n"
573  |        "% RIPE Database for non-operational purposes.\n"
574  |        "% Refer to http://www.ripe.net/db/dbcopyright.html for more information.\n"
575  |     );
576  |   }
577  |   
578  | } /* write_objects() */
579  | 
580  | /* insert_radix_serials() */
581  | /*++++++++++++++++++++++++++++++++++++++
582  |   Insert the radix serial numbers into a temporary table in the database.
583  | 
584  |   mask_t bitmap The bitmap of attribute to be converted.
585  |    
586  |   SQ_connection_t *sql_connection The connection to the database.
587  | 
588  |   char *id_table The id of the temporary table (This is a result of the hacky
589  |                   way we've tried to get MySQL to do sub-selects.)
590  |   
591  |   GList *datlist The list of data from the radix tree.
592  | 
593  |   XXX Hmmmmm this isn't really a good place to free things... infact it's quite nasty.  :-(
594  |   
595  |   More:
596  |   +html+ <PRE>
597  |   Authors:
598  |         ottrey
599  |   +html+ </PRE><DL COMPACT>
600  |   +html+ <DT>Online References:
601  |   +html+ <DD><UL>
602  |              <LI><A HREF="http://www.gtk.org/rdp/glib/glib-doubly-linked-lists.html">Glist</A>
603  |   +html+ </UL></DL>
604  | 
605  |   ++++++++++++++++++++++++++++++++++++++*/
606  | static void insert_radix_serials(SQ_connection_t *sql_connection, char *id_table, GList *datlist) {
607  |   GList    *qitem;
608  |   char sql_command[STR_XL];
609  |   int serial;
610  | 
611  |   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
612  |     rx_datcpy_t *datcpy = qitem->data;
613  | 
614  |     serial = datcpy->leafcpy.data_key;
615  | 
616  |     sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial);
617  |     SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
618  | 
619  |     wr_free(datcpy->leafcpy.data_ptr);
620  |   }
621  |   
622  |   g_list_foreach(datlist, rx_free_list_element, NULL);
623  |   g_list_free(datlist);
624  | 
625  | } /* insert_radix_serials() */
626  | 
627  | 
628  | /* write_radix_immediate() */
629  | /*++++++++++++++++++++++++++++++++++++++
630  |   Display the immediate data carried with the objects returned by the
631  |   radix tree.
632  | 
633  |   GList *datlist
634  |   sk_conn_st *condat  Connection data for the client
635  | More:
636  |   +html+ <PRE>
637  |   Authors:
638  |         marek
639  |   +html+ </PRE><DL COMPACT>
640  |   +html+ <DT>Online References:
641  |   +html+ <DD><UL>
642  |   +html+ </UL></DL>
643  |   
644  | 
645  |   Also free the list of answers.
646  | */
647  | static void write_radix_immediate(GList *datlist, sk_conn_st *condat) 
648  | {
649  |   GList    *qitem;
650  |   
651  |   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
652  |     rx_datcpy_t *datcpy = qitem->data;
653  | 
654  |     SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
655  |     SK_cd_puts(condat, "\n");
656  |     
657  |     wr_free(datcpy->leafcpy.data_ptr);
658  |   }
659  |   
660  |   g_list_foreach(datlist, rx_free_list_element, NULL);
661  |   g_list_free(datlist);
662  | } /* write_radix_immediate() */
663  | 
664  | 
665  | /* map_qc2rx() */
666  | /*++++++++++++++++++++++++++++++++++++++
667  |   The mapping between a query_command and a radix query.
668  | 
669  |   Query_instruction *qi The Query Instruction to be created from the mapping
670  |                         of the query command.
671  | 
672  |   const Query_command *qc The query command to be mapped.
673  | 
674  |   More:
675  |   +html+ <PRE>
676  |   Authors:
677  |         ottrey
678  |   +html+ </PRE><DL COMPACT>
679  |   +html+ <DT>Online References:
680  |   +html+ <DD><UL>
681  |   +html+ </UL></DL>
682  | 
683  |   ++++++++++++++++++++++++++++++++++++++*/
684  | static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
685  |   int result=0;
686  |   char log_str[STR_XL];
687  | 
688  |   qi->rx_keys = qc->keys;
689  | 
690  |   if (MA_bitcount(qc->object_type_bitmap) == 0) {
691  |     /* Ie. there was no object typed specified with the -T flag. */
692  |     result=1;
693  |   }
694  |   else {
695  |     switch(qi->family) {
696  |       case RX_FAM_IN:
697  |         if (MA_isset(qc->object_type_bitmap, C_IN)) {
698  |           result=1;
699  |         }
700  |       break;
701  | 
702  |       case RX_FAM_RT:
703  |         if (MA_isset(qc->object_type_bitmap, C_RT)) {
704  |           result=1;
705  |         }
706  |       break;
707  |       
708  |       default:
709  |         fprintf(stderr, "ERROR: Bad family type in radix query\n");
710  |     }
711  |   }
712  | 
713  |   if (result == 1) {
714  |     if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
715  |       qi->rx_srch_mode = RX_SRCH_EXLESS;
716  |       qi->rx_par_a = 0;
717  |     }
718  |     else if ( (qc->L == 1) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
719  |       qi->rx_srch_mode = RX_SRCH_LESS;
720  |       qi->rx_par_a = RX_ALL_DEPTHS;
721  |     }
722  |     else if ( (qc->L == 0) && (qc->M == 1) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
723  |       qi->rx_srch_mode = RX_SRCH_MORE;
724  |       qi->rx_par_a = RX_ALL_DEPTHS;
725  |     }
726  |     else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 1) && (qc->m == 0) && (qc->x == 0) ) {
727  |       qi->rx_srch_mode = RX_SRCH_LESS;
728  |       qi->rx_par_a = 1;
729  |     }
730  |     else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 1) && (qc->x == 0) ) {
731  |       qi->rx_srch_mode = RX_SRCH_MORE;
732  |       qi->rx_par_a = 1;
733  |     }
734  |     else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 1) ) {
735  |       qi->rx_srch_mode = RX_SRCH_EXACT;
736  |       qi->rx_par_a = 0;
737  |     }
738  |     else {
739  |       sprintf(log_str, "ERROR in qc2rx mapping: bad combination of flags\n");
740  |       log_inst_print(log_str);
741  |       result = 0;
742  |     }
743  |   }
744  | 
745  |   return result;
746  | 
747  | } /* map_qc2rx() */
748  | 
749  | /* run_referral() */
750  | /*
751  |    invoked when no such domain found. Goes through the domain table
752  |    and searches for shorter domains, then if it finds one with referral 
753  |    it performs it, otherwise it just returns nothing.
754  | 
755  |    to perform referral, it actually composes the referral query 
756  |    for a given host/port/type and calls the whois query function.
757  | 
758  |    Well, it returns nothing anyway (void). It just prints to the socket.
759  | 
760  | */
761  | void run_referral(SQ_connection_t *sql_connection, Query_instructions *qis,   Query_environ *qe, int qi_index) {
762  |   char *dot = qis->qc->keys;
763  |   char querystr[STR_L];
764  |   char log_str[STR_L];
765  |   SQ_row_t *row;
766  |   SQ_result_set_t *result;
767  |   char sql_command[STR_XL];
768  |   int stop_loop=0;
769  |   char *ref_host;
770  |   char *ref_type;
771  |   char *ref_port;
772  |   int  ref_port_int;
773  | 
774  |   strcpy(querystr,"");
775  | 
776  |   while( !stop_loop && (dot=index(dot,'.')) != NULL ) {
777  |     dot++;
778  | 
779  |     sprintf(log_str, "run_referral: checking %s\n", dot);
780  |     log_inst_print(log_str);
781  | 
782  | /* Changed for RIPE4 - ottrey 27/12/1999
783  |     sprintf(sql_command, "SELECT * FROM domain WHERE domain = '%s'", dot);
784  | */
785  |     sprintf(sql_command, "SELECT domain.object_id, domain, type, port, host FROM domain, refer WHERE domain.object_id = refer.object_id AND domain = '%s'", dot);
786  |     result = SQ_execute_query(SQ_STORE, sql_connection, sql_command);
787  | 
788  |     switch( SQ_num_rows(result) ) {
789  |       case 0: /* no such domain -> no action, will try next chunk */
790  |       break;
791  | 
792  |       case 1: /* check for referral host and perform query if present
793  |                in any case end the loop */
794  |       stop_loop=1;
795  |       assert( (row = SQ_row_next(result)) != NULL);
796  |       
797  |       ref_host = SQ_get_column_string(result, row, 4);
798  |       sprintf(log_str, "referral host is >%s<\n",ref_host);
799  |       log_inst_print(log_str);
800  |       if( ref_host != NULL && strlen(ref_host) > 0 ) {
801  |         ref_type = SQ_get_column_string(result, row, 2);
802  |         ref_port = SQ_get_column_string(result, row, 3);
803  |         
804  |         /* get the integer value, it should be correct */
805  |         if( sscanf( ref_port, "%d",&ref_port_int) < 1 ) {
806  |           die;
807  |         }
808  |          
809  |         /* compose the query: */
810  | 
811  |         /* put -r if the reftype is RIPE and -r or -i were used */
812  |         if( strcmp(ref_type,"RIPE") == 0 
813  |             && (   Query[qis->instruction[qi_index]->queryindex]
814  |                    .querytype == Q_INVERSE       
815  |                    || qis->recursive > 0  )   ) {
816  |           strcat(querystr," -r ");
817  |         }
818  | 
819  |         /* prepend with -Vversion,IP for type CLIENTADDRESS */
820  |         if( strcmp(ref_type,"CLIENTADDRESS") == 0 ) {
821  |           char optv[STR_M];
822  | 
823  |           snprintf(optv,STR_M," -V%s,%s ","RIP0.88", qe->condat.ip);
824  |           strcat(querystr,optv);
825  |         }
826  | 
827  |         /* now set the search term - set to the stripped down version 
828  |            for inverse query, full-length otherwise */
829  |         if( Query[qis->instruction[qi_index]->queryindex].querytype == Q_INVERSE ) {
830  |           strcat(querystr,dot);
831  |         }
832  |         else {
833  |           strcat(querystr,qis->qc->keys);
834  |         }
835  |         
836  |         /* WH_sock(sock, host, port, query, maxlines, timeout)) */
837  |         switch( WH_sock(qe->condat.sock, ref_host, ref_port_int, querystr,  25, 5) ) {
838  |           case WH_TIMEOUT:
839  |           SK_cd_puts(&(qe->condat),"referral timeout\n");
840  |           break;
841  | 
842  |           case WH_MAXLINES:
843  |           SK_cd_puts(&(qe->condat),"referral maxlines exceeded\n");
844  |           break;
845  | 
846  |           default:
847  |           ;
848  |         } /*switch WH_sock */
849  |       }
850  |       break;
851  | 
852  |       default: /* more than one domain in this file: something broken */
853  |       die;
854  |     }
855  |     SQ_free_result(result);
856  |   }
857  | } /*run_referral*/
858  | 
859  | /* QI_execute() */
860  | /*++++++++++++++++++++++++++++++++++++++
861  |   Execute the query instructions.  This is called by a g_list_foreach
862  |   function, so each of the sources in the "database source" list can be passed
863  |   into this function.
864  | 
865  |   This function has bloated itself.  Can we split it up Marek?  (ottrey 13/12/99)
866  | 
867  |   void *database_voidptr Pointer to the database.
868  |   
869  |   void *qis_voidptr Pointer to the query_instructions.
870  |    
871  |   More:
872  |   +html+ <PRE>
873  |   Authors:
874  |         ottrey
875  |   +html+ </PRE><DL COMPACT>
876  |   +html+ <DT>Online References:
877  |   +html+ <DD><UL>
878  |              <LI><A
879  |              HREF="http://www.gtk.org/rdp/glib/glib-singly-linked-lists.html#G-SLIST-FOREACH">g_list_foreach</A>
880  |   +html+ </UL></DL>
881  | 
882  |   ++++++++++++++++++++++++++++++++++++++*/
883  | void QI_execute(void *database_voidptr, 
884  | 		Query_instructions *qis, 
885  | 		Query_environ *qe,	
886  | 		acc_st *acc_credit,
887  | 		acl_st *acl
888  | 		) {
889  |   char *database = (char *)database_voidptr;
890  |   Query_instruction **ins=NULL;
891  |   char id_table[STR_S];
892  |   char sql_command[STR_XL];
893  |   GList *datlist=NULL;
894  |   int i;
895  |   SQ_row_t *row;
896  |   char log_str[STR_L];
897  |   SQ_result_set_t *result;
898  |   SQ_connection_t *sql_connection=NULL;
899  |   char *countstr;
900  |   int   count;
901  | 
902  |   sql_connection = SQ_get_connection(CO_get_host(), CO_get_database_port(), database, CO_get_user(), CO_get_password() );
903  | 
904  |   if (sql_connection == NULL) {
905  |     SK_cd_puts(&(qe->condat), "% WARNING: Failed to make connection to ");
906  |     SK_cd_puts(&(qe->condat), database);
907  |     SK_cd_puts(&(qe->condat), " database mirror.\n\n");
908  | 
909  |     /* XXX void prevents us from sending any error code back. It is OK ? */
910  |     return;
911  |   }
912  |   
913  |   /* XXX This is a really bad thing to do.  
914  |      It should'nt _have_ to be called here.
915  |      But unfortunately it does.   -- Sigh. */
916  |   sprintf(id_table, "ID_%d", mysql_thread_id(sql_connection) );
917  | 
918  |   /* create a table for id's of all objects found */
919  |   sprintf(sql_command, "CREATE TABLE %s ( id int ) TYPE=HEAP", id_table);
920  |   SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
921  | 
922  |   /* create a table for individual subqueries (one keytype) */
923  |   sprintf(sql_command, "CREATE TABLE %s_S ( id int ) TYPE=HEAP", id_table);
924  |   SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
925  |     
926  |   /* Iterate through query instructions */
927  |   ins = qis->instruction;
928  |   for (i=0; ins[i] != NULL; i++) {
929  |     Query_instruction *qi = ins[i];
930  | 
931  |     switch ( qi->search_type ) {
932  |     case R_SQL:
933  |       if ( qi->query_str != NULL ) {
934  | 
935  | 	/* handle special cases first */
936  | 	if( Query[qi->queryindex].class == C_DN ) {
937  | 
938  | 	  /* XXX if any more cases than just domain appear, we will be
939  | 	     cleaning the _S table from the previous query here */
940  | 	  
941  | 	  /* now query into the _S table */
942  | 	  sprintf(sql_command, "INSERT INTO %s_S %s", id_table, qi->query_str);
943  | 	  SQ_execute_query(SQ_NOSTORE, sql_connection, sql_command);
944  | 	  
945  | 	  /* if any results - copy to the id's table. 
946  | 	     Otherwise, run referral */
947  | 	  
948  | 	  sprintf(sql_command, "SELECT COUNT(*) FROM %s_S", id_table);
949  | 	  result = SQ_execute_query(SQ_STORE,sql_connection, sql_command);
950  | 	  row = SQ_row_next(result);
951  | 	  countstr = SQ_get_column_string(result, row, 0);
952  | 	  sscanf(countstr, "%d", &count);
953  | 	  SQ_free_result(result);
954  | 
955  | 	  sprintf(log_str, "DN lookup for %s found %d entries\n",
956  | 		  qis->qc->keys, count);
957  | 	  log_inst_print(log_str);
958  | 	  
959  | 	 
960  | 	  if( count ) {
961  | 	    sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s_S", 
962  | 		    id_table, id_table);
963  | 	    SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command);
964  | 	  }
965  | 
966  | 	  if( count == 0 
967  | 	      || Query[qi->queryindex].querytype == Q_INVERSE ) {
968  | 	    /* now: if the domain was not found, we run referral.
969  | 	       unless prohibited by a flag 
970  | 	      
971  | 	       But for inverse queries we return the things that were
972  | 	       or were not found AND also do the referral unless prohibited.
973  | 	    */
974  | 	    if (qis->qc->R == 0) {
975  | 	      run_referral(sql_connection, qis, qe, i);
976  | 	    }
977  | 	  }
978  | 	  
979  | 	}
980  | 	else {
981  | 	  sprintf(sql_command, "INSERT INTO %s %s", id_table, qi->query_str);
982  | 	  SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command);
983  | 
984  | 	  sprintf(sql_command, "SELECT COUNT(*) FROM %s", id_table);
985  | 	  result = SQ_execute_query(SQ_STORE,sql_connection, sql_command);
986  | 	  row = SQ_row_next(result);
987  | 	  countstr = SQ_get_column_string(result, row, 0);
988  | 	  sscanf(countstr, "%d", &count);
989  | 	  SQ_free_result(result);
990  | 
991  | 	  sprintf(log_str, "%d entries after class %s/%s lookup for %s found\n", count,
992  | 		  DF_get_class_code(Query[qi->queryindex].class),
993  | 		  DF_get_attribute_code(Query[qi->queryindex].attribute),
994  | 		  qis->qc->keys);
995  | 	  log_inst_print(log_str);
996  | 	}
997  |       }
998  |       break;
999  | 
1000 | #define RIPE_REG 17
1001 |     case R_RADIX:
1002 |       if ( RX_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, qi->rx_keys, RIPE_REG, qi->space, qi->family, &datlist, RX_ANS_ALL) == RX_OK ) {
1003 | 	sprintf(log_str, "After RX query (mode %d par %d spc %d fam %d reg %d key %s) datlist has %d objects\n",
1004 | 		qi->rx_srch_mode, qi->rx_par_a, qi->space, qi->family, 
1005 | 		RIPE_REG, 	qi->rx_keys,    
1006 | 		g_list_length(datlist) );
1007 | 	log_inst_print(log_str); 
1008 | 	    
1009 |       }
1010 |       else {
1011 | 	/* Skip query */
1012 | 	sprintf(log_str, " /* Skip in query */\n");
1013 | 	log_inst_print(log_str);
1014 |       }
1015 |       break;
1016 | 
1017 |     default: die;
1018 |     } /* switch */
1019 |   }
1020 | 
1021 |   /* post-processing */
1022 | 
1023 |   if( qis->filtered == 0 ) {
1024 |     /* add radix results to the end */
1025 |     insert_radix_serials(sql_connection, id_table, datlist);
1026 | 
1027 |     /* display objects */
1028 |     write_objects(sql_connection, id_table, qis->recursive, 0 /*nofilter*/, 
1029 | 		  qis->fast, &(qe->condat), acc_credit, acl);
1030 |   }
1031 |   else {
1032 |     /* XXX TODO display filtered objects, expanding sets */
1033 |     /* right now only the ugly filter thing instead */
1034 | 
1035 |     /* write_set_objects */
1036 | 
1037 |     /* write the remaining (non-set,non-radix) objects through the filter.
1038 |        imply no recursion */
1039 |     write_objects(sql_connection, id_table, 0, qis->filtered, qis->fast, &(qe->condat), acc_credit, acl);
1040 | 
1041 |     /* display the immediate data from the radix tree */
1042 |     /* XXX pass+decrease credit here */
1043 |     write_radix_immediate(datlist, &(qe->condat));
1044 |   }
1045 |   /* Now drop the _S table */
1046 |   sprintf(sql_command, "DROP TABLE %s_S", id_table);
1047 |   SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command);
1048 | 
1049 |   /* Now drop the IDS table */
1050 |   sprintf(sql_command, "DROP TABLE %s", id_table);
1051 |   SQ_execute_query(SQ_NOSTORE,sql_connection, sql_command);
1052 |   SQ_close_connection(sql_connection);
1053 | 
1054 |   
1055 | } /* QI_execute() */
1056 | /* instruction_free() */
1057 | /*++++++++++++++++++++++++++++++++++++++
1058 |   Free the instruction.
1059 | 
1060 |   Query_instruction *qi query_instruction to be freed.
1061 |    
1062 |   More:
1063 |   +html+ <PRE>
1064 |   Authors:
1065 |         ottrey
1066 |   +html+ </PRE><DL COMPACT>
1067 |   +html+ <DT>Online References:
1068 |   +html+ <DD><UL>
1069 |   +html+ </UL></DL>
1070 | 
1071 |   ++++++++++++++++++++++++++++++++++++++*/
1072 | static void instruction_free(Query_instruction *qi) {
1073 |   if (qi != NULL) {
1074 |     if (qi->query_str != NULL) {
1075 |       wr_free(qi->query_str);
1076 |     }
1077 |     wr_free(qi);
1078 |   }
1079 | } /* instruction_free() */
1080 | 
1081 | /* QI_free() */
1082 | /*++++++++++++++++++++++++++++++++++++++
1083 |   Free the query_instructions.
1084 | 
1085 |   Query_instructions *qis Query_instructions to be freed.
1086 |    
1087 |   XXX This isn't working too well at the moment.
1088 | 
1089 |   More:
1090 |   +html+ <PRE>
1091 |   Authors:
1092 |         ottrey
1093 |   +html+ </PRE><DL COMPACT>
1094 |   +html+ <DT>Online References:
1095 |   +html+ <DD><UL>
1096 |   +html+ </UL></DL>
1097 | 
1098 |   ++++++++++++++++++++++++++++++++++++++*/
1099 | void QI_free(Query_instructions *qis) {
1100 |   /* XXX huh!?H?
1101 |   int i;
1102 | 
1103 |   for (i=0; qis[i] != NULL; i++) {
1104 |     instruction_free(*qis[i]);
1105 |   }
1106 |   */
1107 |   if (qis != NULL) {
1108 |     wr_free(qis);
1109 |   }
1110 | 
1111 | } /* QI_free() */
1112 | 
1113 | /*++++++++++++++++++++++++++++++++++++++
1114 |   Determine if this query should be conducted or not.
1115 | 
1116 |   If it was an inverse query - it the attribute appears in the query command's bitmap.
1117 |   If it was a lookup query - if the attribute appears in the object type bitmap or
1118 |                              disregard if there is no object_type bitmap (Ie object filter).
1119 | 
1120 |   mask_t bitmap The bitmap of attribute to be converted.
1121 |    
1122 |   const Query_command *qc The query_command that the instructions are created
1123 |                           from.
1124 |   
1125 |   const Query_t q The query being investigated.
1126 | 
1127 |   ++++++++++++++++++++++++++++++++++++++*/
1128 | static int valid_query(const Query_command *qc, const Query_t q) {
1129 |   int result=0;
1130 | 
1131 |   if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
1132 |     if (q.query != NULL) {
1133 |       switch (q.querytype) {
1134 |         case Q_INVERSE:
1135 |           if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
1136 |             result = 1;
1137 |           }
1138 |         break;
1139 | 
1140 |         case Q_LOOKUP:
1141 |           if (MA_bitcount(qc->object_type_bitmap) == 0) {
1142 |             result=1;
1143 |           }
1144 |           else if (MA_isset(qc->object_type_bitmap, q.class)) {
1145 |             result=1;
1146 |           }
1147 |         break;
1148 | 
1149 |         default:
1150 |           fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
1151 |       }
1152 |     }
1153 |   }
1154 | 
1155 |   return result;
1156 | } /* valid_query() */
1157 | 
1158 | /* QI_new() */
1159 | /*++++++++++++++++++++++++++++++++++++++
1160 |   Create a new set of query_instructions.
1161 | 
1162 |   const Query_command *qc The query_command that the instructions are created
1163 |                           from.
1164 | 
1165 |   const Query_environ *qe The environmental variables that they query is being
1166 |                           performed under.
1167 |   More:
1168 |   +html+ <PRE>
1169 |   Authors:
1170 |         ottrey
1171 |   +html+ </PRE><DL COMPACT>
1172 |   +html+ <DT>Online References:
1173 |   +html+ <DD><UL>
1174 |   +html+ </UL></DL>
1175 | 
1176 |   ++++++++++++++++++++++++++++++++++++++*/
1177 | Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
1178 |   Query_instructions *qis=NULL;
1179 |   Query_instruction *qi=NULL;
1180 |   int i_no=0;
1181 |   int i;
1182 |   char *query_str;
1183 | 
1184 |   char log_str[STR_L];
1185 | 
1186 |   //qis = (Query_instructions *)calloc(1, sizeof(Query_instructions));
1187 |   dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK);
1188 |   
1189 | 
1190 |   qis->filtered = qc->filtered;
1191 |   qis->fast = qc->fast;
1192 |   qis->recursive = qc->recursive;
1193 |   qis->qc = (qc);
1194 | 
1195 | 
1196 |   for (i=0; Query[i].query != NULL; i++) {
1197 | 
1198 |     /* If a valid query. */
1199 |     if ( valid_query(qc, Query[i]) == 1) {
1200 |       //qi = (Query_instruction *)calloc(1, sizeof(Query_instruction));
1201 |       dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK);
1202 | 
1203 |       qi->queryindex = i;
1204 | 
1205 |       /* SQL Query */
1206 |       if ( Query[i].refer == R_SQL) {
1207 |         qi->search_type = R_SQL;
1208 |         query_str = create_query(Query[i], qc);
1209 | 
1210 |         if (query_str!= NULL) {
1211 |           qi->query_str = query_str;
1212 |           qis->instruction[i_no++] = qi;
1213 |         }
1214 |       }
1215 |       /* Radix Query */
1216 |       else if (Query[i].refer == R_RADIX) {
1217 |         qi->search_type = R_RADIX;
1218 |         qi->space = Query[i].space;
1219 |         qi->family = Query[i].family;
1220 | 
1221 |         if (map_qc2rx(qi, qc) == 1) {
1222 |         int j;
1223 |         int found=0;
1224 | 
1225 |           /* check that there is no such query yet */
1226 |           for (j=0; j<i_no; j++) {
1227 |             Query_instruction *qij = qis->instruction[j];
1228 |             
1229 |             if( qij->search_type == R_RADIX
1230 |                 && qij->space       == qi->space
1231 |                 && qij->family      == qi->family) {
1232 |               found=1;
1233 |               break;
1234 |             }
1235 |           }
1236 | 
1237 |           if ( found ) {
1238 |             /* Discard the Query Instruction */
1239 |             wr_free(qi);
1240 |           } 
1241 |           else {
1242 |             /* Add the query_instruction to the array */
1243 |             qis->instruction[i_no++] = qi;
1244 |           }
1245 |         }
1246 |       }
1247 |       else {
1248 |         sprintf(log_str, "ERROR: bad search_type\n");
1249 |         log_inst_print(log_str);
1250 |       }
1251 |     }
1252 |   }
1253 |   qis->instruction[i_no++] = NULL;
1254 | 
1255 |   return qis;
1256 | 
1257 | } /* QI_new() */