#!/usr/bin/env ruby

#### your data ####
STORE_BASE_DIRECTORY = "The path of the directory where you'll store all the downloaded files."
DOWNLOAD_TO_CENTRAL_PLACE = "The path of a central place to which all new files shall be loaded (e.g. Desktop)" # set to 'false' to download to STORE_BASE_DIRECTORY
ACCOUNTS = {
  :university => { :user => 'username' }
}
PAGES = [
  { :id => 'x',
    :directory => 'x',
    :account => :university,
    :url => 'http://www.example.com',
    :css_to_link => 'body a'}
]

#### code ####

require 'rubygems'
require 'hpricot'
require 'open-uri'
require 'highline/import'
require 'fileutils'
require 'ping'

def authentication
  @account ? { :http_basic_authentication => [@accounts[@account][:user], get_password] } : {}
end

def document
  open(@page[:url], authentication) { |f| Hpricot(f) }
end

def get_file_from(url)
  File.open(target_file, "wb") { |file| file.write(open(url, authentication).read) }
end

def get_password
  @accounts[@account][:pass] ||= ask("Please enter the password for the account '#{@account}': ") { |q| q.echo = false }
end

def target_file
  DOWNLOAD_TO_CENTRAL_PLACE ? new_file : store_file
end

def new_file
  DOWNLOAD_TO_CENTRAL_PLACE ? File.join(DOWNLOAD_TO_CENTRAL_PLACE, @filename) : ''
end

def store_file
  parent_directory = File.join STORE_BASE_DIRECTORY, @page[:directory]
  FileUtils.mkdir parent_directory unless File.directory?(parent_directory)
  File.join parent_directory, @filename
end

def fetch_file(link)
  @filename = File.basename link[:href]
  url = URI.join @page[:url], URI.escape(link[:href])
  
  if File.file?(new_file) or File.file?(store_file)
    puts "- already loaded: " + @filename
  else
    print '- downloading: ' + target_file + ' ... '
    get_file_from(url)
    print "done.\n"
  end
end

def internet?
  test_host = URI.parse(PAGES[0][:url]).host
  Ping.pingecho(test_host, 3, 80) or raise 'Not connected to the internet.'
end

#### script ####
begin
  internet?
  @accounts = ACCOUNTS.dup
  
  PAGES.each do |page| 
    @page = page
    @account = page[:account]

    puts "\n# #{page[:id]}:"
    (document/page[:css_to_link]).each(&self.method(:fetch_file))
  end

  # reset password cache
  @accounts = nil
rescue Exception => e
  message = e.respond_to?(:message) ? e.message : e.inspect
  puts "\nFatal: " + message.to_s + " (at page '#{@page[:id]}')"
  puts "-> check the password for #{@account}!" if e.message =~ /401/
end
