#!/usr/bin/env ruby # # dcp: copy files to/from dropbox like scp command # # Copyright (C) 2017 by Tada, Tadashi # Distributed under GPL # require 'dcp' require 'optparse' require 'pit' module DCP class MultiError < StandardError attr_reader :messages def initialize(messages) @messages = messages end end class CLI def run opts, args = parse_options dst = args.pop begin precheck(args, dst) rescue MultiError => e e.messages.each{|m| error m} exit 1 rescue => e error e.message exit 1 end begin dst = dst.chop if dst =~ /\/\z/ # root directory => '' args.each do |src| open(src, 'r') do |r| d = directory?(dst) ? "#{dst}/#{File.basename(src)}" : dst open(d, 'w') do |w| while data = r.read(10_000_000) do w.write(data) print '.' unless opts[:quiet] end end puts unless opts[:quiet] end end rescue => e usage "#{e.message} (#{e.class})" end end private def error(msg) $stderr.puts "dcp: #{msg}" end def parse_options(argv = ARGV) op = OptionParser.new self.class.module_eval do define_method(:usage) do |msg = nil| puts op.to_s error msg if msg exit 1 end end opts = { quiet: false } op.on('-q', '--quiet', "quiet mode") do opts[:quiet] = true end op.on('-h', '--help', "print this messages") do usage end op.banner += ' [db:]SRC [SRC...] [db:]DEST' begin args = op.parse(argv) rescue OptionParser::InvalidOption => e usage e.message end usage 'needs src and dest' if args.size < 2 return opts, args end def dropbox return @dropbox if @dropbox auth = ::Pit::get('dcp') unless auth[:dropbox_token] print "Enter dropbox app key: " api_key = $stdin.gets.chomp print "Enter dropbox app secret: " api_secret = $stdin.gets.chomp authenticator = ::DropboxApi::Authenticator.new(api_key, api_secret) puts "\nGo to this url and click 'Authorize' to get the token:" puts authenticator.authorize_url print "Enter the token: " code = $stdin.gets.chomp auth[:dropbox_token] = authenticator.get_token(code).token Pit::set('dcp', data: auth) end return @dropbox = Dropbox.new(auth[:dropbox_token]) end def dropbox_path?(path) path =~ /\A(db|dropbox):/ end def dropbox_path(file) raise StandardError.new("not a dropbox path: #{file}") unless dropbox_path?(file) file.split(/:/, 2)[1] end def precheck(srcs, dst) errors = [] srcs.each do |src| begin if dropbox_path?(src) errors << "cannot copy from dropbox: #{src}" else if directory?(src) errors << "cannot copy from directory: #{src}" end end rescue Errno::ENOENT errors << "file not found: #{src}" end end begin if directory?(dst) && srcs.size > 1 errors << "cannot copy multiple files into a file: #{dst}" end rescue Errno::ENOENT errors << "file not found: #{dst}" end raise MultiError.new(errors) unless errors.empty? end def open(file, opts) if dropbox_path?(file) dropbox.open(dropbox_path(file), opts){|f| yield f} else File.open(file, opts){|f| yield f} end end def directory?(file) if dropbox_path?(file) dropbox.directory?(dropbox_path(file)) else File.stat(file).directory? end end end end DCP::CLI.new.run