#include "stdafx.h"
#include "file_downloader.h"


// ----------------------------------------------------------------------------
// global functions
// ----------------------------------------------------------------------------


CURLcode File_downloader::global_init(long flags)
{
  return curl_global_init(flags);
}


void File_downloader::global_cleanup()
{
  curl_global_cleanup();
}


int File_downloader::write_data(void *buffer, size_t size, size_t nmemb, void *data)
{
  File_downloader *fd= (File_downloader *)data;
  if (fd && fd->_file.stream() && !fd->_cancelled)
    return (int)fwrite(buffer, size, nmemb, fd->_file.stream());
  else
    return -1;
}


int File_downloader::update_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
  File_downloader *fd= (File_downloader *)clientp;
  if (fd)
    fd->_progress_cb(dltotal, dlnow);

  return 0;
}


int File_downloader::process_debug_msg(CURL *curl, curl_infotype infotype, char *msg, size_t size, void *data)
{
  FILE *file= (FILE*) data;
  if (!file)
    return -1;

  const char *msg_type= NULL;
  switch (infotype)
  {
    case CURLINFO_TEXT:
    case CURLINFO_HEADER_IN:
    case CURLINFO_HEADER_OUT:
    case CURLINFO_END:
      (void) fwrite(msg, sizeof(char), size, file);
      (void) fflush(file);
      break;

    case CURLINFO_DATA_IN:
    case CURLINFO_DATA_OUT:
    case CURLINFO_SSL_DATA_IN:
    case CURLINFO_SSL_DATA_OUT:
      break;
  }

  return 0;
}


int File_downloader::translate_proxy_type(const std::string &val)
{
  int res= 0;
  if (0 == val.compare("HTTP"))
    res= CURLPROXY_HTTP;
  else if (0 == val.compare("SOCKS4"))
    res= CURLPROXY_SOCKS4;
  else if (0 == val.compare("SOCKS5"))
    res= CURLPROXY_SOCKS5;
  return res;
}


// ----------------------------------------------------------------------------
// File_downloader
// ----------------------------------------------------------------------------


File_downloader::File_downloader()
:
_cancelled(false)
{
}


File_downloader::~File_downloader()
{
  cancel_download(); // if any
}


void File_downloader::add_log_message(const std::string &msg)
{
  if (_log.stream())
    fwrite(msg.c_str(), sizeof(char), msg.size(), _log.stream());
}


void File_downloader::disconnect_progress_cb()
{
  _progress_cb.disconnect();
}


bool File_downloader::download()
{
  (void) _log.open();

  CURL *curl= curl_easy_init();
  if (!curl)
  {
    add_log_message(std::string("Failed to initialize CURL handler."));
    return false;
  }

  if (!_file.open("wb"))
    return false;

  _cancelled= false;

  curl_easy_setopt(curl, CURLOPT_URL, _url.c_str());
  
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &File_downloader::write_data);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
  
  if (!_user_agent.empty())
    curl_easy_setopt(curl, CURLOPT_USERAGENT, _user_agent.c_str());

  // proxy
  if (_proxy_server.empty())
    _proxy_type.clear();
  curl_easy_setopt(curl, CURLOPT_PROXYTYPE, translate_proxy_type(_proxy_type));
  curl_easy_setopt(curl, CURLOPT_PROXY, _proxy_server.c_str());
  curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, _proxy_userpwd.c_str());
  curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);

  // referer & redirects
  curl_easy_setopt(curl, CURLOPT_AUTOREFERER, true);
  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);

  // authorization
  if (_server_userpwd.empty())
    _server_userpwd= "anonymous:anonymous@anonymous.org";
  curl_easy_setopt(curl, CURLOPT_USERPWD, _server_userpwd.c_str());
  curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);

  // SSL certificates
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);

  // progress
  curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
  curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, this);
  curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &File_downloader::update_progress);

  // logging
  curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, &File_downloader::process_debug_msg);
  curl_easy_setopt(curl, CURLOPT_DEBUGDATA, _log.stream());
  curl_easy_setopt(curl, CURLOPT_VERBOSE, true);

  // process http error pages
  curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);

  CURLcode _res= curl_easy_perform(curl);

  _file.close();

  curl_easy_cleanup(curl);

  _log.close();

  return (CURLE_OK == _res);
}


void File_downloader::cancel_download()
{
  _cancelled= true;
}
