modules/qc/query_command.c

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

FUNCTIONS

This source file includes following functions.
  1. QC_bitmap_to_string
  2. my_getopt
  3. QC_query_command_to_string
  4. log_command
  5. QC_free
  6. QC_new
  7. QC_environ_update

/***************************************
  $Revision: 1.10 $

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

  Status: NOT REVUED, NOT TESTED

  ******************/ /******************
  Filename            : query_command.c
  Author              : ottrey@ripe.net
  OSs Tested          : Solaris
  To Do               : Write some kind of options parser (to check for valid
                        combinations of options.)
  Comments            :
  ******************/ /******************
  Copyright (c) 1999                              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.
  ***************************************/
#include <stdlib.h>
#include <stdio.h>

#include "query_command.h"
#include "objects.h"
#include "constants.h"
#include "which_keytypes.h"

#define MAX_OPT_ARG_C 20

/*+ String sizes +*/
#define STR_S   63
#define STR_M   255
#define STR_L   1023
#define STR_XL  4095
#define STR_XXL 16383

/* XXX These probably wont get used.  I'm using a switch statement instead.  -ottrey 5/7/99 */
/*
mask_t Inv_attr_mask;
mask_t Object_mask;
*/


/* QC_bitmap_to_string() */
/*++++++++++++++++++++++++++++++++++++++
  Convert the bitmap of attributes used in this query_command to a string.

  mask_t bitmap The bitmap of attribute to be converted.
   
  More:
  +html+ <PRE>
  Authors:
        ottrey
  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
char *QC_bitmap_to_string(mask_t bitmap) {
/* [<][>][^][v][top][bottom][index][help] */

  return MA_to_string(bitmap, AT_get_attributes(), DUP_TOKENS, 0);

} /* QC_bitmap_to_string() */

/* my_getopt() */
/*++++++++++++++++++++++++++++++++++++++
  A thread safe version of getopt, used to get the options from the whois
  query.

  int opt_argc The number of query arguments.
  
  char **opt_argv The query arguments.
  
  char *optstring The string containing valid options.
  
  int *my_optind_ptr A pointer to the index into the options of the option
  returned.
  
  char **my_optarg_ptr A pointer to the arguments to be returned.
   
  More:
  +html+ <PRE>
  Authors:
        ottrey
  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+     <LI>man getopt
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
static int my_getopt(int opt_argc, char **opt_argv, char *optstring, int *my_optind_ptr, char **my_optarg_ptr) {
/* [<][>][^][v][top][bottom][index][help] */
  int c='?';
  int i, j;
  int no_options;
  int optind = *my_optind_ptr;
  char option[3];
  int option_matched=0;
  
  /* Get the number of options in the option string */
  for(i=0, no_options=0; i < strlen(optstring) ; i++) {
    if (optstring[i] != ':') {
      no_options++;
    }
  }

  /* Iterate through all the option until it matches the current opt_argv */
  /* Ie. opt_argv[optind] */
  for (i=0, j=0; i <= no_options; i++, j++) {
    /* Construct one option from the optstring */
    option[0] = '-';
    if (optstring[j] == ':') {
      j++;
    }
    option[1] = optstring[j];
    if ( optstring[j+1] == ':' ) {
      option[2] = ':';
    }
    else {
      option[2] = '\0';
    }
    option[3] = '\0';

    if (optind < opt_argc) {
      if (strlen(opt_argv[optind]) > 0) {
        /*
        printf("opt_argv[%d] == option <==> %s == %s\n", optind, opt_argv[optind], option);
        */
        if (strncmp(opt_argv[optind], option, 2) == 0) {
          /* Does the option have arguments. */
          if (option[2] == ':') {
            /* If the option has arguments */
            if (strlen(opt_argv[optind]) > 2) {
              /* If the arguments are in this token */
              *my_optarg_ptr = (opt_argv[optind])+2;
            }
            else {
              /* If the arguments are in the next token */
              *my_optarg_ptr = opt_argv[optind+1];
              optind++;
            }
          }
          else {
            /* There are no arguments to this token */
            *my_optarg_ptr = NULL;
          }
          /* Option matched - break out of the search */
          option_matched = 1;
          break;
        }
      }
    }
  } /* for() */
  
  if ( option_matched == 1 ) {
    /* This option was matched, return it. */
    c = option[1];

    /* Move to the next opt_argv */
    optind++;
    *my_optind_ptr = optind;
  }
  else {
    /* Discontinue search */
    c = EOF;
  }

  return c;

} /* my_getopt() */

/* 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) {
/* [<][>][^][v][top][bottom][index][help] */
  char *result;
  char result_buf[STR_XL];
  char *str1;
  char *str2;
  char *str3;
  char *str4;

  str1 = QC_bitmap_to_string(query_command->inv_attrs_bitmap);
  str2 = QC_bitmap_to_string(query_command->object_type_bitmap);
  str3 = WK_to_string(query_command->keytypes_bitmap);
  str4 = AT_sources_list_to_string(query_command->sources_list);
  sprintf(result_buf, "Query_command : recursive=%d, inv_attrs=%s, k=%d, object_type=%s, sources=%s, (a=%d,g=%d,l=%d,m=%d,t=%d,v=%d,F=%d,L=%d,M=%d,R=%d,S=%d,V=%d), possible keytypes=%s, keys=[%s]\n",
          query_command->recursive,
          str1,
          query_command->k,
          str2,
          str4,
          query_command->a,
          query_command->g,
          query_command->l,
          query_command->m,
          query_command->t,
          query_command->v,
          query_command->F,
          query_command->L,
          query_command->M,
          query_command->R,
          query_command->S,
          query_command->V,
          str3,
          query_command->keys);
  free(str1);
  free(str2);
  free(str3);
  free(str4);

  result = (char *)calloc(1, strlen(result_buf)+1);
  strcpy(result, result_buf);

  return result;
  
} /* 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) {
/* [<][>][^][v][top][bottom][index][help] */
  FILE *logf;
  char *str;

  if (CO_get_comnd_logging() == 1) {
    str = QC_query_command_to_string(query_command);
    if (strcmp(CO_get_comnd_logfile(), "stdout") == 0) {
      printf("query=[%s]\n%s", query_str, str);
    }
    else {
      logf = fopen(CO_get_comnd_logfile(), "a");
      fprintf(logf, "query=[%s]\n%s", query_str, str);
      fclose(logf);
    }
    free(str);
  }

} /* log_command() */

/* 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) {
/* [<][>][^][v][top][bottom][index][help] */
  if (qc != NULL) {
    if (qc->keys != NULL) {
      free(qc->keys);
    }

    g_list_free(qc->sources_list);

    free(qc);
  }
} /* QC_free() */

/* QC_new() */
/*++++++++++++++++++++++++++++++++++++++
  Create a new query_command.

  char *query_str The garden variety whois query string.

  int sock The client socket.

  Pre-condition: OB_init() must be called before this.
                 Ie the objects have to be created first.

  XXX sock shouldn't be passed here.  But it needs to in order to report errors to the client.
  Doh!.... this needs some looking into.
   
  More:
  +html+ <PRE>
  Authors:
        ottrey
  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
Query_command *QC_new(char *query_str, int sock) {
/* [<][>][^][v][top][bottom][index][help] */
  char *my_optarg;
  int my_optind;
  int c;
  int errflg = 0;
  char *inv_attrs_str = NULL;
  char *object_types_str = NULL;
  char *sources_str = NULL;
  int opt_argc;
  char *opt_argv[MAX_OPT_ARG_C];
  char *value;
  char *tmp_query_str;
  char tmp_query_str_buf[STR_L];
  int key_length;
  int i;

  Query_command *query_command;

  int index;
  int type;
  int attr;
  int offset;

  char *str;
  char str_buf[STR_XL];

  query_command = (Query_command *)calloc(1, sizeof(Query_command)+1);
  query_command->a = 0;
  query_command->g = 0;
  query_command->inv_attrs_bitmap = MA_new(MA_END);
  query_command->k = 0;
  query_command->recursive = 1;
  query_command->l = 0;
  query_command->m = 0;
  /* The 0th source is initialized by default. */
  query_command->sources_list = g_list_append(query_command->sources_list, (void *)AT_get_source(0));
  query_command->t = 0;
  query_command->v = 0;
  query_command->F = 0;
  query_command->L = 0;
  query_command->M = 0;
  query_command->R = 0;
  query_command->S = 0;
  query_command->object_type_bitmap = MA_new(OBJECT_MASK);
  query_command->V = 0;
  /*
  query_command->keytypes_bitmap = MA_new(MA_END);
  */
  query_command->keys = NULL;

  my_optind=1;

  /* This is so Marek can't crash me :-) */
  /* Side Effect - query keys are subsequently cut short to STR_L size. */
  /*
  tmp_query_str = (char *)calloc(1, strlen(query_str));
  strcpy(tmp_query_str, query_str, STR_L);
  */
  strncpy(tmp_query_str_buf, query_str, STR_L-1);
  tmp_query_str_buf[STR_L-1] = '\0';

  opt_argv[0] = NULL;
  opt_argv[1] = (char *)strtok(tmp_query_str_buf, " ");
  opt_argc = 2;
  while ( (opt_argv[opt_argc] = (char *)strtok(NULL, " ")) != NULL ) {
    opt_argc++;
    /* I really would like to put this statement in the while clause, but
       I don't think we can be sure which order it will execute the two
       parts in.  - So I'll leave it here. */
    if ( opt_argc >= MAX_OPT_ARG_C ) break;
  }
  opt_argv[opt_argc] = NULL;


  while ((c = my_getopt(opt_argc, opt_argv, "agi:klrms:t:v:FLMRST:V", &my_optind, &my_optarg)) != EOF) {
    switch (c) {
      case 'a':
        /* The 0th source is initialized already by default. - so don't add it again. */
        /* Add the rest of the sources to the list. */
        for (i=1; AT_get_source(i) != NULL; i++) {
          query_command->sources_list = g_list_append(query_command->sources_list, (void *)AT_get_source(i));
        }
      break;

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

      case 'i':
        if (my_optarg != NULL) {
          inv_attrs_str = my_optarg;
          while (*inv_attrs_str) {
            index = getsubopt(&inv_attrs_str, AT_get_attributes(), &value);
            if (index == -1) {
              attr = -1;
              strcpy(str_buf, "");
              sprintf(str_buf, "Unkown attribute encountered.\n"); 
              SK_puts(sock, str_buf);
              errflg++;
            }
            else {
              attr = index/DUP_TOKENS;
              if ( MA_isset(OB_get_inv_attr_mask(), attr) == 1 ) {
                /* Add the attr to the bitmap. */
                MA_set(&(query_command->inv_attrs_bitmap), attr, 1);
              }
              else {
                strcpy(str_buf, "");
                sprintf(str_buf, "\"%s\" does not belong to inv_attr.\n", (AT_get_attributes())[index]); 
                SK_puts(sock, str_buf);
                errflg++;
              }
            } 
          } /* while () */
        } /* if () */
      break;

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

      case 'r':
        query_command->recursive = 0;
      break;

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

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

      case 's':
        if (my_optarg != NULL) {
          sources_str = my_optarg;
          /* The 0th source is initialized already by default. - so remove it first. */
          query_command->sources_list = g_list_remove(query_command->sources_list, (void *)AT_get_source(0));
          while (*sources_str) {
            index = getsubopt(&sources_str, AT_get_sources(), &value);
            if (index == -1) {
              strcpy(str_buf, "");
              sprintf(str_buf, "Unkown source encountered.\nNot one of: %s\n", AT_sources_to_string()); 
              SK_puts(sock, str_buf);
              errflg++;
            }
            else {
              query_command->sources_list = g_list_append(query_command->sources_list, (void *)AT_get_source(index));
            } 
          } /* while () */
        } /* if () */
        /*
        query_command->s=1;
        */
      break;

      case 't':
        if (my_optarg != NULL) {
          object_types_str = my_optarg;
          while (*object_types_str) {
            index = getsubopt(&object_types_str, AT_get_attributes(), &value);
            if (index == -1) {
              strcpy(str_buf, "");
              sprintf(str_buf, "Unkown object encountered.\n"); 
              SK_puts(sock, str_buf);
              errflg++;
            }
            else {
              type = index/DUP_TOKENS;
              query_command->t=type;
            }
          }
        }
      break;

      case 'v':
        if (my_optarg != NULL) {
          object_types_str = my_optarg;
          if (*object_types_str) {
            index = getsubopt(&object_types_str, AT_get_attributes(), &value);
            if (index == -1) {
              strcpy(str_buf, "");
              sprintf(str_buf, "Unkown object encountered.\n"); 
              SK_puts(sock, str_buf);
              errflg++;
            }
            else {
              type = index/DUP_TOKENS;
              query_command->v=type;
            }
          }
        }
      break;

      case 'F':
        query_command->F=1;
      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':
        /* XXX This is a bit tricky.
           The bits are initialized to be all set.
           Then each encountered bit is unset.
           Finally the result is "not'ed by using XOR with the original. */
        if (my_optarg != NULL) {
          mask_t tmp = MA_new(OBJECT_MASK);
          mask_t original = MA_new(OBJECT_MASK);
          object_types_str = my_optarg;
          while (*object_types_str) {
            index = getsubopt(&object_types_str, AT_get_attributes(), &value);
            if (index == -1) {
              strcpy(str_buf, "");
              sprintf(str_buf, "Unkown attribute encountered.\n"); 
              SK_puts(sock, str_buf);
              errflg++;
            }
            else {
              type = index/DUP_TOKENS;
              if ( MA_isset(OB_get_object_mask(), type) == 1 ) {
                /* Add the type to the bitmap. */
                MA_set(&tmp, type, 0);
              }
              else {
                strcpy(str_buf, "");
                sprintf(str_buf, "\"%s\" does not belong to object_type.\n", (AT_get_attributes())[index]); 
                SK_puts(sock, str_buf);
                errflg++;
              }
            }
          }
          query_command->object_type_bitmap = MA_xor(original, tmp);
          MA_free(&original);
          MA_free(&tmp);
        }
      break;

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

      case '?':
        errflg++;
      break;

      default:
        errflg++;
    }
  }

  /* XXX Report the error.  This could be improved. */
  if (opt_argv[my_optind] != NULL) {
    if ( (errflg) || (strncmp(opt_argv[my_optind], "-", 1) == 0) ) {
      strncpy(str_buf, USAGE, STR_XL-1);
      SK_puts(sock, str_buf);
    }
  }
  else {
    if (errflg) {
      strncpy(str_buf, USAGE, STR_XL-1);
      SK_puts(sock, str_buf);
    }
  }
    

  /* Work out the length of space needed */
  key_length = 0;
  for (i=my_optind ; i < opt_argc; i++) {
    /* length for the string + 1 for the '\0'+ 1 for the ' ' + 1 for good luck. */
    if (opt_argv[i] != NULL) {
      key_length += strlen(opt_argv[i])+3;
    }
  }

  query_command->keys = (char *)calloc(1, key_length+1);
  strcpy(query_command->keys, "");
  if (errflg == 0) {
    for (i=my_optind; i < opt_argc; i++) {
      strcat(query_command->keys, opt_argv[i]);
      if ( (i + 1) < opt_argc) {
        strcat(query_command->keys, " ");
      }
    }
  } /* XXX - Be careful about where this brace goes. */

  /* Now we don't need this anymore */
  /*
  free(tmp_query_str);
  */

  /* Now convert the key to uppercase. */
  for (i=0; i <= key_length; i++) {
    query_command->keys[i] = toupper(query_command->keys[i]);
  }

  /* Now make the keytypes_bitmap. */
  query_command->keytypes_bitmap = WK_new(query_command->keys);

  if ( CO_get_comnd_logging() == 1 ) {
    log_command(query_str, query_command);
  }

  return query_command;

} /* QC_new() */

/* QC_environ_update() */
/*++++++++++++++++++++++++++++++++++++++
  Update the query environment.
  Return the new environment?
  This will include things like setting recursion, and the sources, and other
  options deemed to be of an environmental type.
  XXX This hasn't been implemented yet.  Haven't quite decided what to do here
  yet.
  (I personally don't like functions that change two things at once, so that
  may also be looked into aswell.)

  Query_command *qc The query command to be updated.
  
  Query_command *qe The current environment.
   
  More:
  +html+ <PRE>
  Authors:
        ottrey
  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
Query_command *QC_environ_update(Query_command *qc, Query_command *qe) {
/* [<][>][^][v][top][bottom][index][help] */
  Query_command *query_command;

  query_command = (Query_command *)calloc(1, sizeof(Query_command)+1);

  query_command->recursive = 1;

  return query_command;

} /* QC_environ_update() */

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