## modules/qc/query_command.c

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

## FUNCTIONS

This source file includes following functions.

1. my_getopt
2. QC_environ_to_string
3. QC_query_command_to_string
4. log_command
5. QC_environ_free
6. QC_free
7. QC_new
8. QC_environ_new
9. QC_environ_update

    /***************************************
      $Revision: 1.14 $

      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 <string.h>

    #include "query_command.h"
    #include "defs.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;
    */

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

      str1 = DF_sources_list_to_string(qe.sources_list);

      if (qe.V == NULL) {
        sprintf(result_buf, "keep_connection=%s, recursion=%s, sources=%s, version=%s", qe.k?"on":"off", qe.recursive?"on":"off", str1, "?");
      }
      else {
        sprintf(result_buf, "keep_connection=%s, recursion=%s, sources=%s, version=%s", qe.k?"on":"off", qe.recursive?"on":"off", str1, qe.V);
      }

      free(str1);

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

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

      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);
      str4 = QC_environ_to_string(query_command->qe);
      sprintf(result_buf, "Query_command : environ={%s}, inv_attrs=%s, object_type=%s, (g=%d,l=%d,m=%d,t=%d,v=%d,x=%d,F=%d,L=%d,M=%d,R=%d,S=%d), possible keytypes=%s, keys=[%s]\n",
              str4,
              str1,
              str2,
              query_command->g,
              query_command->l,
              query_command->m,
              query_command->t,
              query_command->v,
              query_command->x,
              query_command->F,
              query_command->L,
              query_command->M,
              query_command->R,
              query_command->S,
              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_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) {
    /* [<][>][^][v][top][bottom][index][help] */
      if (qe != NULL) {
        if (qe->sources_list != NULL) {
          g_list_free(qe->sources_list);
        }
        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) {
    /* [<][>][^][v][top][bottom][index][help] */
      if (qc != NULL) {
        if (qc->keys != NULL) {
          free(qc->keys);
        }

        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:

      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, Query_environ *qe) {
    /* [<][>][^][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];

      GList *first_source;

      query_command = (Query_command *)calloc(1, sizeof(Query_command)+1);
      query_command->qe = *qe;
      query_command->g = 0;
      query_command->inv_attrs_bitmap = MA_new(MA_END);
      query_command->l = 0;
      query_command->m = 0;
      query_command->t = 0;
      query_command->v = 0;
      query_command->x = 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(MA_END);
      /*
      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:xFLMRST:V:", &my_optind, &my_optarg)) != EOF) {
        switch (c) {
          case 'a':
            /* Remove any user specified sources from the sources list. */
            while ((first_source = g_list_first(query_command->qe.sources_list)) != NULL) {
              query_command->qe.sources_list = g_list_remove(query_command->qe.sources_list, first_source->data);
            }
            /* Add all the config sources to the sources list. */
            for (i=0; DF_get_source(i) != NULL; i++) {
              query_command->qe.sources_list = g_list_append(query_command->qe.sources_list, (void *)DF_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, DF_get_attribute_aliases(), &value);
                if (index == -1) {
                  attr = -1;
                  strcpy(str_buf, "");
                  sprintf(str_buf, "Unknown attribute encountered.\n");
                  SK_puts(sock, str_buf);
                  errflg++;
                }
                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 {
                    strcpy(str_buf, "");
                    sprintf(str_buf, "\"%s\" does not belong to inv_attr.\n", (DF_get_attribute_aliases())[index]);
                    SK_puts(sock, str_buf);
                    errflg++;
                  }
                  MA_free(&inv_attr_mask);
                }
              } /* while () */
            } /* if () */
          break;

          case 'k':
            /* This is a tricky XOR operation....  ;-)
               State transition for k_flag:
               0 -> 0 then k flag = 0, connected = 0
               0 -> 1 then k flag = 1, connected = 1
               1 -> 0 then k flag = 1, connected = 1
               1 -> 1 then k flag = 0, connected = 0
            */
            query_command->qe.k ^= 1;
          break;

          case 'r':
            /* Toggle recursion */
            query_command->qe.recursive ^= 1;
          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;
              /* Remove any sources from the sources list. */
              while ((first_source = g_list_first(query_command->qe.sources_list)) != NULL) {
                query_command->qe.sources_list = g_list_remove(query_command->qe.sources_list, first_source->data);
              }
              while (*sources_str) {
                index = getsubopt(&sources_str, DF_get_sources(), &value);
                if (index == -1) {
                  strcpy(str_buf, "");
                  sprintf(str_buf, "Unknown source encountered.\nNot one of: %s\n", DF_sources_to_string());
                  SK_puts(sock, str_buf);

                  /* Put the default source back in. */
                  SK_puts(sock, "Reverting to default source - ");
                  SK_puts(sock, CO_get_database());
                  SK_puts(sock, "\n");
                  query_command->qe.sources_list = g_list_append(query_command->qe.sources_list, (void *)CO_get_database());
                  errflg++;
                }
                else {
                  query_command->qe.sources_list = g_list_append(query_command->qe.sources_list, (void *)DF_get_source(index));
                }
              } /* while () */
            } /* if () */
          break;

          case 't':
            if (my_optarg != NULL) {
              object_types_str = my_optarg;
              while (*object_types_str) {
                index = getsubopt(&object_types_str, DF_get_class_aliases(), &value);
                if (index == -1) {
                  strcpy(str_buf, "");
                  sprintf(str_buf, "Unknown object encountered.\n");
                  SK_puts(sock, str_buf);
                  errflg++;
                }
                else {
                  type = DF_get_class_index(index);
                  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, DF_get_class_aliases(), &value);
                if (index == -1) {
                  strcpy(str_buf, "");
                  sprintf(str_buf, "Unknown object encountered.\n");
                  SK_puts(sock, str_buf);
                  errflg++;
                }
                else {
                  type = DF_get_class_index(index);
                  query_command->v=type;
                }
              }
            }
          break;

          case 'x':
            query_command->x=1;
          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':
            if (my_optarg != NULL) {
              object_types_str = my_optarg;
              while (*object_types_str) {
                index = getsubopt(&object_types_str, DF_get_class_aliases(), &value);
                if (index == -1) {
                  strcpy(str_buf, "");
                  sprintf(str_buf, "Unknown class encountered.\n");
                  SK_puts(sock, str_buf);
                  errflg++;
                }
                else {
                  type = DF_get_class_index(index);
                  /* Add the type to the bitmap. */
                  MA_set(&(query_command->object_type_bitmap), type, 1);
                }
              }
            }
          break;

          case 'V':
            if (query_command->qe.V != NULL) {
              /* free up the old client info */
              free(query_command->qe.V);
            }
            query_command->qe.V = (char *)calloc(1, strlen(my_optarg)+1);
            strcpy(query_command->qe.V, my_optarg);
          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_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(void) {
    /* [<][>][^][v][top][bottom][index][help] */
      Query_environ *qe;

      qe = (Query_environ *)calloc(1, sizeof(Query_environ)+1);

      /* Don't keep the connection open */
      qe->k = 0;

      /* Use recursion */
      qe->recursive = 1;

      /* Client info */
      qe->V = NULL;

      /* The source is initialized to be the one defined in the config by default. */
      qe->sources_list = g_list_append(qe->sources_list, (void *)CO_get_database());

      return qe;

    } /* QC_environ_new() */

    /* QC_environ_update() */
    /*++++++++++++++++++++++++++++++++++++++
      Update the query environment.

      This includes setting;
       - recursion
       - sources

      Query_command *qc The current query command.

      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_environ *QC_environ_update(Query_command *qc, Query_environ *qe) {
    /* [<][>][^][v][top][bottom][index][help] */

      *qe = qc->qe;

      return qe;

    } /* QC_environ_update() */

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