/*****************************************************************
 * gmerlin - a general purpose multimedia framework and applications
 *
 * Copyright (c) 2001 - 2012 Members of the Gmerlin project
 * gmerlin-general@lists.sourceforge.net
 * http://gmerlin.sourceforge.net
 *
 * 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, see <http://www.gnu.org/licenses/>.
 * *****************************************************************/

#include "server.h"
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include <gmerlin/http.h>
#include <gmerlin/log.h>
#define LOG_DOMAIN "proxy"

static void proxy_func(client_t * c)
  {
  int i;
  int fd = -1;
  bg_socket_address_t * addr = NULL;
  char * dest = NULL;
  const char * key = NULL;
  char * dest_host = NULL;
  int dest_port = -1;
  const char * path;
  gavl_dictionary_t req;
  gavl_dictionary_t res;
  const char * var;
  
  uint8_t * body = NULL;
  int body_len = 0;

  gavl_dictionary_init(&req);
  gavl_dictionary_init(&res);
  
  if(!(dest = gavl_strdup(gavl_dictionary_get_string(&c->req, "X-Gmerlin-Proxy-Dest"))))
    {
    /* TODO: 400 Bad Request */
    goto fail;
    }

  if(gavl_dictionary_get_string_i(&c->req, "Content-Length"))
    body = bg_http_read_body(c->fd, &c->req, &body_len);
  
  gavl_dictionary_copy(&req, &c->req);
  gavl_dictionary_set_string(&req, "X-Gmerlin-Proxy-Dest", NULL);

  /* /proxy/path -> /path */
  
  path = bg_http_request_get_path(&c->req);
  path += 6; // strlen("/proxy");
  bg_http_request_set_path(&req, path);

  /* Split URL */

  if(!bg_url_split(dest,
                   NULL,
                   NULL,
                   NULL,
                   &dest_host,
                   &dest_port,
                   NULL))
    goto fail;
  
  /* Exchange Host: header */
  for(i = 0; i < req.num_entries; i++)
    {
    if(!strcasecmp(req.entries[i].name, "Host"))
      key = req.entries[i].name;
    }

  if(key)
    {
    if(dest_port > 0)
      gavl_dictionary_set_string_nocopy(&req, key,
                              bg_sprintf("%s:%d", dest_host, dest_port));
    else
      gavl_dictionary_set_string(&req, key, dest_host);
    }

  /* Exchange Connection */
  key = NULL;
  for(i = 0; i < req.num_entries; i++)
    {
    if(!strcasecmp(req.entries[i].name, "Connection"))
      key = req.entries[i].name;
    }

  if(key)
    gavl_dictionary_set_string(&req, key, "Close");
  
  /* Send to final destination */

  addr = bg_socket_address_create();

  if(dest_port < 0)
    dest_port = 80;
  
  if(!bg_socket_address_set(addr, dest_host, dest_port, SOCK_STREAM))
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Invalid destination address: %s:%d",
           dest_host, dest_port);
    goto fail;
    }

  if((fd = bg_socket_connect_inet(addr, 5000)) < 0)
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Connection to %s:%d failed: %s",
           dest_host, dest_port, strerror(errno));
    goto fail;
    }
  if(!bg_http_request_write(fd, &req))
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Writing request to %s:%d failed: %s",
           dest_host, dest_port, strerror(errno));
    goto fail;
    }
  if(body && !bg_socket_write_data(fd, body, body_len))
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Forwarding body failed: %s",
           strerror(errno));
    goto fail;
    }
  
  if(body)
    {
    free(body);
    body = NULL;
    body_len = 0;
    }

  if(!bg_http_response_read(fd, &res, 5000))
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Reading response from %s:%d failed: %s",
           dest_host, dest_port, strerror(errno));
    goto fail;
    }
  if(gavl_dictionary_get_string_i(&res, "Content-Length"))
    body = bg_http_read_body(fd, &res, &body_len);
  else
    {
    var = gavl_dictionary_get_string_i(&res, "Transfer-Encoding");

    if(var && !strcasecmp(var, "chunked"))
      {
      body = bg_http_read_body(fd, &res, &body_len);
      gavl_dictionary_set_string(&res, "Transfer-Encoding", NULL);
      gavl_dictionary_set_long(&res, "Content-Length", body_len);
      }

    }
  

  /* Pass the response header unchanged */
  if(!bg_http_response_write(c->fd, &res))
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Forwarding response failed: %s",
           strerror(errno));
    goto fail;
    }
  if(body && !bg_socket_write_data(c->fd, body, body_len))
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Forwarding body failed: %s",
           strerror(errno));
    goto fail;
    }
  fail:

  if(dest_host)
    free(dest_host);

  if(dest)
    free(dest);
  
  if(addr)
    bg_socket_address_destroy(addr);
  
  if(body)
    free(body);

  if(fd > 0)
    close(fd);
  
  gavl_dictionary_free(&req);
  gavl_dictionary_free(&res);
  }

#if 0
static void cleanup_func(void * data)
  {

  }
#endif

int server_handle_proxy(bg_http_connection_t * conn, void * data)
  {
  client_t * c;
  server_t * s = data;

  c = client_create(s, CLIENT_TYPE_PROXY, conn->fd, &conn->req, NULL,
                    NULL, proxy_func);

  /* TODO */
  //  c->keep_alive = keep_alive;
  server_attach_client(s, c);
  conn->fd = -1;
  return 1;
  }
