#!/bin/bash
#---------------------------------------------------------------------
##
## @Synopsis Functions that download and verify urls.
##
##
## This file contains functions for downloading and verifying urls.
## It does this by fowarding url download and verification requests to
## the appropriate url handler.  For example, the request to download
## the following url is made through the generic F<url_download>
## function:
##
##     http://machinename.com/path/to/file.tar.bz2
##
## The F<url_download> function parses the url prefix (in this
## case, http) and passes the url to the http download handler
## (F<url_http_download>).  A similar approach is used for url 
## verification.
##
## This file provides an infrastructure that makes it relatively easy
## to add new url handlers.  In order to add new handlers, all that
## has to be done is add a new file to the sorcerer library directory
## with the new url handler functions defined in the file.  This new
## file will automatically be discovered and used by the sorcerer 
## scripts.
##
## The following section describes how to add new url handlers in
## a little more detail.
##
## <p>WRITING NEW URL HANDLERS</p>
##
## This section describes the steps needed to write new url handlers.
##
## <p>Decide on the Url Format</p>
##
## Urls must be of the form <prefix>://<address>.  The prefix should
## be something unique.  Only the prefix is used by this script,
## the address is not parsed or used, simply passed to the appropriate
## url handler.
##
## <p>Create a File to Hold the New Url Handling Functions</p>
##
## In the SGL library directory (i.e., the directory pointed to by the
## SGL_LIBRARY variable), create a new file called url_<prefix>.  For
## example, if your new url prefix is I<xyz>, you should create a new
## file called F<url_xyz>.  The file should be executable.
##
## <p>Implement Url Handlers</p>
##
## The next (and final) step is to write the actual functions that
## will handle url requests and put them in the new file you just
## created.  The functions that must be implemented are:
##
##    url_<URL_PREFIX>_download <url>
##    url_<URL_PREFIX>_verify <url>
##    url_<URL_PREFIX>_set_options <option>
##    url_<URL_PREFIX>_help
##    url_<URL_PREFIX>_hostname <url>
##    url_<URL_PREFIX>_netselect <url>
##
## The easiest way to figure out what to do is to look at
## one of the existing files (e.g., url_http handles http requests).
##
## <p>Handling Multiple Url Types in a Single File</p>
##
## It's perfectly valid for a file to handle mutlple types of urls.
## The F<url_http> file actually handles ftp, http, and https urls.
## Take a look at the file to see how it's done.
##
##
## @Copyright Copyright 2002 by the Source Mage Team
##
#---------------------------------------------------------------------

#---------------------------------------------------------------------
# Load library files (url_*) that contain url handlers
#
# (2002/09/29) added if so it onlt loads the stuff once
#---------------------------------------------------------------------
if ! [[ $URL_HANDLER_FILES ]] ; then
  URL_HANDLER_FILES=`ls $SGL_LIBRARY_MODULES/url_handlers/url_*[^~]`
  for  url_handler_file  in  $URL_HANDLER_FILES;  do
    [  -x $url_handler_file  ]                               && 
    URL_PREFIX=`echo $url_handler_file | sed "s/.*\/url_//"` && 
    URL_TYPES[${#URL_TYPES[@]}]=$URL_PREFIX                  &&
    . $url_handler_file
  done
fi


#---------------------------------------------------------------------
## url_name_is_function <function name>
## @param function name
## @Type Private
## @return 0 if argument is a valid function
## @return 1 otherwise
## Returns true if input argument is the name of an
## existing function, false otherwise.
##
#---------------------------------------------------------------------
function url_is_function()  {
  local  FUNCTION_NAME=$1
  [  "`type -t $FUNCTION_NAME`"  =  "function"  ]
}


#---------------------------------------------------------------------
## @Type Private
## @param url 
## @Stdout url prefix
## @return 0 valid url
## @return 1 otherwise
## Takes a url and echos the url prefix.  Returns
## true if a valid url could be found, returns false otherwise.
##
## This is the only place parsing of a url should take place outside of
## a url_handler. Doing so elsewhere is bugworthy.
##
#---------------------------------------------------------------------
function url_get_prefix()  {
  local URL=$1
  local  URL_PREFIX=`echo $URL | sed "s/[\:\/\/].*$//"` 
  [  -n  "$URL_PREFIX"  ]  &&
  echo    $URL_PREFIX
}

#---------------------------------------------------------------------
## @Type Private
## @param url 
## @Stdout url hostname
## @return 0 valid url
## @return 1 otherwise
## Takes a url and echos the url hostname. Returns
## true if a hostname could be found, returns false otherwise.
##
#---------------------------------------------------------------------
function url_get_hostname()  {
  local tmp_url=$1 tmp_prefix
  tmp_prefix=$(url_get_prefix $tmp_url)                 &&
  url_is_function url_${tmp_prefix}_hostname            &&
  url_${tmp_prefix}_hostname $tmp_url
}

#---------------------------------------------------------------------
## @Type Private
## @param url 
## @Stdout url netslect output
## @return 0 valid url
## @return 1 otherwise
## Prints the netselect output from the url handlers attempt at
## running netselect.
##
#---------------------------------------------------------------------
function url_get_netselect()  {
  local tmp_url=$1 tmp_prefix
  tmp_prefix=$(url_get_prefix $tmp_url)                 &&
  url_is_function url_${tmp_prefix}_hostname            &&
  url_${tmp_prefix}_netselect $tmp_url
}


#---------------------------------------------------------------------
## @Type Private 
## @Global BASE_URL FILE
## If BASE_URL is set, then change URL to 
## use BASE_URL as the download location.  Also sets FILE variable.
#---------------------------------------------------------------------
function url_change_base()  {
  if  [  -n  "$BASE_URL"  ]  &&  [  -n  "$FILE"  ];  then  
    URL="$BASE_URL/`basename $FILE`"
  fi
}


#---------------------------------------------------------------------
## @param url
## @return 0 valid url
## @return 1 otherwise
## Returns true if the given url is a valid url understood by the url
## library, returns false otherwise.
##
#---------------------------------------------------------------------
function url_is_valid()  {
  [ "$1" == " " ] && return 1 #If it's only a space, it's not valid
  local  URL_PREFIX=`echo $1 | sed "s/[\:\/\/].*$//"` 
  [  -n  "$URL_PREFIX"  ]  &&
  url_is_function "url_${URL_PREFIX}_download"
}


#---------------------------------------------------------------------
## @param urllist
## @Stdout new list
## Ranks the urls in order of speed from fastest to slowest using netselect
## Makes use of url_<url_type>_hostname functions.
##
## T
##
## If multiple urls from the same hostname are passed in, their ordering
## is preserved, although that group of urls may move as a whole in
## the list.
#---------------------------------------------------------------------
function url_rank() {

  local urlList sortedList urlCounter
  local finalList url_speed
  local tmp_url tmp_prefix tmp_hostname tmp_list

  debug "liburl" "unsorted list: $*"

  urlList="$*"

  # Even if theres one url it might have multiple A records and we'll
  # want netselect to find the fastest one

  # The take home message is that a url can be /any/ random string of text,
  # and a url is officially defined by its handler. 

  # So we are having the handler give us the netselected output
  # from there we are sticking things into an array and letting
  # bash handle the sorting for us...
  function url_rank_divide_by_prefix() {
    local tmp_url=$1 tmp_prefix
    tmp_prefix=$(url_get_prefix $tmp_url)                 &&
    url_is_function url_${tmp_prefix}_hostname            &&
    hash_append url_div_hash $tmp_prefix $tmp_url
  }
  iterate url_rank_divide_by_prefix " $IFS" $urlList

  for tmp_prefix in $(hash_get_table_fields url_div_hash) ; do
    tmp_list=$(hash_get url_div_hash $tmp_prefix)
    debug liburl "urls are $tmp_list"
    # the awk command turns netselect output like this:
    # 69 url1
    # 234 url2
    # into the following bash code, which is then executed
    # url_speed[69]="${url_speed[69]} url1"
    # url_speed[234]="${url_speed[234]} url2"
    eval $(url_${tmp_prefix}_netselect $tmp_list|
      awk '{printf "url_speed[%s]=\"${url_speed[%s]} %s\";",$1,$1,$2}')
  done
  
  # since we put things in url_speed indexed by speed, the * should
  #expand back in sorted order

  sortedList=$(echo ${url_speed[*]})
  debug "liburl" "Ordered list pre-sanity check is $sortedList"

  # if all sites are ICMP Unreachable, return the unsorted list instead of null.
  if [[ -z "$sortedList" ]] ; then
    echo $urlList
    return
  fi

  # try really hard not to lose urls by appending missing ones to the end
  # of the list, please tell me if theres a faster way to do this
  function url_rank_iterator2() {
    echo $sortedList|grep -q "$1" || sortedList="$sortedList $1"
  }
  iterate url_rank_iterator2 " $IFS" $urlList

  # just in case something failed along the way just return what we had
  if [[ -z "$sortedList" ]] ; then
    debug "liburl" "Ordering failed somewhere, giving back original input"
    echo $urlList
  else
    debug "liburl" "Ordered URLs: $sortedList"
    echo $sortedList
  fi
}



#---------------------------------------------------------------------
## @param url
## @return 0 if file could be downloaded
## @return 1 otherwise
## @Global FILE
## Downloads the specified url.  Returns true if file could be 
## downloaded, false otherwise.  The name of the downloaded file
## is left in the FILE variable.
##
#---------------------------------------------------------------------
function url_download() {
  local URL_LIST=$*
  local SUCCESS=false

  debug "liburl" "url_download() $*"

  # you can't expect me to download something without urls...
  [ -z "$URL_LIST" ] && return 1
  
  if (  [[ $NET_SELECT == on ]]  &&  spell_installed "netselect"  );  then
    lock_resources "network" "network"
    message -n "${CHECK_COLOR}Looking for the fastest mirror site...${DEFAULT_COLOR} "
    URL_LIST=`url_rank $URL_LIST 2> /dev/null` 
    unlock_resources "network" "network"
    message " done."
  fi

  for URL in $URL_LIST; do
    if  url_is_valid $URL                             &&
        url_change_base                               &&
        URL_PREFIX=`url_get_prefix $URL`              &&
        url_is_function "url_${URL_PREFIX}_download"  && 
        url_${URL_PREFIX}_download $URL;              then
        SUCCESS=true
        break;
    fi
  done


  $SUCCESS
}


#---------------------------------------------------------------------
## @param url
## @return 0 url could be verified
## @return 1 otherwise
## Verifies the specified url.  Returns true if file could be 
## verified, false otherwise.
##
#---------------------------------------------------------------------
function url_verify() {
  local URL=$1
  
  url_change_base
  URL_PREFIX=`url_get_prefix $URL`            &&
  url_is_function "url_${URL_PREFIX}_verify"  && 
  url_${URL_PREFIX}_verify $URL
}


#---------------------------------------------------------------------
## @param option 
## Set options for url handling
##
#---------------------------------------------------------------------
function url_set_options() {
  for URL_TYPE in ${URL_TYPES[@]}; do
    url_is_function url_${URL_TYPE}_set_options &&
    url_${URL_TYPE}_set_options $@
  done
}


#---------------------------------------------------------------------
## @Stdout help message 
## Prints help message regarding url handling
##
#---------------------------------------------------------------------
function url_help() {
  for URL_TYPE in ${URL_TYPES[@]}; do
    url_is_function url_${URL_TYPE}_help &&
    url_${URL_TYPE}_help
  done
}


#---------------------------------------------------------------------
## This software is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This software is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this software; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#---------------------------------------------------------------------

