Module: Crackup
- Defined in:
- lib/crackup.rb,
lib/crackup/driver.rb,
lib/crackup/errors.rb,
lib/crackup/fs_object.rb,
lib/crackup/drivers/ftp.rb,
lib/crackup/file_object.rb,
lib/crackup/drivers/file.rb,
lib/crackup/symlink_object.rb,
lib/crackup/directory_object.rb
Defined Under Namespace
Modules: Driver, FileSystemObject Classes: CompressionError, DirectoryObject, EncryptionError, Error, FileObject, IndexError, StorageError, SymlinkObject
Constant Summary collapse
- GPG_DECRYPT =
'echo :passphrase | :gpg --batch --quiet --no-tty --no-secmem-warning --cipher-algo aes256 --compress-algo bzip2 --passphrase-fd 0 --output :output_file :input_file'- GPG_ENCRYPT =
'echo :passphrase | :gpg --batch --quiet --no-tty --no-secmem-warning --cipher-algo aes256 --compress-algo bzip2 --passphrase-fd 0 --output :output_file --symmetric :input_file'
Instance Attribute Summary collapse
-
#driver ⇒ Object
Returns the value of attribute driver.
-
#local_files ⇒ Object
Returns the value of attribute local_files.
-
#options ⇒ Object
Returns the value of attribute options.
-
#remote_files ⇒ Object
Returns the value of attribute remote_files.
Class Method Summary collapse
-
.compress_file(infile, outfile) ⇒ Object
Reads infile and compresses it to outfile using zlib compression.
-
.debug(message) ⇒ Object
Prints message to
stdoutif verbose mode is enabled. -
.decompress_file(infile, outfile) ⇒ Object
Reads infile and decompresses it to outfile using zlib compression.
-
.decrypt_file(infile, outfile) ⇒ Object
Calls GPG to decrypt infile to outfile.
- .driver ⇒ Object
-
.encrypt_file(infile, outfile) ⇒ Object
Calls GPG to encrypt infile to outfile.
-
.error(message) ⇒ Object
Prints the specified message to
stderrand exits with an error code of 1. -
.escapeshellarg(arg) ⇒ Object
Wraps arg in single quotes (double quotes in Windows), escaping any quotes contained therein, thus making it safe for use as a shell argument.
-
.find_gpg ⇒ Object
Returns the name of the GnuPG executable to use.
-
.find_remote_files(pattern) ⇒ Object
Gets an array of files in the remote file index whose local paths match pattern.
-
.get_list(files) ⇒ Object
Gets a flat array of filenames from files, which may be either a Hash or a Crackup::FileSystemObject.
-
.get_local_files ⇒ Object
Gets a Hash of FileSystemObjects representing the files and directories on the local system in the locations specified by the array of filenames in
options[:from]. -
.get_remote_files(url) ⇒ Object
Gets a Hash of Crackup::FileSystemObjects present at the remote location.
-
.get_removed_files(local_files, remote_files) ⇒ Object
Gets an Array of Crackup::FileSystemObjects representing files and directories that exist at the remote location but no longer exist at the local location.
-
.get_tempfile ⇒ Object
Creates a new temporary file in the system’s temporary directory and returns its name.
-
.get_updated_files(local_files, remote_files) ⇒ Object
Gets an Array of Crackup::FileSystemObjects representing files and directories that are new or have been modified at the local location and need to be updated at the remote location.
- .options ⇒ Object
-
.prompt(message) ⇒ Object
Prints message to
stdoutand waits for user input, which is then returned. -
.remove_files(files) ⇒ Object
Deletes each Crackup::FileSystemObject specified in the files array from the remote location.
-
.update_files(files) ⇒ Object
Uploads each Crackup::FileSystemObject specified in the files array to the remote location.
-
.update_remote_index ⇒ Object
Brings the remote file index up to date with the local one.
Instance Attribute Details
#driver ⇒ Object
Returns the value of attribute driver.
15 16 17 |
# File 'lib/crackup.rb', line 15 def driver @driver end |
#local_files ⇒ Object
Returns the value of attribute local_files.
15 16 17 |
# File 'lib/crackup.rb', line 15 def local_files @local_files end |
#options ⇒ Object
Returns the value of attribute options.
15 16 17 |
# File 'lib/crackup.rb', line 15 def @options end |
#remote_files ⇒ Object
Returns the value of attribute remote_files.
15 16 17 |
# File 'lib/crackup.rb', line 15 def remote_files @remote_files end |
Class Method Details
.compress_file(infile, outfile) ⇒ Object
Reads infile and compresses it to outfile using zlib compression.
20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/crackup.rb', line 20 def self.compress_file(infile, outfile) File.open(infile, 'rb') do |input| Zlib::GzipWriter.open(outfile, 9) do |output| while data = input.read(1048576) do output.write(data) end end end rescue => e raise Crackup::CompressionError, "Unable to compress #{infile}: #{e}" end |
.debug(message) ⇒ Object
Prints message to stdout if verbose mode is enabled.
34 35 36 |
# File 'lib/crackup.rb', line 34 def self.debug() puts if @options[:verbose] || $VERBOSE end |
.decompress_file(infile, outfile) ⇒ Object
Reads infile and decompresses it to outfile using zlib compression.
39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/crackup.rb', line 39 def self.decompress_file(infile, outfile) Zlib::GzipReader.open(infile) do |input| File.open(outfile, 'wb') do |output| while data = input.read(1048576) do output.write(data) end end end rescue => e raise Crackup::CompressionError, "Unable to decompress #{infile}: #{e}" end |
.decrypt_file(infile, outfile) ⇒ Object
Calls GPG to decrypt infile to outfile.
53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/crackup.rb', line 53 def self.decrypt_file(infile, outfile) File.delete(outfile) if File.exist?(outfile) gpg_command = String.new(GPG_DECRYPT) gpg_command.gsub!(':gpg', find_gpg()) gpg_command.gsub!(':input_file', escapeshellarg(infile)) gpg_command.gsub!(':output_file', escapeshellarg(outfile)) gpg_command.gsub!(':passphrase', escapeshellarg(@options[:passphrase])) unless system(gpg_command) raise Crackup::EncryptionError, "Unable to decrypt file: #{infile}" end end |
.driver ⇒ Object
67 68 69 |
# File 'lib/crackup.rb', line 67 def self.driver return @driver end |
.encrypt_file(infile, outfile) ⇒ Object
Calls GPG to encrypt infile to outfile.
72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/crackup.rb', line 72 def self.encrypt_file(infile, outfile) File.delete(outfile) if File.exist?(outfile) gpg_command = String.new(GPG_ENCRYPT) gpg_command.gsub!(':gpg', find_gpg()) gpg_command.gsub!(':input_file', escapeshellarg(infile)) gpg_command.gsub!(':output_file', escapeshellarg(outfile)) gpg_command.gsub!(':passphrase', escapeshellarg(@options[:passphrase])) unless system(gpg_command) raise Crackup::EncryptionError, "Unable to encrypt file: #{infile}" end end |
.error(message) ⇒ Object
Prints the specified message to stderr and exits with an error code of 1.
88 89 90 |
# File 'lib/crackup.rb', line 88 def self.error() abort "#{APP_NAME}: #{}" end |
.escapeshellarg(arg) ⇒ Object
Wraps arg in single quotes (double quotes in Windows), escaping any quotes contained therein, thus making it safe for use as a shell argument.
94 95 96 97 98 99 100 |
# File 'lib/crackup.rb', line 94 def self.escapeshellarg(arg) if RUBY_PLATFORM =~ /mswin32/ return "\"#{arg.gsub('"', '\\"')}\"" else return "'#{arg.gsub("'", "\\'")}'" end end |
.find_gpg ⇒ Object
Returns the name of the GnuPG executable to use. First we search for gpg or gpg.exe in the path. On Windows, if it isn’t in the system path, we try to find a pointer to it in the registry. If everything fails, a Crackup::Error is raised.
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/crackup.rb', line 106 def self.find_gpg # Don't bother finding gpg again if we've already found it. return @gpg_path unless @gpg_path.nil? # First, check to see if gpg is in the path. if RUBY_PLATFORM =~ /mswin32/ path_dirs = ENV['PATH'].split(';') filename = 'gpg.exe' else path_dirs = ENV['PATH'].split(':') filename = 'gpg' end Find.find(*path_dirs) do |path| return @gpg_path = filename if File.executable?(File.join(path, filename)) end # Okay, it's not in the path. Unix users are screwed, but if we're on # Windows, we'll make a last-ditch attempt to find it by checking for its # registry key. if RUBY_PLATFORM =~ /mswin32/ # Bail out if we can't load the Win32::Registry library. unless require('win32/registry') raise Crackup::Error, 'GnuPG not found.' end # Try to read the GnuPG registry key. begin gpg_path = nil Win32::Registry.open(Win32::Registry::HKEY_CURRENT_USER, 'Software\GNU\GnuPG') {|reg| gpg_path = reg.read_s('gpgProgram') } rescue => e raise Crackup::Error, 'GnuPG not found.' end if File.executable?(gpg_path) return @gpg_path = "\"#{gpg_path}\"" end end # No luck. Bail out. raise Crackup::Error, 'GnuPG not found.' end |
.find_remote_files(pattern) ⇒ Object
Gets an array of files in the remote file index whose local paths match pattern.
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/crackup.rb', line 152 def self.find_remote_files(pattern) files = [] pattern.chomp!('/') @remote_files.each do |name, file| if File.fnmatch?(pattern, file.name) files << file next end files += file.find(pattern) if file.is_a?(Crackup::DirectoryObject) end return files end |
.get_list(files) ⇒ Object
Gets a flat array of filenames from files, which may be either a Hash or a Crackup::FileSystemObject.
170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/crackup.rb', line 170 def self.get_list(files) list = [] if files.is_a?(Hash) files.each_value {|value| list += get_list(value) } elsif files.is_a?(Crackup::FileSystemObject) list += files.to_s.split("\n") end return list.sort end |
.get_local_files ⇒ Object
Gets a Hash of FileSystemObjects representing the files and directories on the local system in the locations specified by the array of filenames in options[:from].
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/crackup.rb', line 185 def self.get_local_files local_files = {} @options[:from].each do |filename| next unless File.exist?(filename = filename.chomp('/')) next if local_files.has_key?(filename) # Skip this file if it's in the exclusion list. unless @options[:exclude].nil? next if @options[:exclude].any? do |pattern| File.fnmatch?(pattern, filename) end end debug "--> #{filename}" local_files[filename] = Crackup::FileSystemObject.from(filename) end return local_files end |
.get_remote_files(url) ⇒ Object
Gets a Hash of Crackup::FileSystemObjects present at the remote location.
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/crackup.rb', line 207 def self.get_remote_files(url) tempfile = get_tempfile() # Download the index file. begin @driver.get(url + '/.crackup_index', tempfile) rescue => e return {} end # Decompress/decrypt the index file. oldfile = tempfile tempfile = get_tempfile() if @options[:passphrase].nil? begin decompress_file(oldfile, tempfile) rescue => e raise Crackup::IndexError, "Unable to decompress index file. Maybe " + "it's encrypted?" end else begin decrypt_file(oldfile, tempfile) rescue => e raise Crackup::IndexError, "Unable to decrypt index file." end end # Load the index file. file_list = {} begin File.open(tempfile, 'rb') {|file| file_list = Marshal.load(file) } rescue => e raise Crackup::IndexError, "Remote index is invalid!" end unless file_list.is_a?(Hash) raise Crackup::IndexError, "Remote index is invalid!" end return file_list end |
.get_removed_files(local_files, remote_files) ⇒ Object
Gets an Array of Crackup::FileSystemObjects representing files and directories that exist at the remote location but no longer exist at the local location.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/crackup.rb', line 255 def self.get_removed_files(local_files, remote_files) removed = [] remote_files.each do |name, remotefile| unless local_files.has_key?(name) removed << remotefile next end localfile = local_files[name] if remotefile.is_a?(Crackup::DirectoryObject) && localfile.is_a?(Crackup::DirectoryObject) removed += get_removed_files(localfile.children, remotefile.children) end end return removed end |
.get_tempfile ⇒ Object
Creates a new temporary file in the system’s temporary directory and returns its name. All temporary files will be deleted when the program exits.
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/crackup.rb', line 277 def self.get_tempfile # We would use Ruby's tempfile library here, but for some reason it # sometimes deletes temp files before the program exits, which can cause all # kinds of problems. i = -1 while tempfile = File.join(Dir.tmpdir(), ".crackup.#{Process.pid}.#{i += 1}") do break unless File.exist?(tempfile) end at_exit do begin File.delete(tempfile) rescue => e end end return tempfile end |
.get_updated_files(local_files, remote_files) ⇒ Object
Gets an Array of Crackup::FileSystemObjects representing files and directories that are new or have been modified at the local location and need to be updated at the remote location.
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/crackup.rb', line 301 def self.get_updated_files(local_files, remote_files) updated = [] local_files.each do |name, localfile| # Add the file to the list if it doesn't exist at the remote location. unless remote_files.has_key?(name) updated << localfile next end remotefile = remote_files[name] if localfile.is_a?(Crackup::DirectoryObject) && remotefile.is_a?(Crackup::DirectoryObject) # Add to the list all updated files contained in the directory and its # subdirectories. updated += get_updated_files(localfile.children, remotefile.children) elsif localfile != remotefile updated << localfile end end return updated end |
.options ⇒ Object
326 327 328 |
# File 'lib/crackup.rb', line 326 def self. return @options end |
.prompt(message) ⇒ Object
Prints message to stdout and waits for user input, which is then returned.
332 333 334 335 |
# File 'lib/crackup.rb', line 332 def self.prompt() puts + ': ' return $stdin.gets end |
.remove_files(files) ⇒ Object
Deletes each Crackup::FileSystemObject specified in the files array from the remote location.
339 340 341 |
# File 'lib/crackup.rb', line 339 def self.remove_files(files) files.each {|file| file.remove } end |
.update_files(files) ⇒ Object
Uploads each Crackup::FileSystemObject specified in the files array to the remote location.
345 346 347 |
# File 'lib/crackup.rb', line 345 def self.update_files(files) files.each {|file| file.update } end |
.update_remote_index ⇒ Object
Brings the remote file index up to date with the local one.
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/crackup.rb', line 350 def self.update_remote_index tempfile = get_tempfile() remotefile = @options[:to] + '/.crackup_index' File.open(tempfile, 'wb') {|file| Marshal.dump(@local_files, file) } oldfile = tempfile tempfile = get_tempfile() if @options[:passphrase].nil? compress_file(oldfile, tempfile) else encrypt_file(oldfile, tempfile) end begin success = @driver.put(remotefile, tempfile) rescue => e tryagain = prompt('Unable to update remote index. Try again? (y/n)') retry if tryagain.downcase == 'y' raise Crackup::IndexError, "Unable to update remote index: #{e}" end end |