<?php
// $Id$

/**
 * @file
 * Home of the FeedsHTTPFetcher and related classes.
 */

define('FEEDS_HTTP_NO_BASIC_AUTH', 0);
define('FEEDS_HTTP_BASIC_AUTH_PER_SOURCE', 1);
define('FEEDS_HTTP_BASIC_AUTH_PER_IMPORTER', 2);

/**
 * Definition of the import batch object created on the fetching stage by
 * FeedsHTTPFetcher.
 */
class FeedsHTTPBatch extends FeedsImportBatch {
  protected $url;
  protected $file_path;

  /**
   * Constructor.
   */
  public function __construct($url = NULL) {
    $this->url = $url;
    parent::__construct();
  }

  /**
   * Implementation of FeedsImportBatch::getRaw();
   */
  public function getRaw() {
    feeds_include_library('http_request.inc', 'http_request');
    $result = http_request_get($this->url);
    if ($result->code != 200) {
      throw new Exception(t('Download of @url failed with code !code.', array('@url' => $this->url, '!code' => $result->code)));
    }
    return $result->data;
  }

  /**
   * Implementation of FeedsImportBatch::getFilePath().
   */
  public function getFilePath() {
    if (!isset($this->file_path)) {
      $dest = file_destination(file_directory_path() .'/feeds/'. get_class($this) .'_'. md5($this->url) .'_'. time(), FILE_EXISTS_RENAME);
      $this->file_path = file_save_data($this->getRaw(), $dest);
      if($this->file_path === 0) {
        throw new Exception(t('Cannot write content to %dest', array('%dest' => $dest)));
      }
    }
    return $this->file_path;
  }
}

/**
 * Fetches data via HTTP.
 */
class FeedsHTTPFetcher extends FeedsFetcher {

  /**
   * Implementation of FeedsFetcher::fetch().
   */
  public function fetch(FeedsSource $source) {
    $source_config = $source->getConfigFor($this);
    $url = $source_config['source'];
    $url = $this->basicAuth($url, isset($source_config['basic_auth']) ? $source_config['basic_auth'] : array());
    return new FeedsHTTPBatch($url);
  }

  /**
   * Clear caches.
   */
  public function clear(FeedsSource $source) {
    $source_config = $source->getConfigFor($this);
    $url = $source_config['source'];
    feeds_include_library('http_request.inc', 'http_request');
    http_request_clear_cache($url);
  }

  /**
   * Expose source form.
   */
  public function sourceForm($source_config) {
    $form = array();
    $form['source'] = array(
      '#type' => 'textfield',
      '#title' => t('URL'),
      '#description' => t('Enter a feed URL.'),
      '#default_value' => isset($source_config['source']) ? $source_config['source'] : '',
      '#maxlength' => NULL,
      '#required' => TRUE,
    );
    if (isset($this->config['basic_auth']) && $this->config['basic_auth'] == FEEDS_HTTP_BASIC_AUTH_PER_SOURCE) {
      $form['basic_auth'] = array(
        '#type' => 'fieldset',
        '#collapsible' => TRUE,
        '#collapsed' => empty($source_config['basic_auth']['basic_auth_user']),
        '#title' => t('Authentication'),
        '#description' => t('Enter user name and password for authentication. Leave empty if no authentication is required.'),
      );
      $form['basic_auth'] += $this->basicAuthForm(isset($source_config['basic_auth']) ? $source_config['basic_auth'] : array());
    }
    return $form;
  }

  /**
   * Override parent::configDefaults().
   */
  public function configDefaults() {
    return array(
      'basic_auth' => FEEDS_HTTP_NO_BASIC_AUTH,
      'basic_auth_user' => '',
      'basic_auth_password' => '',
    );
  }

  /**
   * Override parent::configForm();
   */
  public function configForm() {
    $form = array();
    $form['basic_auth'] = array(
      '#type' => 'fieldset',
      '#title' => t('HTTP Basic Authentication'),
    );
    $form['basic_auth']['basic_auth'] = array(
      '#type' => 'radios',
      '#options' => array(
        FEEDS_HTTP_NO_BASIC_AUTH => t('No authentication'),
        FEEDS_HTTP_BASIC_AUTH_PER_SOURCE => t('Specify credentials when creating a feed.'),
        FEEDS_HTTP_BASIC_AUTH_PER_IMPORTER => t('One set of credentials for all feeds.'),
      ),
      '#default_value' => $this->config['basic_auth'],
    );
    $form['basic_auth'] += $this->basicAuthForm($this->config);
    return $form;
  }

  /**
   * Validate config form.
   */
  public function configFormValidate(&$values) {
    // Don't accidentally wipe out password.
    if (empty($values['basic_auth_password'])) {
      $values['basic_auth_password'] = $this->config['basic_auth_password'];
    }
    if ($values['basic_auth'] != FEEDS_HTTP_BASIC_AUTH_PER_IMPORTER) {
      $values['basic_auth_user'] = '';
      $values['basic_auth_password'] = '';
    }
  }

  /**
   * Basic auth form.
   */
  protected function basicAuthForm($config) {
    $form = array();
    $form['basic_auth_user'] = array(
      '#type' => 'textfield',
      '#title' => t('Username'),
      '#default_value' => empty($config['basic_auth_user']) ? '' : $config['basic_auth_user'],
    );
    $form['basic_auth_password'] = array(
      '#type' => 'password',
      '#title' => t('Password'),
    );
    return $form;
  }

  /**
   * Encode basic authentication credentials in URL depending on configuration.
   */
  protected function basicAuth($url, $source_config = array()) {
    if (!isset($this->config['basic_auth'])) {
      return $url;
    }
    elseif ($this->config['basic_auth'] == FEEDS_HTTP_BASIC_AUTH_PER_SOURCE) {
      if (!empty($source_config['basic_auth_user']) && !empty($source_config['basic_auth_password'])) {
        return $this->basicAuthEncodeCredentials($url, $source_config['basic_auth_user'], $source_config['basic_auth_password']);
      }
    }
    elseif ($this->config['basic_auth'] == FEEDS_HTTP_BASIC_AUTH_PER_IMPORTER) {
      return $this->basicAuthEncodeCredentials($url, $this->config['basic_auth_user'], $this->config['basic_auth_password']);
    }
    return $url;
  }

  /**
   * Encode basic authentication credentials into URL.
   */
  protected function basicAuthEncodeCredentials($url, $user, $password) {
    $parsed = parse_url($url);
    return str_replace("{$parsed['scheme']}://", "{$parsed['scheme']}://$user:$password@", $url);
  }
}