## modules/qi/query_instructions.c

/* [<][>][^][v][top][bottom][index][help] */

## FUNCTIONS

This source file includes following functions.

1. qi_kill_body
2. sql_execute_watched
3. create_name_query
4. create_asblock_query
5. add_filter
6. create_query
7. QI_fast_output
8. filter
9. write_results
10. write_objects
11. insert_radix_serials
12. write_radix_immediate
13. map_qc2rx
14. run_referral
15. qi_prep_run_refer
16. qi_collect_domain
17. add_ref_name
18. qi_collect_ids
19. qi_fetch_references
20. QI_execute
21. instruction_free
22. QI_free
23. valid_query
24. QI_new
25. QI_queries_to_string

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

/* [<][>][^][v][top][bottom][index][help] */