Class: Sprout::ArchiveUnpacker

Inherits:
Object
  • Object
show all
Includes:
Archive::Tar
Defined in:
lib/sprout/archive_unpacker.rb

Overview

Unpack downloaded files from a variety of common archive types. This library should efficiently extract archived files on OS X, Win XP, Vista, DOS, Cygwin, and Linux.

It will attempt to infer the archive type by standard mime-type file extensions, but if there is a file with no extension, the unpack_archive method can be provided with an @archive_type symbol argument that is one of the following values:

:exe
:zip
:targz
:gzip
:swc
:rb
:dmg

Instance Method Summary collapse

Instance Method Details

#inferred_archive_type(file_name) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/sprout/archive_unpacker.rb', line 184

def inferred_archive_type(file_name)
  if is_zip?(file_name)
    return :zip
  elsif is_targz?(file_name)
    return :targz
  elsif is_gzip?(file_name)
    return :gz
  elsif is_swc?(file_name)
    return :swc
  elsif is_rb?(file_name)
    return :rb
  elsif is_dmg?(file_name)
    return :dmg
  elsif is_exe?(file_name)
    return :exe
  else
    return nil
  end
  
end

#is_dmg?(file) ⇒ Boolean

Returns:

  • (Boolean)


227
228
229
# File 'lib/sprout/archive_unpacker.rb', line 227

def is_dmg?(file)
  return (file.split('.').pop == 'dmg')
end

#is_exe?(file) ⇒ Boolean

Returns:

  • (Boolean)


231
232
233
# File 'lib/sprout/archive_unpacker.rb', line 231

def is_exe?(file)
  return (file.split('.').pop == 'exe')
end

#is_gzip?(file) ⇒ Boolean

Returns:

  • (Boolean)


215
216
217
# File 'lib/sprout/archive_unpacker.rb', line 215

def is_gzip?(file)
  return (file.split('.').pop == 'gz')
end

#is_rb?(file) ⇒ Boolean

Returns:

  • (Boolean)


223
224
225
# File 'lib/sprout/archive_unpacker.rb', line 223

def is_rb?(file)
  return (file.split('.').pop == 'rb')
end

#is_swc?(file) ⇒ Boolean

Returns:

  • (Boolean)


219
220
221
# File 'lib/sprout/archive_unpacker.rb', line 219

def is_swc?(file)
  return (file.split('.').pop == 'swc')
end

#is_targz?(file) ⇒ Boolean

Returns:

  • (Boolean)


209
210
211
212
213
# File 'lib/sprout/archive_unpacker.rb', line 209

def is_targz?(file)
  parts = file.split('.')
  part = parts.pop
  return (part == 'tgz' || part == 'gz' && parts.pop == 'tar')
end

#is_zip?(file) ⇒ Boolean

Returns:

  • (Boolean)


205
206
207
# File 'lib/sprout/archive_unpacker.rb', line 205

def is_zip?(file)
  return (file.split('.').pop == 'zip')
end

#suffix_for_archive_type(type) ⇒ Object



176
177
178
179
180
181
182
# File 'lib/sprout/archive_unpacker.rb', line 176

def suffix_for_archive_type(type)
  if(type == :targz)
    return '.tar.gz'
  else
    return ".#{type.to_s}"
  end
end

#unpack_archive(file_name, dir, force = false, archive_type = nil) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/sprout/archive_unpacker.rb', line 24

def unpack_archive(file_name, dir, force=false, archive_type=nil)
  archive_type ||= inferred_archive_type(file_name)
  suffix = suffix_for_archive_type(archive_type)
  
  unpacked = unpacked_file_name(file_name, dir, suffix)
  if(File.exists?(unpacked) && force)
    FileUtils.rm_rf(unpacked)
  end
  
  if(!File.exists?(unpacked))
    case archive_type.to_s
      when 'zip'
        unpack_zip(file_name, dir)
      when 'targz'
        unpack_targz(file_name, dir)
      when 'tbz2'
        unpack_tbz2(file_name, dir)
      when 'dmg'
        unpack_dmg(file_name, dir)
      when 'exe'
        FileUtils.mkdir_p(dir)
        FileUtils.move(file_name, dir)
      when 'swc' || 'rb'
        return
      else
        raise ArchiveUnpackerError.new("ArchiveUnpacker does not know how to unpack files of type: #{archive_type} for file_name: #{file_name}")
    end
  end
end

#unpack_dmg(dmg_file, dir) ⇒ Object

This is actually not unpacking the FlashPlayer Binary file as expected… OSX is treated the player binary as if it is a regular text file, but if it is copied manually, the command works fine!?



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/sprout/archive_unpacker.rb', line 143

def unpack_dmg(dmg_file, dir)
  # 1) Mount the dmg in place
  # 2) Recursively Copy its contents to asproject_home
  # 3) Unmount the dmg
  if(mounted_path.nil?)
    raise StandardError.new('DMG file downloaded, but the RemoteFileTask needs a mounted_path in order to mount it')
  end

  if(!File.exists?(full_mounted_path))
    system("hdiutil mount #{dmg_file}")
  end
  
  begin
    mounted_target = File.join(full_mounted_path, extracted_file)
  
    # Copy the DMG contents using system copy rather than ruby utils
    # Because OS X does something special with .app files that the
    # Ruby FileUtils and File classes break...
    from = mounted_target
#        from = File.join(full_mounted_path, extracted_file)
    to = File.join(@user.downloads, @name.to_s, extracted_file)
    FileUtils.makedirs(File.dirname(to))
    
    if(File.exists?(from))
      `ditto '#{from}' '#{to}'`
    end
  rescue StandardError => e
    if(File.exists?(full_mounted_path))
      system("hdiutil unmount -force \"#{full_mounted_path}\"")
    end
  end
end

#unpack_tar_zip(tgz_file, dir, ext) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/sprout/archive_unpacker.rb', line 121

def unpack_tar_zip(tgz_file, dir, ext)
  if(!File.exists?(dir))
    FileUtils.makedirs(dir)
  end
  tar = Zlib::GzipReader.new(File.open(tgz_file, 'rb'))
  Minitar.unpack(tar, dir)
  
  # Recurse and unpack gzipped children (Adobe did this double 
  # gzip with the Linux FlashPlayer for some reason)
  Dir.glob("#{dir}/**/*.#{ext}").each do |child|
    if(child != tgz_file && dir != File.dirname(child))
      unpack_targz(child, File.dirname(child))
    end
  end
  
end

#unpack_targz(tgz_file, dir) ⇒ Object



117
118
119
# File 'lib/sprout/archive_unpacker.rb', line 117

def unpack_targz(tgz_file, dir)
  unpack_tar_zip tgz_file, dir, 'tar.gz'
end

#unpack_tbz2(tgz_file, dir) ⇒ Object



113
114
115
# File 'lib/sprout/archive_unpacker.rb', line 113

def unpack_tbz2(tgz_file, dir)
  unpack_tar_zip tgz_file, dir, 'tar.bz2'
end

#unpack_zip(zip_file, dir) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/sprout/archive_unpacker.rb', line 54

def unpack_zip(zip_file, dir)
  # Avoid the rubyzip Segmentation Fault bug
  # at least on os x...
  if(RUBY_PLATFORM =~ /darwin/)
      # Unzipping on OS X
      FileUtils.makedirs(dir)
      zip_dir = File.expand_path(File.dirname(zip_file))
      zip_name = File.basename(zip_file)
      output = File.expand_path(dir)
      # puts ">> zip_dir: #{zip_dir} zip_name: #{zip_name} output: #{output}"
      %x(cd #{zip_dir};unzip #{zip_name} -d #{output})
  else
    retries = 0
    begin
      retries += 1
      Zip::ZipFile::open(zip_file) do |zf|
        zf.each do |e|
          fpath = File.join(dir, e.name)
          FileUtils.mkdir_p(File.dirname(fpath))
          # Disgusting, Gross Hack to fix DOS/Ruby Bug
          # That makes the zip library throw a ZipDestinationFileExistsError
          # When the zip archive includes two files whose names 
          # differ only by extension.
          # This bug actually appears in the File.exists? implementation
          # throwing false positives!
          # If you're going to use this code, be sure you extract
          # into a new, empty directory as existing files will be 
          # clobbered...
          begin
            if(File.exists?(fpath) && !File.directory?(fpath))
              hackpath = fpath + 'hack'
              zf.extract(e, hackpath)
              File.copy(hackpath, fpath)
              File.delete(hackpath)
            else
              zf.extract(e, fpath)
            end
          rescue NotImplementedError => ni_err
            puts "[WARNING] #{ni_err} for: #{e}"
          end
        end
      end
    rescue StandardError => err
      FileUtils.rm_rf(dir)
      if(retries < 3)
        FileUtils.makedirs(dir)
        retry
      end
      raise err
    end
  end
end

#unpacked_file_name(file, dir, suffix = nil) ⇒ Object



107
108
109
110
111
# File 'lib/sprout/archive_unpacker.rb', line 107

def unpacked_file_name(file, dir, suffix=nil)
  basename = File.basename(file, suffix)
  path = File.expand_path(dir)
  return File.join(path, basename)
end