/***************************************************************************
 *
 * Copyright (c) 2000 BalaBit IT Ltd, Budapest, Hungary
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: ftpolicy.c,v 1.8.4.3 2001/09/04 09:49:00 bazsi Exp $
 *
 * Author:  Andras Kis-Szabo <kisza@sch.bme.hu>
 * Author:  Attila SZALAY <sasa@balabit.hu>
 * Auditor:
 * Notes:
 *
 ***************************************************************************/

#include <zorp/zorp.h>
#include <zorp/stream.h>
#include <zorp/readline.h>
#include <zorp/proxy.h>
#include <zorp/policy.h>
#include <zorp/thread.h>
#include <zorp/zpython.h>
#include <zorp/log.h>

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

#include "ftp.h"
#include "ftpolicy.h"


GHashTable *
ftp_policy_command_hash_create(void)
{
  GHashTable *tmp;

  tmp = g_hash_table_new(g_str_hash, g_str_equal);

  return tmp;
}

gboolean
ftp_policy_command_hash_search(FtpProxy *self,gchar *command)
{
  FtpCommandDescriptor *tmp;
  
  tmp = g_hash_table_lookup(self->policy_command_hash, command);
  return tmp != NULL;
}

gboolean
ftp_hash_get_type(ZPolicyObj *tuple, guint *filter_type)
{
  ZPolicyObj *tmp;

  if (!z_policy_seq_check(tuple))
    {
      if (z_policy_var_parse(tuple, "i", filter_type))
        return TRUE;
      /* not a sequence */
      return FALSE;
    }

  tmp = z_policy_seq_getitem(tuple, 0);
  if (!z_policy_var_parse(tmp, "i", filter_type))
    {
      /* policy syntax error */
      z_policy_var_unref(tmp);
      return FALSE;
    }
  z_policy_var_unref(tmp);
  return TRUE;
}
                                                          

guint
ftp_policy_command_hash_do(FtpProxy *self)
{
  guint vissza;
  ZPolicyObj *res;
  ZPolicyObj *tmp;
  ZPolicyObj *command_where;
  unsigned int command_do;
  gchar work[10];
  gchar *msg;
  int i;
  
  z_proxy_enter(self);
  tmp = g_hash_table_lookup(self->policy_command_hash, self->request_cmd->str);
  if (!tmp)
    {
      tmp = g_hash_table_lookup(self->policy_command_hash, "*");
    }
  if (!tmp)
    {
      /*LOG
        This message attempt when zorp cannot found what to do with
        a command.
       */
      z_proxy_log(self, FTP_DEBUG, 6, "Policy doesn't contain default. Use hard-coded;");
      z_proxy_leave(self);
      return FTP_REQ_REJECT;
    }
  z_policy_lock(self->super.thread);
  if (!ftp_hash_get_type(tmp,&command_do))
    {
      /*LOG
        This message attempt when zorp cannot parse the policy line.
       */
      z_proxy_log(self, FTP_PPOLICY, 1, "Policy type invalid;");
    }
  z_policy_unlock(self->super.thread);
  switch(command_do)
    {
    case FTP_REQ_ACCEPT:
    case FTP_REQ_ABORT:
      vissza = command_do;
      break;
    case FTP_REQ_REJECT:
      z_policy_lock(self->super.thread);
      if (z_policy_var_parse(tmp, "(is)", &command_do, &msg))
        {
          for(i = 0; i < 3; i++)
            work[i]=msg[i];

          work[i]=0;
          g_string_assign(self->answer_cmd, work);
          g_string_assign(self->answer_param, &msg[i+1]);
        }
      vissza = command_do;
      z_policy_unlock(self->super.thread);
      break;
    case FTP_REQ_POLICY:
      z_policy_lock(self->super.thread);
      if(!z_policy_var_parse(tmp,"(iO)",&command_do,&command_where))
        {
          /*LOG
            This message attempt when zorp cannot parse the policy line
           */
          z_proxy_log(self, FTP_PPOLICY, 1, "Cannot parse policy line; command='%s'", self->request_cmd->str);
          vissza = FTP_REQ_ABORT;
        }
      else
        {
          g_string_assign(self->answer_cmd, "500");
          g_string_assign(self->answer_param, "Error parsing command");
          
          res = z_policy_call_object(command_where, z_policy_var_build("(s)",self->request_cmd->str));
          if (res == NULL)
            {
              /*LOG
                This message attempt when zorp cannot call the given
                policy function.
               */
              z_proxy_log(self, FTP_PPOLICY, 1, "Error in policy calling; command='%s'", self->request_cmd->str);
              vissza = FTP_REQ_ABORT;
            }
          else
            {
              if (!z_policy_var_parse(res,"i",&vissza))
                {
                  /*LOG
                    This mesage attempt when called policy function
                    return with wrong value.
                   */
                  z_proxy_log(self, FTP_PPOLICY, 1, "Can't parsing return code; command='%s'", self->request_cmd->str);
                  vissza = FTP_REQ_ABORT;
                }
              else
                {
                  switch(vissza)
                    {
                    case FTP_REQ_ACCEPT:
                    case FTP_REQ_ABORT:
                    case FTP_REQ_REJECT:
                      break;
                    case Z_UNSPEC:
                    case Z_DENY:
                    case Z_DROP:
                      vissza = FTP_REQ_REJECT;
                      break;
                    default:
                      break;
                    }
                }
            }
        }
      z_policy_unlock(self->super.thread);
      break;
    default:
      vissza = FTP_REQ_ABORT;
      break;
    }
  z_proxy_leave(self);
  return vissza;
}

ZDimHashTable *
ftp_policy_answer_hash_create(void)
{
  ZDimHashTable *tmp;

  tmp = z_dim_hash_table_new(2, 2, DIMHASH_WILDCARD, DIMHASH_CONSUME);

  return tmp;
}

guint
ftp_policy_answer_hash_do(FtpProxy *self)
{
  guint vissza;
  ZPolicyObj *res;
  ZPolicyObj *tmp;
  ZPolicyObj *answer_where;
  unsigned int answer_do;
  gchar key1[5];
  gchar key2[5];
  gchar *key[2];
  gchar *msg;
  int i;
  gchar work[10];

  z_proxy_enter(self);
  
  if (self->request_cmd->len > 0)
    g_snprintf(key1, sizeof(key1), "%s", self->request_cmd->str);
  else
    g_snprintf(key1, sizeof(key1), "Null");

  g_snprintf(key2, sizeof(key2), "%s", self->answer_cmd->str);
  
  key[0] = key1;
  key[1] = key2;
  
  tmp = z_dim_hash_table_search(self->policy_answer_hash, 2, key);
  
  if (!tmp)
    {
      /*LOG
        This message attempt when zorp cannot found what to do with
        a command answer.
       */
      z_proxy_log(self, FTP_DEBUG, 6, "Policy doesn't conatin default. Use hard-coded default;");
      z_proxy_leave(self);
      return FTP_RSP_REJECT;
    }
    
  z_policy_lock(self->super.thread);
  if (!ftp_hash_get_type(tmp, &answer_do))
    {
      /*LOG
        This message attempt when zorp canot parse the policy line.
       */
      z_proxy_log(self, FTP_PPOLICY, 1, "Answer type invalid; command='%s', answer='%s'", self->request_cmd->str, self->answer_cmd->str);
    }
  z_policy_unlock(self->super.thread);
  
  switch(answer_do)
    {
    case FTP_RSP_REJECT:
      vissza = FTP_RSP_REJECT;
      
      z_policy_lock(self->super.thread);
      if (!z_policy_var_parse(tmp, "(is)", &answer_do, &msg))
        {
          g_string_assign(self->answer_cmd, "500");
          g_string_assign(self->answer_param, "Error parsing answer");
        }
      else
        {
          for(i = 0; i < 3; i++)
            work[i]=msg[i];
          work[i]=0;
          g_string_assign(self->answer_cmd, work);
          g_string_assign(self->answer_param, &msg[i+1]);
        }
      z_policy_unlock(self->super.thread);
      break;
    case FTP_RSP_ACCEPT:
      vissza = FTP_RSP_ACCEPT;
      break;
    case FTP_RSP_ABORT:
      vissza = FTP_RSP_ABORT;
      
      z_policy_lock(self->super.thread);
      if (!z_policy_var_parse(tmp, "(is)", &answer_do, &msg))
        {
          g_string_assign(self->answer_cmd, "500");
          g_string_assign(self->answer_param, "Error parsing answer");
        }
      else
        {
          for(i = 0; i < 3; i++)
            work[i]=msg[i];
          work[i]=0;
          g_string_assign(self->answer_cmd, work);
          g_string_assign(self->answer_param, &msg[i+1]);
        }
      z_policy_unlock(self->super.thread);
      break;
    case FTP_RSP_POLICY:
      z_policy_lock(self->super.thread);
      if(!z_policy_var_parse(tmp,"(iO)", &answer_do, &answer_where))
        {
          /*LOG
            This message attempt when zorp cannot parse policy line.
           */
          z_proxy_log(self, FTP_PPOLICY, 1, "Bad policy line; command='%s', answer='%s'", self->request_cmd->str, self->answer_cmd->str);
          g_string_assign(self->answer_cmd, "500");
          g_string_assign(self->answer_param, "Error parsing answer (bad policy)");
          vissza = FTP_RSP_ABORT;
        }
      else
        {
          res = z_policy_call_object(answer_where, z_policy_var_build("(ss)", self->request_cmd->str, self->answer_cmd->str));
          if (res == NULL)
            {
              /*LOG
                This message caused by an exeption in python.
               */
              z_proxy_log(self, FTP_PPOLICY, 1, "Error in policy calling; command='%s', answer='%s'", self->request_cmd->str, self->answer_cmd->str);
              g_string_assign(self->answer_cmd, "500");
              g_string_assign(self->answer_param, "Error parsing answer (bad policy)");
              vissza = FTP_RSP_ABORT;
            }
          else
            {
              if(!z_policy_var_parse(res, "i", &vissza))
                {
                  /*LOG
                    This message atempt when zorp cannot parse policy
                    return value.
                   */
                  z_proxy_log(self, FTP_PPOLICY, 1, "Return code invalid from policy function; command='%s', answer='%s'", self->request_cmd->str, self->answer_cmd->str);
                  g_string_assign(self->answer_cmd, "500");
                  g_string_assign(self->answer_param, "Error parsing answer (bad policy)");
                  vissza = FTP_RSP_ABORT;
                }
              else
                {
                  switch(vissza)
                    {
                    case FTP_RSP_ACCEPT:
                    case FTP_RSP_REJECT:
                    case FTP_RSP_ABORT:
                      break;
                    case Z_DENY:
                    case Z_DROP:
                    case Z_UNSPEC:
                      vissza = FTP_RSP_REJECT;
                      break;
                    default:
                      g_string_assign(self->answer_cmd, "500");
                      g_string_assign(self->answer_param, "Error parsing answer, connection dropped.");
                      vissza = FTP_RSP_ABORT;
                    }
                }
            }
        }
      z_policy_unlock(self->super.thread);
      break;
    default:
      g_string_assign(self->answer_cmd, "500");
      g_string_assign(self->answer_param, "Error parsing answer, connection dropped.");
      vissza = FTP_RSP_ABORT;
      break;
    }
  z_proxy_leave(self);
  return vissza;
}
