/* *************************************************************
   RWhois Software

   Copyright (c) 1996-2000 Network Solutions, Inc.

   See the file LICENSE for conditions of use and distribution.
**************************************************************** */

#include "rwhois.h"
#include "conf.h"
#include "strutils.h"
#include "whoisquery.h"

#define MAX_QUERY_LEN 1024

typedef struct {
  rw_query_args_struct query_args;
  rw_recurse_mode_enum recurse_mode;
  rw_recurse_mode_enum whois_recurse_mode;
  char                 *host;
  int                  port;
  int                  debug_level;
} rwhois_cl_opts_struct;


void
display_query_records(dl_list_type *record_list)
{
  int              not_done_records;
  int              not_done_pairs;
  rw_record_struct *cur_rec;
  rw_avpair_struct *cur_av;

  if (!record_list || dl_list_empty(record_list)) {
    return;
  }

  not_done_records = dl_list_first(record_list);

  /* display the actual records */
  while (not_done_records)
  {
    cur_rec = (rw_record_struct *) dl_list_value(record_list);

    /* skip broken records and referrals (they should be in the set) */
    if (!cur_rec || cur_rec->referral != NULL) {
      not_done_records = dl_list_next(record_list);
      continue;
    }

    if (cur_rec->text) {
      fputs(cur_rec->text, stdout);
      fputs("\n", stdout);
    }

    /* display the rwhois record */
    not_done_pairs = dl_list_first(cur_rec->av_pair_list);

    while (not_done_pairs)
    {
      cur_av = (rw_avpair_struct *) dl_list_value(cur_rec->av_pair_list);
      printf("%-30s%s\n", cur_av->attribute, cur_av->value);

      not_done_pairs = dl_list_next(cur_rec->av_pair_list);
    }

    not_done_records = dl_list_next(record_list);
    if (not_done_records) {
      fputs("\n", stdout);
    }
  }
}

void
display_query_referrals(rw_referral_group_struct *referral_group)
{
  int                      not_done;
  rw_referral_struct       *cur_ref;
  dl_list_type             *ref_list;

  if (!referral_group) return;

  ref_list = referral_group->referrals;
  if (!ref_list) return;

  not_done = dl_list_first(ref_list);

  while (not_done)
  {
    cur_ref = (rw_referral_struct *) dl_list_value(ref_list);
    if (!cur_ref) {
      not_done = dl_list_next(ref_list);
      continue;
    }

    if (STR_EXISTS(cur_ref->url)) {
      printf("referral: %s\n", cur_ref->url);
    } else {
      printf("referral: %s://%s:%d\n", SAFE_STR(cur_ref->scheme, ""),
             SAFE_STR(cur_ref->host, ""), cur_ref->port);
    }

    not_done = dl_list_next(ref_list);
  }
}

void
display_all_query_referrals(dl_list_type *referral_group_list)
{
  int                      not_done;
  rw_referral_group_struct *group;

  if (!referral_group_list) return;

  not_done = dl_list_first(referral_group_list);
  while (not_done) {
    group = (rw_referral_group_struct *) dl_list_value(referral_group_list);
    display_query_referrals(group);

    not_done = dl_list_next(referral_group_list);
  }
}
void
display_query_results(dl_list_type *record_list,
                      dl_list_type *referral_group_list)
{
  if (record_list) {
    display_query_records(record_list);
  }

  if (referral_group_list) {
    if (record_list) {
      fputs("\n", stdout);
    }

    display_all_query_referrals(referral_group_list);
  }
}

void
display_whois_result(rw_whois_result_struct *whoisres)
{
  if (whoisres->disclaimer) {
    fputs(whoisres->disclaimer, stdout);
  }
  if (whoisres->text) {
    fputs(whoisres->text, stdout);
  }
}

void
follow_whois_referrals(dl_list_type       *ref_group_list,
                       char               *query,
                       int                debug_level,
                       rw_response_struct *response)
{
  rw_referral_group_struct *cur_group;
  rw_referral_struct       *cur_ref;
  rw_whois_result_struct   *whois_result;
  int                      not_done_groups;
  int                      not_done_refs;

  assert(response);

  if (!ref_group_list || dl_list_empty(ref_group_list)) {
    return;
  }

  not_done_groups = dl_list_first(ref_group_list);
  while (not_done_groups) {
    cur_group = (rw_referral_group_struct *) dl_list_value(ref_group_list);

    if (!cur_group || !cur_group->referrals ||
        dl_list_empty(cur_group->referrals)) {
      not_done_groups = dl_list_next(ref_group_list);
      continue;
    }

    not_done_refs = dl_list_first(cur_group->referrals);
    while (not_done_refs) {
      cur_ref = (rw_referral_struct *) dl_list_value(cur_group->referrals);

      if (!cur_ref || NOT_STR_EXISTS(cur_ref->scheme) ||
          !STR_EQ(cur_ref->scheme, "whois")) {
        not_done_refs = dl_list_next(cur_group->referrals);
        continue;
      }

      whois_result = rwhois_query_whois_referral(cur_ref, query, debug_level,
                                                 response);

      if (whois_result) {
        display_whois_result(whois_result);
        destroy_rw_whois_result_struct(whois_result);
        whois_result = NULL;
      }

      /* delete this referral, as we have followed it, and deal with
         the advance logic */
      if (dl_list_at_tail(cur_group->referrals)) {
        not_done_refs = FALSE;
      } else {
        not_done_refs = TRUE;
      }

      dl_list_delete(cur_group->referrals);
    }


    not_done_groups = dl_list_next(ref_group_list);
  }
}

void
usage(char *prog_name)
{
  fprintf(stderr, "usage: %s [-l limit] [-s host] [-p port] [-rRv] query\n",
          prog_name);
  fprintf(stderr, "       -l limit: set the result limit on the rwhois server.\n");
  fprintf(stderr, "       -s host: query the rwhois server at this host [default: %s]\n", DEFAULT_RWHOIS_SERVER);
  fprintf(stderr, "       -p port: query the rwhois server at this port [default: %d]\n", DEFAULT_RWHOIS_PORT);
  fprintf(stderr, "       -r: follow referrals [default]\n");
  fprintf(stderr, "       -R: do not follow referrals\n");
  fprintf(stderr, "       -v: verbose\n");
  fprintf(stderr, "       -h: help; this text\n");

  exit(64);
}

rwhois_cl_opts_struct *
parse_command_line(int argc, char *argv[])
{
  char                  c;
  int                   i;
  rwhois_cl_opts_struct *opts        = NULL;

  opts = (rwhois_cl_opts_struct *) CALLOC(1, sizeof(*opts));

  /* set defaults */
  opts->host = DEFAULT_RWHOIS_SERVER;
  opts->port = DEFAULT_RWHOIS_PORT;
  opts->recurse_mode = RW_RECURSE;
  opts->whois_recurse_mode = RW_RECURSE;

  /* parse the command line */
  while ((c = getopt(argc, argv, "hl:s:p:rRWv")) > 0)
  {
    switch (c) {
    case 'h':
      usage(argv[0]);
      break;
    case 'l':
      opts->query_args.limit = atoi(optarg);
      opts->query_args.limit_changed_flag = TRUE;
      break;
    case 's':
      opts->host = optarg;
      break;
    case 'p':
      opts->port = atoi(optarg);
      break;
    case 'r':
      opts->recurse_mode = RW_RECURSE;
      break;
    case 'R':
      opts->recurse_mode = RW_NO_RECURSE;
      break;
    case 'W':
      opts->whois_recurse_mode = RW_NO_RECURSE;
      break;
    case 'v':
      opts->debug_level++;
      break;
    default:
      usage(argv[0]);
      break;
    }
  }

  if (optind >= argc) {
    usage(argv[0]);
  }

  /* join the rest of the arguments together into a query */
  opts->query_args.query = (char *) CALLOC(MAX_QUERY_LEN, sizeof(char));

  for (i = optind; i < argc; i++) {
    strcat(opts->query_args.query, argv[i]);
    strcat(opts->query_args.query, " ");
  }
  trim(opts->query_args.query);

  return opts;
}

int
main(int argc, char *argv[])
{
  rwhois_cl_opts_struct *opts;
  dl_list_type          *record_list         = NULL;
  dl_list_type          *referral_group_list = NULL;
  rw_response_struct    response;
  rw_server_struct      server;

  opts = parse_command_line(argc, argv);

  if (!opts) abort();

  referral_group_list = dl_list_create(destroy_rw_referral_group_struct);

  server.host = opts->host;
  server.port = opts->port;
  server.debug_level = opts->debug_level;

  record_list = recursive_query_rwhois(&(opts->query_args),
                                       &server,
                                       SPEC_VERSION,
                                       IMP_VERSION,
                                       opts->recurse_mode,
                                       referral_group_list,
                                       NULL,
                                       &response);


  if ( (!record_list || dl_list_empty(record_list)) &&
       (!referral_group_list || dl_list_empty(referral_group_list)) ) {
    if (response.status >= RW_OK) {
      printf("No results found for '%s'.\n", opts->query_args.query);
    }
  } else {
    printf("Results:\n\n");
  }

  display_query_results(record_list, NULL);

  if (opts->whois_recurse_mode == RW_RECURSE)
  {
    follow_whois_referrals(referral_group_list, opts->query_args.query,
                           opts->debug_level,
                           &response);
  }

  display_query_results(NULL, referral_group_list);

  if (response.status < RW_OK) {
    printf("error querying rwhois server: %s\n", response.msg);
    exit(1);
  }

  /* free the results */
  dl_list_destroy(record_list);
  dl_list_destroy(referral_group_list);

  /* free the command line args */
  if (opts->query_args.query) FREE(opts->query_args.query);
  FREE(opts);

  exit(0);
}
