<?php
// $Id: urlicon.module,v 1.24.2.1 2008/10/06 21:11:02 sanduhrs Exp $

/**
 * URL Icon
 * 
 * @file
 * Filter all external links in nodes and comments.
 *
 * @author
 * Stefan Auditor <stefan.auditor@erdfisch.de>
 * for erdfisch http://erdfisch.de
 */

// Path to directory where favicons are stored
define('UI_FILE_PATH', file_directory_path() .'/urlicon');
define('UI_FORMAT_FAVICON', 0);
define('UI_FORMAT_EXISTINGFAVICON', 1);
define('UI_FORMAT_ICON', 2);
define('UI_FORMAT_CLASS', 3);

/**
 * Implementation of hook_help().
 */
function urlicon_help($path, $arg) {
  switch ($path) {
    case 'admin/help#urlicon':
      $output = '<p>'. t('The URLIcon module automatically adds a CSS class to &lt;a&gt;-elements reflecting their target and fetches the favicon from the target site.') .'</p>';
      $output .= '<p>'. t('Use Input Formats to enable the URL filter') .'</p>';
      $output .= '<p>'. t('<ol><li>Select an existing Input Format or add a new one</li><li>Configure the Input Format</li><li>Enable URL class filter and Save configuration</li><li>Rearrange the weight of the URL filter depending on what filters exist in the format</li></ol>') .'</p>';
      $output .= '<p>'. t('You can enable the urlfilter for an input format from <a href="%admin-filters">administer &gt;&gt; Site Configuration &gt;&gt; Input Filter</a>.', array('%admin-filters' => url('admin/settings/filters'))) .'</p>';
      return $output;
    case 'admin/modules#description':
      return t('Automatically fetch favicons for URLs.');
  }
}

/**
 * Implementation of hook_init().
 */
function urlicon_init() {
  drupal_add_css(drupal_get_path('module', 'urlicon') .'/urlicon.css');
}

/**
 * Implementation of hook_filter().
 */
function urlicon_filter($op, $delta = 0, $format = -1, $text = '') {
  switch ($op) {
    case 'list':
      return array(0 => t('URL Icon filter'));
    
    case 'description':
      return t('Adds favicons to URLs.');
      break;
    case 'process':
      // check for directory
      $dir = UI_FILE_PATH;
      file_check_directory($dir, 1);
      
      $reg_exp = '/<a.+?href=\"((http|https|ftp|telnet|news|mms):\/\/.+?)\"[^>]*>(.+?)<\/a>/i';
      
      $ui_format = variable_get("filter_urlicon_$format", UI_FORMAT_FAVICON);
      switch ($ui_format) {
        case UI_FORMAT_FAVICON:
          $text = preg_replace_callback($reg_exp, '_urlicon_format_favicon', $text);
          break;
        case UI_FORMAT_EXISTINGFAVICON:
          $text = preg_replace_callback($reg_exp, '_urlicon_format_existingfavicon', $text);
          break;
        case UI_FORMAT_ICON:
          $text = preg_replace_callback($reg_exp, '_urlicon_format_icon', $text);
          break;
        case UI_FORMAT_CLASS:
          $text = preg_replace_callback($reg_exp, '_urlicon_format_class', $text);
          break;
      }
      
      return $text;
      break;
    case 'settings':
      return urlicon_settings($format);
      break;
    default:
      return $text;
      break;
  }
}

/**
 * Callback for filter
 */
function _urlicon_format_class($match) {
  $url = parse_url($match[1]);
  $domain = explode('.', $url['host']);
  $domain = $domain[(count($domain)-2)];
  
  if (stristr($match[0], 'class')) $match[0] = str_replace('class="', 'class="uc-'. check_plain($domain) .' ', $match[0]);
  else $match[0] = str_replace('">', '" class="urlicon urlicon-'. check_plain($domain) .'">', $match[0]);
  
  return $match[0];
}

/**
 * Callback for filter
 */
function _urlicon_format_icon($match) {
  $dir = UI_FILE_PATH;
  
  $url = @parse_url($match[1]);
  $domain = explode('.', $url['host']);
  $domain = check_url(str_replace('.', '_', $url['host']));

  // check for favicon availability
  $favicon = base_path() . drupal_get_path('module', 'urlicon') .'/external_link_icon.png';
  
  $link = theme('urlicon', $match[3], $favicon, $match[1], array('alt' => '', 'class' => 'urlicon urlicon-'. check_plain($domain)));
  return $link;
}

/**
 * Callback for filter
 */
function _urlicon_format_favicon($match) {
  // Define acceptable Content-Types
  // see http://www.iana.org/assignments/media-types/image/vnd.microsoft.icon
  // Additional Content-Types suggested by W3C
  // see http://www.w3.org/2005/10/howto-favicon
  $ui_ctype = array(
    // Suggested by IANA
    'application/ico',
    'application/octet-stream',
    'image/vnd.microsoft.icon',
    'image/ico',
    'image/icon',
    'image/x-icon', 
    'text/ico',
    'text/plain',
    // Suggested by W3C
    'image/gif',
    'image/png',
  );
  $dir = UI_FILE_PATH;
  
  $url = @parse_url($match[1]);
  $domain = explode('.', $url['host']);
  $domain = check_url(str_replace('.', '_', $url['host']));

  //check if favicon exists locally
  if ($url['host'] AND !file_exists($dir .'/'. $domain .'.ico') AND !($user->roles[1])) {
  
    //check for favicon in metatags
    $data = drupal_http_request(check_url($match[1]));
    
    if (preg_match('/<link[^>]+rel="(?:shortcut )?icon"[^>]+?href="([^"]+?)"/si', $data->data, $icons)) {
      
      if (strpos($icons[1], '://')) {
        // absolute path
        $data = drupal_http_request(check_url($icons[1]));
      }
      else if (substr($icons[1], 0, 3) == '../') {
        // relative path
        $path = '';
        $elements = explode('/', $url['path']);
        $i = 0;
        while (!strpos($elements[$i], '.') AND $i <= count($elements)) {
          $path .= $elements[$i] .'/';
          $i++;
        }
        
        $data = drupal_http_request(check_url($url['scheme'] .'://'. $url['host'] . $path . $icons[1]));
      }
      else if (substr($icons[1], 0, 1) == '/') {
        // relative path
        $data = drupal_http_request(check_url($url['scheme'] .'://'. $url['host'] . $icons[1]));
      }
      else {
        // get favicon from webroot
        $data = drupal_http_request(check_url('http://'. $url['host'] .'/favicon.ico'));
        watchdog('urlicon', t('Could not find favicon for URL %url with shortcut url %shortcut, trying webroot.', array('%url' => $match[1], '%shortcut' => $icons[1])), WATCHDOG_ERROR);
      }
      
    }
    else {
      // get favicon from webroot
      $data = drupal_http_request(check_url('http://'. $url['host'] .'/favicon.ico'));
      watchdog('urlicon', t('Could not find favicon for URL %url in metatags, trying webroot.', array('%url' => $match[1])));
    }
    
    // Verify if the favicon was returned
    if (($data->code == '200' OR $data->redirect_code == '200') AND ($data->headers['Content-Length'] > 0 OR $data->headers['Content-length'] > 0)) {
      //check for acceptable Content-Type
      //TODO: refactor code
      $content_type_1 = explode(';', $data->headers['Content-Type']);
      $content_type_2 = explode(';', $data->headers['Content-type']);
      
      if (in_array($content_type_1[0], $ui_ctype) OR in_array($content_type_2[0], $ui_ctype)) {
        //save favicon to file
        file_save_data($data->data, $dir .'/'. $domain .'.ico', FILE_EXISTS_REPLACE);
      }
    }
  }
  
  // check for favicon availability
  $favicon = file_exists($dir .'/'. $domain .'.ico') ? (file_create_url($dir .'/'. $domain .'.ico')) : (base_path().drupal_get_path('module', 'urlicon') .'/external_link_icon.png');
  
  $link = theme('urlicon', $match[3], $favicon, $match[1], array('alt' => '', 'class' => 'urlicon urlicon-'. check_plain($domain)));
  return $link;
}

/**
 * Callback for filter
 */
function _urlicon_format_existingfavicon($match) {
  // Define acceptable Content-Types
  // see http://www.iana.org/assignments/media-types/image/vnd.microsoft.icon
  // Additional Content-Types suggested by W3C
  // see http://www.w3.org/2005/10/howto-favicon

  $dir = UI_FILE_PATH;
  
  $url = @parse_url($match[1]);
  $domain = explode('.', $url['host']);
  $domain = check_url(str_replace('.', '_', $url['host']));

 
  // check for favicon availability
  $favicon = file_exists($dir .'/'. $domain .'.ico') ? (file_create_url($dir .'/'. $domain .'.ico')) : (base_path().drupal_get_path('module', 'urlicon') .'/external_link_icon.png');
  
  $link = theme('urlicon', $match[3], $favicon, $match[1], array('alt' => '', 'class' => 'urlicon urlicon-'. check_plain($domain)));
  return $link;
}

/**
 * Implementation of hook_file_download().
 */
function urlicon_file_download($filepath) {
  // Check if the file is controlled by the current module.
  if (strpos($filepath, 'urlicon') !== FALSE) {
    if (user_access('access content')) {
      // This is an assumption
      return array('Content-type: image/ico/');
    }
  }
  else {
    return -1;
  }
}

/**
 * Settings form
 */
function urlicon_settings($format) {
  $form['urlicon'] = array(
    '#type' => 'fieldset',
    '#title' => t('URL Icon'),
    '#collapsible' => TRUE,
  );
  $form['urlicon']["filter_urlicon_$format"] = array(
    '#type' => 'radios',
    '#title' => t('Filter URLs'),
    '#default_value' => variable_get("filter_urlicon_$format", UI_FORMAT_FAVICON),
    '#options' => array(
      UI_FORMAT_FAVICON          => t('Find all external URLs and append the according favicon by searching them from the web (if available)'),
      UI_FORMAT_EXISTINGFAVICON  => t('Find all external URLs and append the according favicon if it is allready saved'),
      UI_FORMAT_ICON    => t('Find all external URLs and append an <em>external link icon</em>'),
      UI_FORMAT_CLASS   => t('Find all external URLs and add a CSS class only (theme it as you like)'),
    ),
    '#description' => t('Choose what to add to a link in the markup.'),
  );
  
  return $form;
}

/**
 * Implementation of hook_theme().
 */
function urlicon_theme($existing, $type, $theme, $path) {
  return array(
    'urlicon' => array(
      'arguments' => array(
        'text' => NULL,
        'favicon' => NULL,
        'path' => NULL,
        'attributes' => array(),
      ),
    ),
  );
}

/**
 * Return a themed link with a favicon.
 */
function theme_urlicon($text, $favicon, $path, $attributes = array()) {
  $favicon = '<img src="'. $favicon .'" '. drupal_attributes($attributes) .' />';
  return l($favicon.' '.$text, $path, array('absolute' => TRUE, 'html' => TRUE, 'attributes' => array('target' => '_blank')));
}