/***************************************************************************
 *
 * 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: httpmisc.c,v 1.3.4.2 2001/09/04 10:12:47 bazsi Exp $
 *
 * Author: Balazs Scheidler <bazsi@balabit.hu>
 * Auditor: 
 * Last audited version: 
 * Notes:
 *   Based on the code by: Viktor Peter Kovacs <vps__@freemail.hu>
 *
 ***************************************************************************/

#include "http.h"

#include <zorp/log.h>

#include <ctype.h>
#include <zorp/log.h>

gboolean
http_split_url(gchar *url, 
	       gchar **proto, guint *proto_len,
	       gchar **user, guint *user_len,
	       gchar **passwd, guint *passwd_len,
	       gchar **host, guint *host_len,
	       guint *port,
	       gchar **file, guint *file_len)
{
  gchar *p, *end, *part[4], *sep[4];
  int i;

  z_enter();
  
  *proto = url;
  *user = NULL;
  *passwd = NULL;
  *host = NULL;
  *file = NULL;
  *file_len = 0;
  *port = 0;

  p = url;
  while (*p && *p != ':')
    p++;
    
  if (!*p)
    {
      z_leave();
      return FALSE;
    }

  if (*(p + 1) != '/' || *(p + 2) != '/')
    {
      /* protocol not terminated by '//' */
      z_leave();
      return FALSE;
    }
  p += 3;
  *proto_len = p - url;

  for (i = 0; i < 4; i++)
    {
      part[i] = p;
      while (*p && *p != ':' && *p != '/' && *p != '@')
        p++;
      sep[i] = p;
      if (!*p || *p == '/')
        break;
      p++;
    }
  switch (i)
    {
    case 0:
      /* hostname only */
      *host = part[0];
      *host_len = sep[0] - part[0];
      break;
    case 1:
      /* username && host || hostname && port number */
      if (*sep[0] == ':')
        {
          *host = part[0];
          *host_len = sep[0] - part[0];
          /* port number */
          *port = strtoul(part[1], &end, 10);
          if (end != sep[1])
            {
              z_leave();
              return FALSE;
            }
        }
      else if (*sep[0] == '@')
        {
          /* username */
          *user = part[0];
          *user_len = sep[0] - part[0];
          *host = part[1];
          *host_len = sep[1] - part[1];
        }
      else
        {
          z_leave();
          return FALSE;
        }
      break;
    case 2:
      /* username && host && port || username && password && host */
      if (*sep[0] == '@' && *sep[1] == ':')
        {
          /* username, host, port */
          *user = part[0];
          *user_len = sep[0] - part[0];
          *host = part[1];
          *host_len = sep[1] - part[1];
          *port = strtoul(part[2], &end, 10);
          if (end != sep[2])
            {
              z_leave();
              return FALSE;
            }
        }
      else if (*sep[0] == ':' && *sep[1] == '@')
        {
          /* username, password, host */
          *user = part[0];
          *user_len = sep[0] - part[0];
          *passwd = part[1];
          *passwd_len = sep[1] - part[1];
          *host = part[2];
          *host_len = sep[2] - part[2];
        }
      else
        {
          z_leave();
          return FALSE;
        }
      break;
    case 3:
      /* username && password && hostname && port */
      if (*sep[0] == ':' && *sep[1] == '@' && *sep[2] == ':')
        {
          *user = part[0];
          *user_len = sep[0] - part[0];
          *passwd = part[1];
          *passwd_len = sep[1] - part[1];
          *host = part[2];
          *host_len = sep[2] - part[2];
          *port = strtoul(part[3], &end, 10);
          if (end != sep[3])
            {
              z_leave();
              return FALSE;
            }
        }
      else
        {
          z_leave();
          return FALSE;
        }
      break;
    default:
      z_leave();
      return FALSE;
    }

  *file = p;
  *file_len = strlen(*file);

  z_leave();
  return TRUE;
}

gint 
xdigit(char c)
{
  c = tolower(c);
  if (c >= '0' && c <= '9')
    return c - '0';
  else if (c >= 'a' && c <= 'f')
    return c - 'a';
  return -1;
}

gboolean
http_check_url(gchar *url)     //checks correct escapeness
{
  guint length = strlen(url);
  guint t = 0;

  while (t < length)
    {
      switch (url[t])
        {
        case '%':
          if ((t + 1) >= length)
            return FALSE;
          
          if (url[t + 1] == '%')
            {
              t++;
              break;
            }
          
          if ((t + 2) >= length)
            return FALSE;
          
          if (!isxdigit(url[t + 1]) || !isxdigit(url[t + 2]))
            return FALSE;
          t += 2;
          break;
        default:
          break;
        }
      t++;
    }
  return TRUE;
}

#if 0
gboolean
http_check_url(GString *url)
{
  guint length = url->len;
  guchar buf[url->len + 1];
  gint src, dst;

  src = 0;
  dst = 0;
  while (src < length)
    {
      switch (url->str[src])
        {
        case '%':
          if ((src + 2) >= length)
            return FALSE;
            
          if (!isxdigit(url->str[src + 1]) || !isxdigit(url->str[src + 2]))
            return FALSE;
          buf[dst] = xdigit(url->str[src + 1]) << 4 | xdigit(url->str[src + 2]);
          dst++;
          src += 2;
          break;
        case '+':
          buf[dst] = ' ';
          dst++;
        default:
          buf[dst++] = url->str[src];
          break;
        }
      src++;
    }
    
  length = dst;
  /* we have a converted URL in buf now, reencode it */
  src = 0;
  g_string_truncate(url, 0);
  while (src < length)
    {
      if (buf[src] == ' ')
        g_string_append_c(url, '+');
      else if (buf[src] < 32 || buf[src] >= 128)
        g_string_sprintfa(url, "%02x", buf[src]);
      else
        g_string_append_c(url, buf[src]);
      src++;
    }
  
  return TRUE;
}
#endif

gboolean
http_header_lookup(GString *headers, gchar *what, gchar **result, guint *length, int valueonly)
{
  guint l = strlen(what);
  int t = 0;

  z_enter();
  
  while (headers->str[t] != 0)
    {
      if (strncasecmp(headers->str + t, what, l) == 0)
        {
          char *eol;
          
          if (valueonly)
            {          
              /* skip to value */
              char *colon = strchr(headers->str + t, ':');
              if (colon)
                {
                  colon++;
                  if (*colon++ != ' ')
                    return FALSE;
                  *result = colon;
                  t = colon - headers->str;
                }
              else
		{
		  /* invalid headers */
		  z_leave();
		  return FALSE;
		}
            }
          else
            {
              *result = headers->str + t;
            }
            
          eol = strchr(headers->str + t, '\n');
          if (eol)
            *length = eol - (headers->str + t) - 1;
          else
            *length = strlen(headers->str + t);
          z_leave();
          return TRUE;
        }
      else
        {
          char *next = strchr(&headers->str[t], '\n');
          
          if (!next)
            {
              z_leave();
              return FALSE;
            }
          else
            t = next - headers->str;
          t++;
        }
    }
  z_leave();
  return FALSE;
}

