Source File qi/query_instructions.c

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