Skip to content

Instantly share code, notes, and snippets.

@majioa
Created January 23, 2013 07:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save majioa/4602862 to your computer and use it in GitHub Desktop.
Save majioa/4602862 to your computer and use it in GitHub Desktop.
Simple FTP mirroring tool in Ruby
#!/usr/bin/ruby
require 'net/ftp'
require 'net/http'
require 'fileutils'
class String
def to_l(srcl,dstl)
val = gsub(/(["'\&\(\)])/) { "\\" + $1 }
`echo #{val} |iconv -f #{srcl} -t #{dstl}`.sub(/\n/,"")
end
end
class Logger
def initialize(verbose,file)
@verbose = verbose
@log = File.open(file,'a') if file
end
def verbose(msg, level = 0)
if @verbose >= level
puts msg
if @log
@log.write(msg + "\n")
@log.flush
end
end
end
def error(msg)
verbose('Exiting: ' + msg)
exit
end
end
class Net::FTP
attr_writer :log, :simulate
def mirror(folder)
@log.verbose("Curdir #{FileUtils.pwd}\nRemdir #{pwd}",2)
name = folder.to_l("cp1251","utf-8")
unless @simulate then
begin
FileUtils.cd(name)
rescue Errno::ENOENT
FileUtils.makedirs(name)
retry
end
end
@log.verbose("Folder #{name}",1)
chdir(folder)
list.each {|line|
@log.verbose("Line #{line}",3)
line =~ /^([d-])[rwx-]{1,9} +\d+ \w+ +\w+ +(\d+) (\w{3} +/
/\d{1,2} +[\d:]+) (.*)$/
next if $4 == "." or $4 == ".."
if $1 == "d" then
@log.verbose("Down to folder #{$4}",2)
mirror($4)
else
file = $4.to_l("cp1251","utf-8")
@log.verbose("File sizes: local = #{File.size?(file)}, "
"remote = #{$2.to_i} ",2)
if !@simulate and File.size?(file) == $2.to_i then
@log.verbose("File #{file} is exist and the same "
"as the remote: not downloading",1)
next;
end
@log.verbose("Downloading file #{file}",1)
begin
getbinaryfile($4, $4.to_l("cp1251","utf-8"), 1024)
unless @simulate
rescue Errno::EEXIST
next
end
end
}
FileUtils.cd("..")
chdir("..")
end
end
class App
def initialize
log = '/local/var/log/mirrorer.log'
@log = Logger.new(3,log)
p = `ps aux`
c = 0
val = $0.gsub(/.*\//,"")
while p =~ /ruby.*#{val}/o
c = c + 1
p = p.sub(/#{val}/o, "")
end
@log.error("Duplex run") if c > 1
end
end
class Mirrorer < App
def initialize(*args)
super
sites = '/local/etc/sites'
#init vars
@simulate = nil
@simulate = true
@root_folder = "/local/share/mirrors"
begin
@sites = IO.read(sites);
rescue Errno::ENOENT
@log.error("File #{sites} doesn't exist. Nothing to mirror")
end
#change to specified path
retries = 0
begin
FileUtils.cd(@root_folder)
rescue Errno::ENOENT
begin
FileUtils.mkdir(@root_folder)
rescue Errno::EACCES
@log.error("Can't create the specfied mirror root folder"
" '#{@root_folder}'")
end
retry
end
end
def mirror
@sites.each { |line|
FileUtils.cd(@root_folder)
next if line =~ /#.*/
@log.verbose("Reading the new line",2)
if line =~ /^(.*) +(.*?)$/ then
site = $1
folder_path = $2
else
site = line
end
@log.error "Wrong site name, please check"
if (site.gsub(/%(..)/) {|s|
$1.hex.chr } !~ /^ *(ftp|http):\/\/(?:(.*?)\/)?
(?:(.*)\/)*(?:(.*)\/?)?$/)
ftp_addr = $2
folder_path = $2 + '/' + $3.to_s.to_l("cp1251","utf-8") if !folder_path
remote_dir = $3
folder = $4
@log.verbose("Connecting to site #{ftp_addr}",0)
@log.verbose("Site = #{ftp_addr.to_l("cp1251","utf-8")}, "
"folder = #{folder_path}, folder = "
"#{folder.to_l("cp1251","utf-8")}",2)
@log.verbose("#{ftp_addr}, #{folder_path}, #{remote_dir}, "
"#{folder}",2)
begin
ftp = Net::FTP.new ftp_addr.gsub(/\//,"")
rescue Errno::ECONNREFUSED
@log.error "Connection to the server #{ftp_addr.gsub(/\//,"")}"
" is refused"
end
ftp.log = @log
ftp.simulate = @simulate
begin
ftp.login
rescue Net::FTPTempError
@log.verbose('421 Too many users - please try again later',0)
next
end
begin
FileUtils.cd folder_path
rescue Errno::ENOENT
begin
FileUtils.makedirs folder_path
rescue Errno::ENOACC
@log.error "Can't create target directories"
end
retry
end
ftp.chdir(remote_dir) if remote_dir
ftp.mirror(folder)
ftp.close
}
end
end
mirrorer = Mirrorer.new
mirrorer.mirror
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment