#include <curl/curl.h>
#include <curl/easy.h>

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "hscurl.h"

static CURL *c = NULL;
static int curl_init_done = 0;
static char* user_agent = NULL;
static char user_agent_fmt[] = "darcs/%s (libcurl/%s)";

// curl_easy_setopt requires that char * parameters must exist
// between invocations. hence, we strdup the parameters, and keep
// them here until a new value is used.
char * persisted_user_agent = NULL;
char * persisted_proxypass = NULL;
char * persisted_url = NULL;

CURLcode safe_curl_easy_setopt_str(CURL *c, CURLoption o, const char *val, char ** persisted_val) {
  int err;
  char * val_copy = NULL;

  if ( val != NULL ) {
    val_copy = strdup(val);
  }

  err = curl_easy_setopt(c, o, val_copy);

  if ( *persisted_val != NULL )
    free (*persisted_val);

  *persisted_val = val_copy;

  return err;
}

// get_curl returns an error code
// cache_time is -1 for default cachability
int get_curl(const char *darcs_version, const char *proxypass, const char *filename, const char *url, int cache_time) {
  CURLcode err;
  FILE *f;
  struct curl_slist *headers = NULL;

  if(!curl_init_done) {
      curl_global_init(CURL_GLOBAL_ALL);
      curl_init_done=1;
  }
  if (c == NULL) c = curl_easy_init();
  if (c == NULL) return -2;
  if (NULL == user_agent) {
    unsigned int uasize = strlen(user_agent_fmt) + strlen(darcs_version) + strlen(curl_version());
    user_agent = malloc(uasize);  /* only allocated once; let program exit clean it up */
    if (NULL != user_agent)
      sprintf(user_agent, user_agent_fmt, darcs_version, curl_version());
  }

  f = fopen(filename,"wb");
  if (f == NULL) return -1;

  err = safe_curl_easy_setopt_str(c, CURLOPT_URL, url, &persisted_url);
  /*curl_easy_setopt(c, CURLOPT_NOSIGNAL, NULL);*/

  err += safe_curl_easy_setopt_str(c, CURLOPT_USERAGENT, (user_agent ? user_agent : "darcs"), &persisted_user_agent);
#ifdef CURLOPT_WRITEDATA
  err += curl_easy_setopt(c, CURLOPT_WRITEDATA, f);
#else
  err += curl_easy_setopt(c, CURLOPT_FILE, f);
#endif
  err += curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1);
  err += curl_easy_setopt(c, CURLOPT_FAILONERROR, 1);
  err += curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
  /* libcurl currently always sends Pragma: no-cache, but never
     Cache-Control, which is contradictory.  We override both, just to
     be sure. */
  headers = curl_slist_append(headers, "Accept: */*");
  if(cache_time == 0) {
      headers = curl_slist_append(headers, "Pragma: no-cache");
      headers = curl_slist_append(headers, "Cache-Control: no-cache");
  } else if(cache_time > 0) {
      /* This won't work well with HTTP/1.0 proxies. */
      char buf[40];
      snprintf(buf, 40, "Cache-Control: max-age=%d", cache_time);
      headers = curl_slist_append(headers, "Pragma:");
      headers = curl_slist_append(headers, buf);
  } else {
      headers = curl_slist_append(headers, "Pragma:");
      headers = curl_slist_append(headers, "Cache-Control:");
  }
  err += curl_easy_setopt(c, CURLOPT_HTTPHEADER, headers);

  /* don't set the proxy user and password to an empty string */
  if (proxypass && *proxypass)
    err += safe_curl_easy_setopt_str(c, CURLOPT_PROXYUSERPWD, proxypass, &persisted_proxypass);

  if(!err)
      err += curl_easy_perform(c);
  curl_slist_free_all(headers);
  fclose(f);
  return (int)err;
}

