/***************************************
  $Revision: 1.58 $

  Query command module (qc).  This is what the whois query gets stored as in
  memory.

  Status: NOT REVUED, TESTED

  ******************/ /******************
  Filename            : query_command.c
  Author              : ottrey@ripe.net
  Modifications by    : marek@ripe.net
  ******************/ /******************
  Copyright (c) 1999,2000,2001,2002               RIPE NCC
 
  All Rights Reserved
  
  Permission to use, copy, modify, and distribute this software and its
  documentation for any purpose and without fee is hereby granted,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.
  
  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  ***************************************/

#define QC_IMPL
#include "rip.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>


#define MAX_OPT_ARG_C 20

extern char *suboptarg;
extern int getsubopt(char **optionp, char * const *tokens, char **valuep);

/*++++++++++++++++++++++++++++++++++++++
  make a list of sources. expects list to hold source handles

  char *
  qc_sources_list_to_string     returns an allocated string, must be freed
  
  GList *list                   list of source handles (as defined by CA)

  ++++++++++++++++++++++++++++++++++++++*/
char *
qc_sources_list_to_string(GList *list)
{
    GString *tmp;
    GList *qitem;
    char *result;

    /* use GString for the result, because it resizes magically */
    tmp = g_string_sized_new(STR_L);

    /* loop through the list, creating a string with the names of the
       sources separated by commas */
    qitem = g_list_first(list);
    if (qitem != NULL) {
        for (;;) {
            ca_dbSource_t *source_hdl = (ca_dbSource_t *) ( qitem->data );
            char *srcname = ca_get_srcname( source_hdl );
            g_string_append(tmp, srcname);

            qitem = g_list_next(qitem);
            if (qitem == NULL) {
                break;
            }

            g_string_append_c(tmp, ',');
        }
    }
      
    /* move to a new buffer for the return */
    result = UT_strdup(tmp->str);
    g_string_free(tmp, TRUE);
    return result;
}

/* QC_environ_to_string() */
/*++++++++++++++++++++++++++++++++++++++
  Convert the query_environ to a string.

  Query_environ *query_environ The query_environ to be converted.
   
  More:
  +html+ <PRE>
  Authors:
        ottrey
  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
char *
QC_environ_to_string(Query_environ qe) 
{
  char *sources;
  char passed_addr[IP_ADDRSTR_MAX];
  GString *tmp;
  char *result;

  /* convert the sources and the passed address (if any) to printable strings */
  sources = qc_sources_list_to_string(qe.sources_list);
  if( IP_addr_b2a( &(qe.pIP), passed_addr, IP_ADDRSTR_MAX) != IP_OK ) { 
    passed_addr[0] = '\0';
  }
  
  /* format the environment info */
  tmp = g_string_sized_new(STR_L);
  g_string_sprintf(tmp, 
                   "host=%s, keep_connection=%s, sources=%s, version=%s%s%s", 
                   qe.condat.ip, 
                   qe.k ? "on" : "off", 
                   sources, 
                   (qe.version == NULL) ? "?" : qe.version,
                   passed_addr[0] == '\0' ? "" : ", passedIP=",
                   passed_addr);
  
  /* move result to return buffer, and free up memory */
  result = UT_strdup(tmp->str);
  g_string_free(tmp, TRUE);
  UT_free(sources);

  return result;
  
} /* QC_environ_to_string() */

/* QC_query_command_to_string() */
/*++++++++++++++++++++++++++++++++++++++
  Convert the query_command to a string.

  Query_command *query_command The query_command to be converted.
   
  More:
  +html+ <PRE>
  Authors:
        ottrey
  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
char *QC_query_command_to_string(Query_command *query_command) {
  char result_buf[STR_XL];
  char *str1;
  char *str2;
  char *str3;

  str1 = MA_to_string(query_command->inv_attrs_bitmap, DF_get_attribute_names());
  str2 = MA_to_string(query_command->object_type_bitmap, DF_get_class_names());
  str3 = WK_to_string(query_command->keytypes_bitmap);
  
  sprintf(result_buf, "Query_command : inv_attrs=%s, recursive=%s, object_type=%s, (c=%s,e=%d,g=%d,l=%d,m=%d,q=%d,t=%d,v=%d,x=%d,F=%d,K=%d,L=%d,M=%d,R=%d,S=%d), possible keytypes=%s, keys=[%s]",
          str1,
	  query_command->recursive?"y":"n",
          str2,
          query_command->c_irt_search ? "TRUE" : "FALSE",
          query_command->e,
          query_command->g,
          query_command->l,
          query_command->m,
          query_command->q,
          query_command->t,
          query_command->v,
          query_command->x,
          query_command->fast,
          query_command->filtered,
          query_command->L,
          query_command->M,
          query_command->R,
          query_command->S,
          str3,
          query_command->keys);
  UT_free(str1);
  UT_free(str2);
  UT_free(str3);

  return UT_strdup(result_buf);
  
} /* QC_query_command_to_string() */

/* log_command() */
/*++++++++++++++++++++++++++++++++++++++
  Log the command.
  This is more to do with Tracing.  And should/will get merged with a tracing
  module (when it is finalized.)

  char *query_str
  
  Query_command *query_command
   
  More:
  +html+ <PRE>
  Authors:
        ottrey
  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
static void log_command(char *query_str, Query_command *query_command) {
  char *str;

  if( ER_is_traced(FAC_QC, ASP_QC_BUILD) ) {
    str = QC_query_command_to_string(query_command);
    ER_dbg_va(FAC_QC, ASP_QC_BUILD,
	      "query=[%s]   %s", query_str, str);
    UT_free(str);
  }
} /* log_command() */

/* QC_environ_free() */
/*++++++++++++++++++++++++++++++++++++++
  Free the query_environ.

  Query_command *qc query_environ to be freed.

  More:
  +html+ <PRE>
  Authors:
        ottrey
  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
void QC_environ_free(Query_environ *qe) {
  if (qe != NULL) {
    UT_free(qe->version);

    if (qe->sources_list != NULL) {
      g_list_free(qe->sources_list); 
      qe->sources_list=NULL;
    }
    UT_free(qe);
  }
} /* QC_environ_free() */

/* QC_free() */
/*++++++++++++++++++++++++++++++++++++++
  Free the query_command.

  Query_command *qc query_command to be freed.

  XXX I'm not sure the bitmaps will get freed.
  qc->inv_attrs_bitmap
  qc->object_type_bitmap
  qc->keytypes_bitmap

  More:
  +html+ <PRE>
  Authors:
        ottrey
  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
void QC_free(Query_command *qc) {
  if (qc != NULL) {
    if (qc->keys != NULL) {
      UT_free(qc->keys);
    }
    UT_free(qc);
  }
} /* QC_free() */



/* QC_fill() */
/*++++++++++++++++++++++++++++++++++++++
  Create a new query_command.
  Returns 0 when OK, -1 when query incorrect.
  
  char *query_str The garden variety whois query string.

  Query_environ *qe the environment

  More:
  +html+ <PRE>
  Authors:
        ottrey - original code
	marek - modified for my getopts, multiple sources;
	        and generally cleaned.
  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
static
int QC_fill(char *query_str, 
	     Query_command *query_command,
	     Query_environ *qe) {
  
  int c;
  int synerrflg = 0;
  int badparerr = 0;
  int minusk = 0;
  char *inv_attrs_str = NULL;
  char *object_types_str = NULL;
  int opt_argc;
  gchar **opt_argv;
  char *value;
/*  char *tmp_query_str;*/
  unsigned key_length;
  int i;
  int index;
  C_Type_t type;
  A_Type_t attr;
  getopt_state_t *gst = NULL;

  query_command->c_irt_search = FALSE;
  query_command->d = 0;
  query_command->e = 0;
  query_command->g = 0;
  query_command->inv_attrs_bitmap = MA_new(MA_END);
  query_command->recursive = 1;  /* Recursion is on by default. */
  query_command->l = 0;
  query_command->m = 0;
  query_command->q = -1;
  query_command->t = -1;
  query_command->v = -1;
  query_command->x = 0;
  query_command->fast = 0;
  query_command->filtered = 0;
  query_command->L = 0;
  query_command->M = 0;
  query_command->R = 0;
  query_command->S = 0;

  /* XXX UGLY - "all zeros" in object_type_bitmap means the same as 
     "all ones". To limit the inconsistency, this is changed at the end 
     of this function, so outside "all zeros" is an illegal value. */
  query_command->object_type_bitmap = MA_new(MA_END);
  /*
  query_command->keytypes_bitmap = MA_new(MA_END);
  */
  query_command->keys = NULL;

  /* This is so Marek can't crash me :-) */
  /* Side Effect - query keys are subsequently cut short to STR_S size. */

/*  tmp_query_str = (char *)UT_calloc(1, STR_S+1);
  strncpy(tmp_query_str, query_str, STR_S);*/

  /* Create the arguments. */
  /* This allows only a maximum of MAX_OPT_ARG_C words in the query. */
/*  opt_argv = g_strsplit(tmp_query_str, " ", MAX_OPT_ARG_C);*/
  opt_argv = g_strsplit(query_str, " ", MAX_OPT_ARG_C);

  /* Determine the number of arguments. */
  for (opt_argc=0; opt_argv[opt_argc] != NULL; opt_argc++);

  dieif( (gst = mg_new(0)) == NULL );
  
  while ((c = mg_getopt(opt_argc, opt_argv, "acdegi:klrmq:s:t:v:xFKLMRST:V:", 
			gst)) != EOF) {
    switch (c) {
      case 'a':
        /* Remove any user specified sources from the sources list. */
	/* free the list only, do not touch the elements */
	g_list_free(qe->sources_list); 
	qe->sources_list=NULL;

        /* Add all the config sources to the sources list. */
	{
	  int i;
	  ca_dbSource_t *hdl;
	  
	  for (i=0; (hdl = ca_get_SourceHandleByPosition(i)) != NULL; i++) {
	    qe->sources_list = g_list_append(qe->sources_list, (void *)hdl);
	  }
	}


      break;

      case 'c':
        query_command->c_irt_search = TRUE;
        break;

      case 'e':
        query_command->e=1;
      break;

      case 'd':
        query_command->d=1;
      break;

      case 'g':
        query_command->g=1;
      break;

      case 'i':
        if (gst->optarg != NULL) {
	  char *hackstr = NULL;

          inv_attrs_str = gst->optarg;
          /* 
             Now a really stupid hard-coded hack to support "pn" being a
             synonym for "ac,tc,zc,ah".  I particularly object to this because
             it references attributes that should only be defined in XML - but
             I don't see a simplier more robust way of doing this hack.
             :-( - ottrey 8/12/99 

	     ** removed a memory leak - MB, 1/08/00

             ** removed the use of "ro", added "person" - shane, 2002-01-23

	     */
          if (   strcmp(inv_attrs_str, "pn") == 0 
	      || strcmp(inv_attrs_str, "person") == 0) {
            hackstr = UT_strdup("ac,tc,zc,ah,cn");
	    inv_attrs_str = hackstr;
          }
          while (*inv_attrs_str) {
            index = getsubopt(&inv_attrs_str, DF_get_attribute_aliases(), &value);
            if (index == -1) {
	      /* Unknown attribute encountered. */
	      char *rep = ca_get_qc_badattr ;
              SK_cd_puts(&(qe->condat), rep);
	      UT_free(rep);

              attr = -1;
              badparerr++;
            }
            else {
              mask_t inv_attr_mask = MA_new(INV_ATTR_MASK);
              attr = DF_get_attribute_index(index);
              if ( MA_isset(inv_attr_mask, attr) == 1 ) {
                /* Add the attr to the bitmap. */
                MA_set(&(query_command->inv_attrs_bitmap), attr, 1);
              }
              else {
	        /* "%s" is not an inverse searchable attribute. */
	        char *rep = ca_get_qc_fmt_attrnotinv ;
	        SK_cd_printf(&(qe->condat), rep, 
		    (DF_get_attribute_aliases())[index]);
                UT_free(rep);
                badparerr++;
              }
            } 
          } /* while () */

	  if( hackstr != NULL) {
	    UT_free(hackstr);
	  }
        } /* if () */
      break;

      case 'k':
	minusk = 1;
      break;

      case 'r':
        query_command->recursive=0;       /* Unset recursion */
      break;

      case 'l':
        query_command->l=1;
      break;

      case 'm':
        query_command->m=1;
      break;

      case 'q':
        if (gst->optarg != NULL) {
          index = getsubopt(&gst->optarg, DF_get_server_queries(), &value);
          if (index == -1) {
            synerrflg++;
          }
          else {
            query_command->q = index;
          } 
        } /* if () */
      break;

      case 's':
        if (gst->optarg != NULL) {
	  char *token, *cursor = gst->optarg;
	  ca_dbSource_t *handle;
	  
          /* Remove any sources from the sources list. */
	  g_list_free(qe->sources_list); 
	  qe->sources_list=NULL;
	  
	  /* go through specified sources */
	  while( (token = strsep( &cursor, "," )) != NULL ) {
	    
	    if( (handle = ca_get_SourceHandleByName(token)) != NULL ) {
	      /* append */
	      qe->sources_list 
		= g_list_append(qe->sources_list, (void *) handle );
	    }
	    else {
	      /* bail out */
	      
	      /* Unknown source %s requested. */
	      char *rep = ca_get_qc_fmt_badsource ;
	      SK_cd_printf(&(qe->condat), rep, token ); 
	      UT_free(rep);
	      
	      /* XXX error */
	      badparerr++;
	      
	    } /* if handle not null */
	  } /* while sources */
        } /* if argument present */
	break;
	
      case 't':
        if (gst->optarg != NULL) {
          object_types_str = gst->optarg;
          while (*object_types_str) {
            index = getsubopt(&object_types_str, DF_get_class_aliases(), &value);
            if (index == -1) {
	      /* Unknown object type encountered */
	      char *rep = ca_get_qc_badobjtype ;
              SK_cd_puts(&(qe->condat), rep);
	      UT_free(rep);
              badparerr++;
            }
            else {
              type = DF_get_class_index(index);
              query_command->t=type;
            }
          }
        }
      break;

      case 'v':
        if (gst->optarg != NULL) {
          object_types_str = gst->optarg;
          if (*object_types_str) {
            index = getsubopt(&object_types_str, DF_get_class_aliases(), &value);
            if (index == -1) {
	      /* Unknown object type encountered */
	      char *rep = ca_get_qc_badobjtype ;
              SK_cd_puts(&(qe->condat), rep);
	      UT_free(rep);
              badparerr++;
            }
            else {
              type = DF_get_class_index(index);
              query_command->v=type;
            }
          }
        }
      break;

      case 'x':
        query_command->x=1;
      break;

      case 'F':
        query_command->fast=1;
	query_command->recursive=0; /* implies no recursion */
      break;

      case 'K':
        query_command->filtered=1;
	query_command->recursive=0; /* implies no recursion */
      break;

      case 'L':
        query_command->L=1;
      break;

      case 'M':
        query_command->M=1;
      break;

      case 'R':
        query_command->R=1;
      break;

      case 'S':
        query_command->S=1;
      break;

      case 'T':
        if (gst->optarg != NULL) {
	  /* parse the specification */
          object_types_str = gst->optarg;
          while (*object_types_str) {
            index = getsubopt(&object_types_str, DF_get_class_aliases(), &value);
            if (index == -1) {
	      /* Unknown object type encountered */
	      char *rep = ca_get_qc_badobjtype ;
              SK_cd_puts(&(qe->condat), rep);
	      UT_free(rep);
              badparerr++;
            }
            else {
              type = DF_get_class_index(index);
              /* Add the type to the bitmap. */
              MA_set(&(query_command->object_type_bitmap), (unsigned) type, 1);
            }
          }
        }
      break;

      case 'V':
        {
          char *token, *cursor = gst->optarg;
          while( (token = strsep( &cursor, "," )) != NULL ) {
            if(IP_addr_e2b( & (qe->pIP), token) 
               != IP_OK ) {
              /* means it was not an IP -> it was a version */
              UT_free(qe->version);
              qe->version = UT_strdup(token);
            }
          }
        }
      break;

      /* any other flag, including '?' and ':' errors */
      default:
        synerrflg++;
    }
  }

  /* copy the key */

  /* Work out the length of space needed */
  key_length = 1; /* for terminal '\0' */
  for (i=gst->optind ; i < opt_argc; i++) {
    /* length for the string + 1 for the '\0'+ 1 for the ' ' */
    if (opt_argv[i] != NULL) {
      key_length += strlen(opt_argv[i])+1;
    }
  }
  /* allocate */
  query_command->keys = (char *)UT_calloc(1, key_length+1);

  /* copy */
  for (i=gst->optind; i < opt_argc; i++) {
    strcat(query_command->keys, opt_argv[i]);
    if ( (i + 1) < opt_argc) {
      strcat(query_command->keys, " ");
    }
  }
    
  /* if no error, process the key, otherwise don't bother */
  if ( ! synerrflg && ! badparerr ) { 
    /* convert the key to uppercase. */
    for (i=0; i <= key_length; i++) {
      query_command->keys[i] = toupper(query_command->keys[i]);
    }
    
    /* make the keytypes_bitmap. */
    query_command->keytypes_bitmap = WK_new(query_command->keys);

    /* fix the object type bitmap - turn "all zeros" into "all ones" */
    if( MA_bitcount(query_command->object_type_bitmap) == 0 ) {
      query_command->object_type_bitmap = MA_not(query_command->object_type_bitmap);
    }
   
    /* -d handling: if the keytype is IPv4/v6 address/prefix/range, then
       exclude the domains unless -d is set 
       XXX this must be kept in sync with new types */
    if( query_command->d == 0 
	&& ( MA_isset(query_command->keytypes_bitmap, WK_IPADDRESS) 
	     || MA_isset(query_command->keytypes_bitmap, WK_IPRANGE )
	     || MA_isset(query_command->keytypes_bitmap, WK_IPPREFIX )
	     || MA_isset(query_command->keytypes_bitmap, WK_IP6PREFIX )
	     ) ) {
      
      MA_set(&(query_command->object_type_bitmap), C_DN , 0);
    }

    /* tracing */
/*    log_command(tmp_query_str, query_command);*/
    log_command(query_str, query_command);

    /* "keep connection" processing:
       when opening connection, -k may be alone or with a query
       later -k must appear alone (or there must be an empty line,
       or an error) for the connection to close.
    */
    if( minusk ) {
      if( qe->k == 0 ) { /* opening */ 
	qe->k = 1;
      }
      else { /* closing, if no key; otherwise keep open */
	if( key_length <= 1 ) {
	  qe->k = 0;
	}
      }
    }
    
  } /* if no error */

  /* we don't need this anymore */
/*  UT_free(tmp_query_str);*/
  UT_free(gst);
  g_strfreev(opt_argv);

  if(synerrflg > 0) { /* severe syntax error. Usage must be printed */
    return QC_SYNERR;
  }
  else if(badparerr > 0) { /* the requester has a clue. No Usage info */
    return QC_PARERR;
  }
  else {
    return 0;
  }
} /* QC_fill() */

/* QC_environ_new() */
/*++++++++++++++++++++++++++++++++++++++
  Create a new query environment.

  More:
  +html+ <PRE>
  Authors:
        ottrey
  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
Query_environ *QC_environ_new(char *ip, int sock) {
  Query_environ *qe;

  qe = (Query_environ *)UT_calloc(1, sizeof(Query_environ));
  qe->condat.ip = ip;
  qe->condat.sock = sock;

  /* The source is initialized to include only the deflook sources */
  {
    int i;
    ca_dbSource_t *hdl;
    
    for (i=0; (hdl = ca_get_SourceHandleByPosition(i)) != NULL; i++) {
      char *amrmrulez = ca_get_srcdeflook(hdl);
      if( strcmp(amrmrulez, "y")==0 ) {
	qe->sources_list = g_list_append(qe->sources_list, (void *)hdl);
      }
      UT_free(amrmrulez);
    }
  }
  
  return qe;

} /* QC_environ_new() */




/* QC_create() */
/*++++++++++++++++++++++++++++++++++++++
  try to parse the query and fill in the QC struct, setting 
  qc->query_type accordingly.
  
  Query_command *QC_create    returns allocated structure
  
  char *input                 user query
  
  Query_environ *qe           query environment structure
  
  Author: 
    marek.
  
  ++++++++++++++++++++++++++++++++++++++*/

Query_command *QC_create(char *input, Query_environ *qe)
{
  Query_command *qc;
  /* allocate place for a copy of the input */
  char *copy = UT_calloc(1,strlen(input)+1); 
  unsigned char *ci, *co;
  int qt;
  /* clean the string from junk - allow only known chars, something like
     tr/A-Za-z0-9\-\_\:\+\=\.\,\@\/ \n//cd; 

     strip leading spaces too
  */

  for(ci = (unsigned char *)input; *ci != 0 && isspace(*ci); ci++) {
    /* EMPTY */
  }

  for(co = (unsigned char *) copy; *ci != 0; ci++) {
    if( strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"     /* only those are allowed */
	       "abcdefghijklmnopqrstuvwxyz"
	       "0123456789-_:+=.,@/?' \n", *ci) != NULL) {
      *(co++) = *ci;
    }
  }

  /* now delete whitespace chars at the end */
  while( co != (unsigned char *)copy /* don't read past the beginning */
	 && isspace(*co) ) {
    *co = '\0';
    co--;
  }


  qc = (Query_command *)UT_calloc(1, sizeof(Query_command));
  
  if ( strlen(copy) == 0) {
    /* An empty query (Ie return) was sent */
    qc->query_type = QC_EMPTY;
  } 
  else {        /* else <==> input_length > 0 ) */
    /* parse query */
    qt = QC_fill(copy, qc, qe);

    if( qt == QC_SYNERR || qt == QC_PARERR ) {
      qc->query_type = qt;
    }
    else {
      /* Update the query environment */
      /* qe = QC_environ_update(qc, qe); */

      /* Only do a query if there are keys. */
      if (qc->keys == NULL || strlen(qc->keys) == 0 ) {
	if( strlen(qc->keys) == 0 
	    && ( qc->q != -1 || qc->t != -1 || qc->v != -1 ) ) {
	  qc->query_type = QC_TEMPLATE;
	}
	else {
	  qc->query_type = QC_NOKEY;
	}
      }
      else {
	if ( strcmp(qc->keys, "HELP") == 0 ) {
	  qc->query_type = QC_HELP;
	}
	/* So, a real query */
	else if( qc->filtered ) {
	  qc->query_type = QC_FILTERED;
	}
	else {
	  qc->query_type = QC_REAL;
	}
      }
    }
  }

  UT_free(copy);

  return qc;
}


/*++++++++++++++++++++++++++++++++++++++

  Get the name of the given query type code.

  char *QC_get_qrytype   returns a pointer to an element of array of static strings

  qc_qtype_t qrytype     query type code

  ++++++++++++++++++++++++++++++++++++++*/

char *QC_get_qrytype(qc_qtype_t qrytype) {
  dieif(qrytype >= QC_TYPE_MAX);

  return qrytype_str[qrytype];
}
