# LICENSE (The MIT License)
# 
# Copyright (c) 2009 SIN-SOFT
# 
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# 'Software'), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


# Main Controller of gdocs-backup, a small rails app for backing up
# all Google Docs into a zip archive. Uses gdata gem for Google API.

class BackupGoogleDocsController < ApplicationController
  
  verify :method => :post, :only => :backup, :redirect_to => '/404.html'
  
  caches_action :source
  helper_method :authsub_url
  before_filter :setup_client
  
  def index
    upgrade_session if params[:token]
  end
  
  def backup
    backup_docs
  end
  
  def source
    render :inline => "<html><head><title>Google Docs Backup : Source</title><%= stylesheet_link_tag :harsh %></head>" +
                      "<body><% harsh File.read('#{__FILE__}') %></body></html>"
  end
  
private

  DocsPaths = {
    'document'      => 'https://docs.google.com/feeds/download/documents/Export?exportFormat=%s&docID=%s',
    'presentation'  => 'https://docs.google.com/feeds/download/presentations/Export?exportFormat=%s&docID=%s',
    'spreadsheet'   => 'https://spreadsheets.google.com/feeds/download/spreadsheets/Export?exportFormat=%s&key=%s&gid=0',
    'pdf'           => 'content tag src attribtue',
  }
  
  ValidExtensions = {
    'document'      => ["odt", "doc", "pdf", "html", "rtf", "txt", "png", "zip"],
    'presentation'  => ["ppt", "pdf", "swf", "png"],
    'spreadsheet'   => ["ods", "xls", "pdf", "html"], # "csv" and "tsv" need gid
    'pdf'           => ["pdf"],
  }
  
  class TokenMissing < RuntimeError; end
  
  def setup_client
    @scope  = ['https://docs.google.com/feeds', 'https://spreadsheets.google.com/feeds', 'https://docs.googleusercontent.com'].join(' ')
    @client = GData::Client::DocList.new(:authsub_scope => @scope, :source => 'gdocs-backup-v2', :version => '3.0')
  end
  
  def authsub_url(secure=false)
    @client.authsub_url(root_url, secure, true)
  end
  
  def upgrade_session
    @client.authsub_token = params[:token]
    # @client.authsub_private_key = File.join(RAILS_ROOT, 'key.pem')
  
    session[:token] = @client.auth_handler.upgrade
    redirect_to root_path

  rescue GData::Client::AuthorizationError
    auth_error
  end
  
  def backup_docs
    raise TokenMissing unless session[:token]
    params[:ext] = {}  unless params[:ext].is_a?(Hash)
    
    @client.authsub_token = session[:token]
    
    file   = Tempfile.new("gdocs-backup")
    zip    = Zip::ZipOutputStream.new(file.path)
    list   = @client.get('https://docs.google.com/feeds/default/private/full').to_xml

    names  = []
    ext    = {}

    ValidExtensions.each do |label, ext_list|
      ext[label] = ext_list.include?(params[:ext][label]) ? params[:ext][label] : ext_list.first
    end
    
    REXML::XPath.each(list, '//feed/entry') do |entry|
      resource_id   = entry.elements['gd:resourceId'].text
      doc_title     = entry.elements['title'].text
      label, doc_id = resource_id.split(/:/,2)
    
      next  unless DocsPaths[label]
      doc_path   = DocsPaths[label] % [ext[label], doc_id]
      doc_path   = entry.elements['content'].attributes['src'] if label == 'pdf'
      
      clean_name = [doc_title.clean, ext[label]].join('.')
      doc_name   = clean_name
      doc_idx    = 0
      doc_name   = clean_name + "(#{doc_idx+=1})" while names.include?(doc_name)
      names << doc_name
      
      zip.put_next_entry(doc_name)
      zip << @client.get(doc_path).body
    end
    zip.close

    log_size  file.size # log the size of the package, so we know how heavy on bandwidth is the app
    send_file file.path, :type => 'application/zip', :disposition => 'attachment', :filename => "gdocs-backup.zip"
    file.close

  rescue GData::Client::AuthorizationError, TokenMissing
    auth_error
  end
  
  def auth_error
    session[:token] = nil
    flash[:error]   = "Google Authorization Error - Please Try Again"
    redirect_to root_path
  end
  
  def log_size(file_size)
    # yea, yea, yea - i *do* log sth about the docs, it's zip size :P
    logger.info("==== %s ==== package size: %d (%.2fM) ====" % [Time.now.to_s, file_size, file_size.to_f / 1024 / 1024])
  end
  
end