<?php
// $Id: FeedsHTTPFetcher.inc,v 1.29.2.2 2010/10/26 19:03:14 yhahn Exp $

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

feeds_include_library('PuSHSubscriber.inc', 'PuSHSubscriber');

/**
 * Result of FeedsHTTPFetcher::fetch().
 */
class FeedsHTTPFetcherResult extends FeedsFetcherResult {
  protected $url;
  protected $file_path;


  /**
   * Constructor.
   */

/**
 *The following function add two new fields to the HTTPFetcher. It's used to modifiy an URL with a prefix and a suffix. 
 *The case scenario may be to use  an URL entered by a user as an argument to build a request. An exemple might be to use the URL of a RSS feed
 *like "http://rss.cnn.com/rss/cnn_topstories.rss" to build a request to an API that will extract the full text of that feed. 
 *By using a URL like this one as a prefix "http://fivefilters.org/content-only/makefulltextfeed.php?url=", all RSS feeds entered  by the users would go to 
 *"http://fivefilters.org/content-only/makefulltextfeed.php?url=http://url.entered.by.user.topstories.rss". 
 *Prefix and suffix can also be useful for sending requests to services such as SEOmoz, MajesticSEO or Google. 
 *You'll notice that we've had a hard time fetching the array properly and that we have default to using a loop that counts each and every character. 
 *This forces configuration names to be exactly 13 characters. Anyway, it works well now as a prototype that serves our needs. 
 *Anyone who wants to improve this code is welcomed.
*/
 public function __construct($url = NULL) {
   $query1 =  db_select('feeds_source','fs')
            ->fields('fs',array('id'))
	    ->condition('source', $url, '=');
   $result1 = $query1->execute();
   $record1 = $result1->fetchAssoc();
   $id_feed_config = ($record1['id']);
    
   $query2 =  db_select('feeds_importer','fi')
            ->fields('fi',array('config'))
	    ->condition('id', $id_feed_config, '=');
   $result2 = $query2->execute();
   $record2 = $result2->fetchAssoc();
   $result = ($record2['config']);
    
   $array1 = $result;
   
   $array2 = array();  
   $array3 = array();
   $prefix = '';
   $suffix = '';
   $i = 251; $j=0;
      
   		if($array1[$i+1] > 0 && $array1[$i+2] >= 0 && $array1[$i+3] == ':'){
       			$i = 256 ;
      			while($array1[$i] != '"'){
        			$array2[$j] = $array1[$i];
				$i++; 
        			$j++;
       			}
       			foreach($array2 as $value){
         			$prefix = $prefix.$value; 
       			}
      			$i = $i+25; 
      
      			if($array1[$i] == 0){
         			$this->url = $prefix.$url ;
      			}
     			elseif($array1[$i]> 0 && $array1[$i+1]>=0 && $array1[$i+2] == ':'){
        			$i+=4;
         			$j=0;
       				while($array1[$i] != '"'){
        				$array3[$j] = $array1[$i];
					$i++; 
        				$j++;
       				}
       				foreach($array3 as $value){
         				$suffix = $suffix.$value;
       				}
     				$this->url = $prefix.$url.$suffix ; 
     			}
     			elseif($array1[$i]>0 && $array1[$i+1]>=0 &&  $array1[$i+2] != ':'){
       					$i+=5;
         				$j=0;
       				while($array1[$i] != '"'){
        				$array3[$j] = $array1[$i];
					$i++; 
        				$j++;
       				}
       				foreach($array3 as $value){
         				$suffix = $suffix.$value;
       				}
				$this->url = $prefix.$url.$suffix ; 
      			}
    		}
   		elseif($array1[$i+1] > 0 && $array1[$i+2] >= 0 && $array1[$i+3] >= 0){
       			$i = 257 ;
       			while($array1[$i] != '"'){
        			$array2[$j] = $array1[$i];
				$i++; 
        			$j++;
       			}
       			foreach($array2 as $value){
         			$prefix = $prefix.$value;
       			}
      			$i = $i+25; 
      			if($array1[$i] == 0){
         			$this->url = $prefix.$url ; 
       			}
     			elseif($array1[$i]> 0 && $array1[$i+1]>=0 && $array1[$i+2] == ':'){
        			$i+=4;
        			$j=0;
       				while($array1[$i] != '"'){
        				$array3[$j] = $array1[$i];
					$i++; 
        				$j++;
       				}
       				foreach($array3 as $value){
         				$suffix = $suffix.$value;
       				}
     				 $this->url = $prefix.$url.$suffix ; 
     			}
     			elseif($array1[$i]>0 && $array1[$i+1]>=0 &&  $array1[$i+2] != ':'){
       				$i+=5;
       				$j=0;
       				while($array1[$i] != '"'){
        				$array3[$j] = $array1[$i];
					$i++; 
        				$j++;
       				}
       				foreach($array3 as $value){
         				$suffix = $suffix.$value;
       				}
       				$this->url = $prefix.$url.$suffix ; 
     			}
   		}
   		else{
       			$this->url = $url;
   		}
    parent::__construct('');
  }

  /**
   * Overrides FeedsFetcherResult::getRaw();
   */
  public function getRaw() {
    feeds_include_library('http_request.inc', 'http_request');
    $result = http_request_get($this->url);
    if (!in_array($result->code, array(200, 201, 202, 203, 204, 205, 206))) {
      throw new Exception(t('Download of @url failed with code !code.', array('@url' => $this->url, '!code' => $result->code)));
    }
    return $this->sanitizeRaw($result->data);
  }
}

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

  /**
   * Implements FeedsFetcher::fetch().
   */
  public function fetch(FeedsSource $source) {
    $source_config = $source->getConfigFor($this);
    if ($this->config['use_pubsubhubbub'] && ($raw = $this->subscriber($source->feed_nid)->receive())) {
      return new FeedsFetcherResult($raw);
    }
    return new FeedsHTTPFetcherResult($source_config['source']);
  }

  /**
   * 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);
  }

  /**
   * Implements FeedsFetcher::request().
   */
  public function request($feed_nid = 0) {
    feeds_dbg($_GET);
    @feeds_dbg(file_get_contents('php://input'));
    // A subscription verification has been sent, verify.
    if (isset($_GET['hub_challenge'])) {
      $this->subscriber($feed_nid)->verifyRequest();
    }
    // No subscription notification has ben sent, we are being notified.
    else {
      try {
        feeds_source($this->id, $feed_nid)->existing()->import();
      }
      catch (Exception $e) {
        // In case of an error, respond with a 503 Service (temporary) unavailable.
        header('HTTP/1.1 503 "Not Found"', null, 503);
        exit();
      }
    }
    // Will generate the default 200 response.
    header('HTTP/1.1 200 "OK"', null, 200);
    exit();
  }

  /**
   * Override parent::configDefaults().
   */
  public function configDefaults() {
    return array(
      'auto_detect_feeds' => FALSE,
      'use_pubsubhubbub' => FALSE,
      'designated_hub' => '',
   
      'url_prefix_01' => '',
      'url_suffix_02' => '',
    );
  }

  /**
   * Override parent::configForm().
   */
  public function configForm(&$form_state) {
    $form = array();
    $form['auto_detect_feeds'] = array(
      '#type' => 'checkbox',
      '#title' => t('Auto detect feeds'),
      '#description' => t('If the supplied URL does not point to a feed but an HTML document, attempt to extract a feed URL from the document.'),
      '#default_value' => $this->config['auto_detect_feeds'],
    );
    $form['use_pubsubhubbub'] = array(
      '#type' => 'checkbox',
      '#title' => t('Use PubSubHubbub'),
      '#description' => t('Attempt to use a <a href="http://en.wikipedia.org/wiki/PubSubHubbub">PubSubHubbub</a> subscription if available.'),
      '#default_value' => $this->config['use_pubsubhubbub'],
    );

   
      $form['url_prefix_01'] = array(
      '#type' => 'textfield',
      '#title' => t('URL Prefix'),
      '#description' => t('Enter URL prefix'),
      '#default_value' => $this->config['url_prefix_01'],
      '#maxlength' => NULL,
    );
 
    $form['url_suffix_02'] = array(
      '#type' => 'textfield',
        
      '#title' => t('URL Suffix'),
      '#description' => t('Enter URL suffix'),
      '#default_value' => $this->config['url_suffix_02'],
      '#maxlength' => NULL,
    );
   $form['designated_hub'] = array(
      '#type' => 'textfield',
      '#title' => t('Designated hub'),
      '#description' => t('Enter the URL of a designated PubSubHubbub hub (e. g. superfeedr.com). If given, this hub will be used instead of the hub specified in the actual feed.'),
      '#default_value' => $this->config['designated_hub'],
//      '#process' => array('ctools_dependent_process'),
      '#dependency' => array(
        'edit-use-pubsubhubbub' => array(1),
      ),
    );  
   return $form;
  }

 /**
   * 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' => FALSE,
    );
    return $form;
  }

  /**
   * Override parent::sourceFormValidate().
   */
  public function sourceFormValidate(&$values) {
    if ($this->config['auto_detect_feeds']) {
      feeds_include_library('http_request.inc', 'http_request');
      if ($url = http_request_get_common_syndication($values['source'])) {
        $values['source'] = $url;
      }
    }
  }

  /**
   * Override sourceSave() - subscribe to hub.
   */
  public function sourceSave(FeedsSource $source) {
    if ($this->config['use_pubsubhubbub']) {
      $this->subscribe($source);
    }
  }

  /**
   * Override sourceDelete() - unsubscribe from hub.
   */
  public function sourceDelete(FeedsSource $source) {
    if ($this->config['use_pubsubhubbub']) {
      $this->unsubscribe($source);
    }
  }

  /**
   * Implement FeedsFetcher::subscribe() - subscribe to hub.
   */
  public function subscribe(FeedsSource $source) {
    $source_config = $source->getConfigFor($this);
    $this->subscriber($source->feed_nid)->subscribe($source_config['source'], url($this->path($source->feed_nid), array('absolute' => TRUE)), valid_url($this->config['designated_hub']) ? $this->config['designated_hub'] : '');
  }

  /**
   * Implement FeedsFetcher::unsubscribe() - unsubscribe from hub.
   */
  public function unsubscribe(FeedsSource $source) {
    $source_config = $source->getConfigFor($this);
    $this->subscriber($source->feed_nid)->unsubscribe($source_config['source'], url($this->path($source->feed_nid), array('absolute' => TRUE)));
  }

  /**
   * Implement FeedsFetcher::importPeriod().
   */
  public function importPeriod(FeedsSource $source) {
    if ($this->subscriber($source->feed_nid)->subscribed()) {
      return 259200; // Delay for three days if there is a successful subscription.
    }
  }

  /**
   * Convenience method for instantiating a subscriber object.
   */
  protected function subscriber($subscriber_id) {
    return PushSubscriber::instance($this->id, $subscriber_id, 'PuSHSubscription', PuSHEnvironment::instance());
  }
}

/**
 * Implement a PuSHSubscriptionInterface.
 */
class PuSHSubscription implements PuSHSubscriptionInterface {
  public $domain;
  public $subscriber_id;
  public $hub;
  public $topic;
  public $status;
  public $secret;
  public $post_fields;
  public $timestamp;

  /**
   * Load a subscription.
   */
  public static function load($domain, $subscriber_id) {
    if ($v = db_query("SELECT * FROM {feeds_push_subscriptions} WHERE domain = :domain AND subscriber_id = :sid", array(':domain' => $domain, ':sid' => $subscriber_id))->fetchAssoc()) {
      $v['post_fields'] = unserialize($v['post_fields']);
      return new PuSHSubscription($v['domain'], $v['subscriber_id'], $v['hub'], $v['topic'], $v['secret'], $v['status'], $v['post_fields'], $v['timestamp']);
    }
  }

  /**
   * Create a subscription.
   */
  public function __construct($domain, $subscriber_id, $hub, $topic, $secret, $status = '', $post_fields = '') {
    $this->domain = $domain;
    $this->subscriber_id = $subscriber_id;
    $this->hub = $hub;
    $this->topic = $topic;
    $this->status = $status;
    $this->secret = $secret;
    $this->post_fields = $post_fields;
  }

  /**
   * Save a subscription.
   */
  public function save() {
    $this->timestamp = time();
    $this->delete($this->domain, $this->subscriber_id);
    drupal_write_record('feeds_push_subscriptions', $this);
  }

  /**
   * Delete a subscription.
   */
  public function delete() {
    db_delete('feeds_push_subscriptions')
      ->condition('domain', $this->domain)
      ->condition('subscriber_id', $this->subscriber_id)
      ->execute();
  }
}

/**
 * Provide environmental functions to the PuSHSubscriber library.
 */
class PuSHEnvironment implements PuSHSubscriberEnvironmentInterface {
  /**
   * Singleton.
   */
  public static function instance() {
    static $env;
    if (empty($env)) {
      $env = new PuSHEnvironment();
    }
    return $env;
  }

  /**
   * Implements PuSHSubscriberEnvironmentInterface::msg().
   */
  public function msg($msg, $level = 'status') {
    drupal_set_message($msg, $level);
  }

  /**
   * Implements PuSHSubscriberEnvironmentInterface::log().
   */
  public function log($msg, $level = 'status') {
    switch ($level) {
      case 'error':
        $severity = WATCHDOG_ERROR;
        break;
      case 'warning':
        $severity = WATCHDOG_WARNING;
        break;
      default:
        $severity = WATCHDOG_NOTICE;
        break;
    }
    feeds_dbg($msg);
    watchdog('FeedsHTTPFetcher', $msg, array(), $severity);
  }
}
