pax_global_header00006660000000000000000000000064136331435620014520gustar00rootroot0000000000000052 comment=3bc55a5d9880827ddca76de41004b5a82eb7642e rubyzip-2.3.0/000077500000000000000000000000001363314356200132265ustar00rootroot00000000000000rubyzip-2.3.0/.gitignore000066400000000000000000000001521363314356200152140ustar00rootroot00000000000000.idea *.gem .bundle Gemfile.lock +samples/*.zip +samples/*.zip.* coverage pkg/ .ruby-gemset .ruby-version rubyzip-2.3.0/.rubocop.yml000066400000000000000000000036161363314356200155060ustar00rootroot00000000000000inherit_from: .rubocop_todo.yml # Set this to the minimum supported ruby in the gemspec. Otherwise # we get errors if our ruby version doesn't match. AllCops: TargetRubyVersion: 2.4 Layout/HashAlignment: EnforcedHashRocketStyle: table EnforcedColonStyle: table # Set a workable line length, given the current state of the code, # and turn off for the tests. Layout/LineLength: Max: 135 Exclude: - 'test/**/*.rb' # In some cases we just need to catch an exception, rather than # actually handle it. Allow the tests to make use of this shortcut. Lint/SuppressedException: AllowComments: true Exclude: - 'test/**/*.rb' # Allow this "useless" test, as we are testing <=> here. Lint/UselessComparison: Exclude: - 'test/entry_test.rb' # Turn off ABC metrics for the tests and set a workable max given # the current state of the code. Metrics/AbcSize: Max: 37 Exclude: - 'test/**/*.rb' # Turn block length metrics off for the tests. Metrics/BlockLength: Exclude: - 'test/**/*.rb' # Turn class length metrics off for the tests. Metrics/ClassLength: Exclude: - 'test/**/*.rb' # Turn method length metrics off for the tests. Metrics/MethodLength: Exclude: - 'test/**/*.rb' # Set a consistent way of checking types. Style/ClassCheck: EnforcedStyle: kind_of? # Allow this multi-line block chain as it actually reads better # than the alternatives. Style/MultilineBlockChain: Exclude: - 'lib/zip/crypto/traditional_encryption.rb' # Allow inner slashes when using // for regex literals. Allow the # Guardfile to use a syntax that is more consistent with its own style. Style/RegexpLiteral: AllowInnerSlashes: true Exclude: - 'Guardfile' Style/SymbolArray: EnforcedStyle: brackets # Turn this cop off for these files as it fires for objects without # an empty? method. Style/ZeroLengthPredicate: Exclude: - 'lib/zip/file.rb' - 'lib/zip/input_stream.rb' rubyzip-2.3.0/.rubocop_todo.yml000066400000000000000000000074171363314356200165360ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config` # on 2020-02-08 14:58:51 +0000 using RuboCop version 0.79.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 15 # Configuration parameters: CountComments. Metrics/ClassLength: Max: 580 # Offense count: 26 Metrics/CyclomaticComplexity: Max: 14 # Offense count: 120 # Configuration parameters: CountComments, ExcludedMethods. Metrics/MethodLength: Max: 30 # Offense count: 2 # Configuration parameters: CountKeywordArgs. Metrics/ParameterLists: Max: 10 # Offense count: 21 Metrics/PerceivedComplexity: Max: 15 # Offense count: 9 Naming/AccessorMethodName: Exclude: - 'lib/zip/entry.rb' - 'lib/zip/filesystem.rb' - 'lib/zip/input_stream.rb' - 'lib/zip/streamable_stream.rb' # Offense count: 7 # Configuration parameters: EnforcedStyle. # SupportedStyles: inline, group Style/AccessModifierDeclarations: Exclude: - 'lib/zip/central_directory.rb' - 'lib/zip/extra_field/zip64.rb' - 'lib/zip/filesystem.rb' # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle. # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Exclude: - 'lib/zip/extra_field/generic.rb' - 'lib/zip/extra_field/ntfs.rb' - 'lib/zip/extra_field/old_unix.rb' - 'lib/zip/extra_field/universal_time.rb' - 'lib/zip/extra_field/unix.rb' - 'lib/zip/extra_field/zip64.rb' - 'lib/zip/extra_field/zip64_placeholder.rb' # Offense count: 26 Style/Documentation: Enabled: false # Offense count: 3 # Configuration parameters: . # SupportedStyles: annotated, template, unannotated Style/FormatStringToken: EnforcedStyle: unannotated # Offense count: 95 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: always, never Style/FrozenStringLiteralComment: Enabled: false # Offense count: 17 # Cop supports --auto-correct. Style/IfUnlessModifier: Exclude: - 'lib/zip/entry.rb' - 'lib/zip/extra_field/generic.rb' - 'lib/zip/file.rb' - 'lib/zip/filesystem.rb' - 'lib/zip/input_stream.rb' - 'lib/zip/pass_thru_decompressor.rb' - 'lib/zip/streamable_stream.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, Autocorrect. # SupportedStyles: module_function, extend_self Style/ModuleFunction: Exclude: - 'lib/zip.rb' # Offense count: 56 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: literals, strict Style/MutableConstant: Enabled: false # Offense count: 23 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods. # SupportedStyles: predicate, comparison Style/NumericPredicate: Exclude: - 'spec/**/*' - 'lib/zip/entry.rb' - 'lib/zip/extra_field/old_unix.rb' - 'lib/zip/extra_field/universal_time.rb' - 'lib/zip/extra_field/unix.rb' - 'lib/zip/file.rb' - 'lib/zip/filesystem.rb' - 'lib/zip/input_stream.rb' - 'lib/zip/ioextras.rb' - 'lib/zip/ioextras/abstract_input_stream.rb' - 'test/file_split_test.rb' - 'test/test_helper.rb' # Offense count: 17 # Cop supports --auto-correct. # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods. # AllowedMethods: present?, blank?, presence, try, try! Style/SafeNavigation: Exclude: - 'lib/zip/entry.rb' - 'lib/zip/input_stream.rb' - 'lib/zip/output_stream.rb' - 'test/file_extract_test.rb' - 'test/filesystem/file_nonmutating_test.rb' - 'test/filesystem/file_stat_test.rb' - 'test/test_helper.rb' rubyzip-2.3.0/.simplecov000066400000000000000000000003161363314356200152300ustar00rootroot00000000000000require 'coveralls' SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter ]) SimpleCov.start do add_filter '/test' end rubyzip-2.3.0/.travis.yml000066400000000000000000000010651363314356200153410ustar00rootroot00000000000000language: ruby dist: xenial cache: bundler rvm: - 2.4 - 2.5 - 2.6 - 2.7 - ruby-head matrix: fast_finish: true include: - rvm: jruby-9.2 jdk: openjdk8 - rvm: jruby-9.2 jdk: openjdk11 - rvm: jruby-head jdk: openjdk11 - rvm: rbx-4 allow_failures: - rvm: ruby-head - rvm: rbx-4 - rvm: jruby-head before_install: - gem --version before_script: - echo `whereis zip` - echo `whereis unzip` env: global: - JRUBY_OPTS="--debug" - COVERALLS_PARALLEL=true notifications: webhooks: https://coveralls.io/webhook rubyzip-2.3.0/Changelog.md000066400000000000000000000327151363314356200154470ustar00rootroot00000000000000# X.X.X (Next) # 2.3.0 (2020-03-14) - Fix frozen string literal error [#431](https://github.com/rubyzip/rubyzip/pull/431) - Set `OutputStream.write_buffer`'s buffer to binmode [#439](https://github.com/rubyzip/rubyzip/pull/439) - Upgrade rubocop and fix various linting complaints [#437](https://github.com/rubyzip/rubyzip/pull/437) [#440](https://github.com/rubyzip/rubyzip/pull/440) Tooling: - Add a `bin/console` script for development [#420](https://github.com/rubyzip/rubyzip/pull/420) - Update rake requirement (development dependency only) to fix a security alert. # 2.2.0 (2020-02-01) - Add support for decompression plugin gems [#427](https://github.com/rubyzip/rubyzip/pull/427) # 2.1.0 (2020-01-25) - Fix (at least partially) the `restore_times` and `restore_permissions` options to `Zip::File.new` [#413](https://github.com/rubyzip/rubyzip/pull/413) - Previously, neither option did anything, regardless of what it was set to. We have therefore defaulted them to `false` to preserve the current behavior, for the time being. If you have explicitly set either to `true`, it will now have an effect. - Fix handling of UniversalTime (`mtime`, `atime`, `ctime`) fields. [#421](https://github.com/rubyzip/rubyzip/pull/421) - Previously, `Zip::File` did not pass the options to `Zip::Entry` in some cases. [#423](https://github.com/rubyzip/rubyzip/pull/423) - Note that `restore_times` in this release does nothing on Windows and only restores `mtime`, not `atime` or `ctime`. - Allow `Zip::File.open` to take an options hash like `Zip::File.new` [#418](https://github.com/rubyzip/rubyzip/pull/418) - Always print warnings with `warn`, instead of a mix of `puts` and `warn` [#416](https://github.com/rubyzip/rubyzip/pull/416) - Create temporary files in the system temporary directory instead of the directory of the zip file [#411](https://github.com/rubyzip/rubyzip/pull/411) - Drop unused `tmpdir` requirement [#411](https://github.com/rubyzip/rubyzip/pull/411) Tooling - Move CI to xenial and include jruby on JDK11 [#419](https://github.com/rubyzip/rubyzip/pull/419/files) # 2.0.0 (2019-09-25) Security - Default the `validate_entry_sizes` option to `true`, so that callers can trust an entry's reported size when using `extract` [#403](https://github.com/rubyzip/rubyzip/pull/403) - This option defaulted to `false` in 1.3.0 for backward compatibility, but it now defaults to `true`. If you are using an older version of ruby and can't yet upgrade to 2.x, you can still use 1.3.0 and set the option to `true`. Tooling / Documentation - Remove test files from the gem to avoid problems with antivirus detections on the test files [#405](https://github.com/rubyzip/rubyzip/pull/405) / [#384](https://github.com/rubyzip/rubyzip/issues/384) - Drop support for unsupported ruby versions [#406](https://github.com/rubyzip/rubyzip/pull/406) # 1.3.0 (2019-09-25) Security - Add `validate_entry_sizes` option so that callers can trust an entry's reported size when using `extract` [#403](https://github.com/rubyzip/rubyzip/pull/403) - This option defaults to `false` for backward compatibility in this release, but you are strongly encouraged to set it to `true`. It will default to `true` in rubyzip 2.0. New Feature - Add `add_stored` method to simplify adding entries without compression [#366](https://github.com/rubyzip/rubyzip/pull/366) Tooling / Documentation - Add more gem metadata links [#402](https://github.com/rubyzip/rubyzip/pull/402) # 1.2.4 (2019-09-06) - Do not rewrite zip files opened with `open_buffer` that have not changed [#360](https://github.com/rubyzip/rubyzip/pull/360) Tooling / Documentation - Update `example_recursive.rb` in README [#397](https://github.com/rubyzip/rubyzip/pull/397) - Hold CI at `trusty` for now, automatically pick the latest ruby patch version, use rbx-4 and hold jruby at 9.1 [#399](https://github.com/rubyzip/rubyzip/pull/399) # 1.2.3 - Allow tilde in zip entry names [#391](https://github.com/rubyzip/rubyzip/pull/391) (fixes regression in 1.2.2 from [#376](https://github.com/rubyzip/rubyzip/pull/376)) - Support frozen string literals in more files [#390](https://github.com/rubyzip/rubyzip/pull/390) - Require `pathname` explicitly [#388](https://github.com/rubyzip/rubyzip/pull/388) (fixes regression in 1.2.2 from [#376](https://github.com/rubyzip/rubyzip/pull/376)) Tooling / Documentation: - CI updates [#392](https://github.com/rubyzip/rubyzip/pull/392), [#394](https://github.com/rubyzip/rubyzip/pull/394) - Bump supported ruby versions and add 2.6 - JRuby failures are no longer ignored (reverts [#375](https://github.com/rubyzip/rubyzip/pull/375) / part of [#371](https://github.com/rubyzip/rubyzip/pull/371)) - Add changelog entry that was missing for last release [#387](https://github.com/rubyzip/rubyzip/pull/387) - Comment cleanup [#385](https://github.com/rubyzip/rubyzip/pull/385) # 1.2.2 NB: This release drops support for extracting symlinks, because there was no clear way to support this securely. See https://github.com/rubyzip/rubyzip/pull/376#issue-210954555 for details. - Fix CVE-2018-1000544 [#376](https://github.com/rubyzip/rubyzip/pull/376) / [#371](https://github.com/rubyzip/rubyzip/pull/371) - Fix NoMethodError: undefined method `glob' [#363](https://github.com/rubyzip/rubyzip/pull/363) - Fix handling of stored files (i.e. files not using compression) with general purpose bit 3 set [#358](https://github.com/rubyzip/rubyzip/pull/358) - Fix `close` on StringIO-backed zip file [#353](https://github.com/rubyzip/rubyzip/pull/353) - Add `Zip.force_entry_names_encoding` option [#340](https://github.com/rubyzip/rubyzip/pull/340) - Update rubocop, apply auto-fixes, and fix regressions caused by said auto-fixes [#332](https://github.com/rubyzip/rubyzip/pull/332), [#355](https://github.com/rubyzip/rubyzip/pull/355) - Save temporary files to temporary directory (rather than current directory) [#325](https://github.com/rubyzip/rubyzip/pull/325) Tooling / Documentation: - Turn off all terminal output in all tests [#361](https://github.com/rubyzip/rubyzip/pull/361) - Several CI updates [#346](https://github.com/rubyzip/rubyzip/pull/346), [#347](https://github.com/rubyzip/rubyzip/pull/347), [#350](https://github.com/rubyzip/rubyzip/pull/350), [#352](https://github.com/rubyzip/rubyzip/pull/352) - Several README improvements [#345](https://github.com/rubyzip/rubyzip/pull/345), [#326](https://github.com/rubyzip/rubyzip/pull/326), [#321](https://github.com/rubyzip/rubyzip/pull/321) # 1.2.1 - Add accessor to @internal_file_attributes #304 - Extended globbing #303 - README updates #283, #289 - Cleanup after tests #298, #306 - Fix permissions on new zip files #294, #300 - Fix examples #297 - Support cp932 encoding #308 - Fix Directory traversal vulnerability #315 - Allow open_buffer to work without a given block #314 # 1.2.0 - Don't enable JRuby objectspace #252 - Fixes an exception thrown when decoding some weird .zip files #248 - Use duck typing with IO methods #244 - Added error for empty (zero bit) zip file #242 - Accept StringIO in Zip.open_buffer #238 - Do something more expected with new file permissions #237 - Case insensitivity option for #find_entry #222 - Fixes in documentation and examples # 1.1.7 - Fix UTF-8 support for comments - `Zip.sort_entries` working for zip output - Prevent tempfile path from being unlinked by garbage collection - NTFS Extra Field (0x000a) support - Use String#tr instead of String#gsub - Ability to not show warning about incorrect date - Be smarter about handling buffer file modes. - Support for Traditional Encryption (ZipCrypto) # 1.1.6 - Revert "Return created zip file from Zip::File.open when supplied a block" # 1.1.5 - Treat empty file as non-exists (@layerssss) - Revert regression commit - Return created zip file from Zip::File.open when supplied a block (@tpickett66) - Zip::Entry::DEFLATED is forced on every file (@mehmetc) - Add InputStream#ungetc (@zacstewart) - Alias for legacy error names (@orien) # 1.1.4 - Don't send empty string to stream (@mrloop) - Zip::Entry::DEFLATED was forced on every file (@mehmetc) - Alias for legacy error names (@orien) # 1.1.3 - Fix compatibility of ::OutputStream::write_buffer (@orien) - Clean up tempfiles from output stream (@iangreenleaf) # 1.1.2 - Fix compatibility of ::Zip::File.write_buffer # 1.1.1 - Speedup deflater (@loadhigh) - Less Arrays and Strings allocations (@srawlins) - Fix Zip64 writing support (@mrjamesriley) - Fix StringIO support (@simonoff) - Possibility to change default compression level - Make Zip64 write support optional via configuration # 1.1.0 - StringIO Support - Zip64 Support - Better jRuby Support - Order of files in the archive can be sorted - Other small fixes # 1.0.0 - Removed support for Ruby 1.8 - Changed the API for gem. Now it can be used without require param in Gemfile. - Added read-only support for Zip64 files. - Added support for setting Unicode file names. # 0.9.9 - Added support for backslashes in zip files (generated by the default Windows zip packer for example) and comment sections with the comment length set to zero even though there is actually a comment. # 0.9.8 - Fixed: "Unitialized constant NullInputStream" error # 0.9.5 - Removed support for loading ruby in zip files (ziprequire.rb). # 0.9.4 - Changed ZipOutputStream.put_next_entry signature (API CHANGE!). Now allows comment, extra field and compression method to be specified. # 0.9.3 - Fixed: Added ZipEntry::name_encoding which retrieves the character encoding of the name and comment of the entry. - Added convenience methods ZipEntry::name_in(enc) and ZipEntry::comment_in(enc) for getting zip entry names and comments in a specified character encoding. # 0.9.2 - Fixed: Renaming an entry failed if the entry's new name was a different length than its old name. (Diego Barros) # 0.9.1 - Added symlink support and support for unix file permissions. Reduced memory usage during decompression. - New methods ZipFile::[follow_symlinks, restore_times, restore_permissions, restore_ownership]. - New methods ZipEntry::unix_perms, ZipInputStream::eof?. - Added documentation and test for new ZipFile::extract. - Added some of the API suggestions from sf.net #1281314. - Applied patch for sf.net bug #1446926. - Applied patch for sf.net bug #1459902. - Rework ZipEntry and delegate classes. # 0.5.12 - Fixed problem with writing binary content to a ZipFile in MS Windows. # 0.5.11 - Fixed name clash file method copy_stream from fileutils.rb. Fixed problem with references to constant CHUNK_SIZE. - ZipInputStream/AbstractInputStream read is now buffered like ruby IO's read method, which means that read and gets etc can be mixed. The unbuffered read method has been renamed to sysread. # 0.5.10 - Fixed method name resolution problem with FileUtils::copy_stream and IOExtras::copy_stream. # 0.5.9 - Fixed serious memory consumption issue # 0.5.8 - Fixed install script. # 0.5.7 - install.rb no longer assumes it is being run from the toplevel source dir. Directory structure changed to reflect common ruby library project structure. Migrated from RubyUnit to Test::Unit format. Now uses Rake to build source packages and gems and run unit tests. # 0.5.6 - Fix for FreeBSD 4.9 which returns Errno::EFBIG instead of Errno::EINVAL for some invalid seeks. Fixed 'version needed to extract'-field incorrect in local headers. # 0.5.5 - Fix for a problem with writing zip files that concerns only ruby 1.8.1. # 0.5.4 - Significantly reduced memory footprint when modifying zip files. # 0.5.3 - Added optimization to avoid decompressing and recompressing individual entries when modifying a zip archive. # 0.5.2 - Fixed ZipFile corruption bug in ZipFile class. Added basic unix extra-field support. # 0.5.1 - Fixed ZipFile.get_output_stream bug. # 0.5.0 - Ruby 1.8.0 and ruby-zlib 0.6.0 compatibility - Changed method names from camelCase to rubys underscore style. - Installs to zip/ subdir instead of directly to site_ruby - Added ZipFile.directory and ZipFile.file - each method return an object that can be used like Dir and File only for the contents of the zip file. - Added sample application zipfind which works like Find.find, only Zip::ZipFind.find traverses into zip archives too. - FIX: AbstractInputStream.each_line with non-default separator # 0.5.0a Source reorganized. Added ziprequire, which can be used to load ruby modules from a zip file, in a fashion similar to jar files in Java. Added gtk_ruby_zip, another sample application. Implemented ZipInputStream.lineno and ZipInputStream.rewind Bug fixes: - Read and write date and time information correctly for zip entries. - Fixed read() using separate buffer, causing mix of gets/readline/read to cause problems. # 0.4.2 - Performance optimizations. Test suite runs in half the time. # 0.4.1 - Windows compatibility fixes. # 0.4.0 - Zip::ZipFile is now mutable and provides a more convenient way of modifying zip archives than Zip::ZipOutputStream. Operations for adding, extracting, renaming, replacing and removing entries to zip archives are now available. - Runs without warnings with -w switch. - Install script install.rb added. # 0.3.1 - Rudimentary support for writing zip archives. # 0.2.2 - Fixed and extended unit test suite. Updated to work with ruby/zlib 0.5. It doesn't work with earlier versions of ruby/zlib. # 0.2.0 - Class ZipFile added. Where ZipInputStream is used to read the individual entries in a zip file, ZipFile reads the central directory in the zip archive, so you can get to any entry in the zip archive without having to skipping through all the preceeding entries. # 0.1.0 - First working version of ZipInputStream. rubyzip-2.3.0/Gemfile000066400000000000000000000000471363314356200145220ustar00rootroot00000000000000source 'https://rubygems.org' gemspec rubyzip-2.3.0/Guardfile000066400000000000000000000003331363314356200150520ustar00rootroot00000000000000guard :minitest do # with Minitest::Unit watch(%r{^test/(.*)\/?(.*)_test\.rb$}) watch(%r{^lib/zip/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb" } watch(%r{^test/test_helper\.rb$}) { 'test' } end rubyzip-2.3.0/README.md000066400000000000000000000245701363314356200145150ustar00rootroot00000000000000# rubyzip [![Gem Version](https://badge.fury.io/rb/rubyzip.svg)](http://badge.fury.io/rb/rubyzip) [![Build Status](https://secure.travis-ci.org/rubyzip/rubyzip.svg)](http://travis-ci.org/rubyzip/rubyzip) [![Code Climate](https://codeclimate.com/github/rubyzip/rubyzip.svg)](https://codeclimate.com/github/rubyzip/rubyzip) [![Coverage Status](https://img.shields.io/coveralls/rubyzip/rubyzip.svg)](https://coveralls.io/r/rubyzip/rubyzip?branch=master) Rubyzip is a ruby library for reading and writing zip files. ## Important note The Rubyzip interface has changed!!! No need to do `require "zip/zip"` and `Zip` prefix in class names removed. If you have issues with any third-party gems that require an old version of rubyzip, you can use this workaround: ```ruby gem 'rubyzip', '>= 1.0.0' # will load new rubyzip version gem 'zip-zip' # will load compatibility for old rubyzip API. ``` ## Requirements - Ruby 2.4 or greater (for rubyzip 2.0; use 1.x for older rubies) ## Installation Rubyzip is available on RubyGems: ``` gem install rubyzip ``` Or in your Gemfile: ```ruby gem 'rubyzip' ``` ## Usage ### Basic zip archive creation ```ruby require 'rubygems' require 'zip' folder = "Users/me/Desktop/stuff_to_zip" input_filenames = ['image.jpg', 'description.txt', 'stats.csv'] zipfile_name = "/Users/me/Desktop/archive.zip" Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile| input_filenames.each do |filename| # Two arguments: # - The name of the file as it will appear in the archive # - The original file, including the path to find it zipfile.add(filename, File.join(folder, filename)) end zipfile.get_output_stream("myFile") { |f| f.write "myFile contains just this" } end ``` ### Zipping a directory recursively Copy from [here](https://github.com/rubyzip/rubyzip/blob/9d891f7353e66052283562d3e252fe380bb4b199/samples/example_recursive.rb) ```ruby require 'zip' # This is a simple example which uses rubyzip to # recursively generate a zip file from the contents of # a specified directory. The directory itself is not # included in the archive, rather just its contents. # # Usage: # directory_to_zip = "/tmp/input" # output_file = "/tmp/out.zip" # zf = ZipFileGenerator.new(directory_to_zip, output_file) # zf.write() class ZipFileGenerator # Initialize with the directory to zip and the location of the output archive. def initialize(input_dir, output_file) @input_dir = input_dir @output_file = output_file end # Zip the input directory. def write entries = Dir.entries(@input_dir) - %w[. ..] ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile| write_entries entries, '', zipfile end end private # A helper method to make the recursion work. def write_entries(entries, path, zipfile) entries.each do |e| zipfile_path = path == '' ? e : File.join(path, e) disk_file_path = File.join(@input_dir, zipfile_path) if File.directory? disk_file_path recursively_deflate_directory(disk_file_path, zipfile, zipfile_path) else put_into_archive(disk_file_path, zipfile, zipfile_path) end end end def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path) zipfile.mkdir zipfile_path subdir = Dir.entries(disk_file_path) - %w[. ..] write_entries subdir, zipfile_path, zipfile end def put_into_archive(disk_file_path, zipfile, zipfile_path) zipfile.add(zipfile_path, disk_file_path) end end ``` ### Save zip archive entries in sorted by name state To save zip archives in sorted order like below, you need to set `::Zip.sort_entries` to `true` ``` Vegetable/ Vegetable/bean Vegetable/carrot Vegetable/celery fruit/ fruit/apple fruit/kiwi fruit/mango fruit/orange ``` After this, entries in the zip archive will be saved in ordered state. ### Default permissions of zip archives On Posix file systems the default file permissions applied to a new archive are (0666 - umask), which mimics the behavior of standard tools such as `touch`. On Windows the default file permissions are set to 0644 as suggested by the [Ruby File documentation](http://ruby-doc.org/core-2.2.2/File.html). When modifying a zip archive the file permissions of the archive are preserved. ### Reading a Zip file ```ruby MAX_SIZE = 1024**2 # 1MiB (but of course you can increase this) Zip::File.open('foo.zip') do |zip_file| # Handle entries one by one zip_file.each do |entry| puts "Extracting #{entry.name}" raise 'File too large when extracted' if entry.size > MAX_SIZE # Extract to file or directory based on name in the archive entry.extract # Read into memory content = entry.get_input_stream.read end # Find specific entry entry = zip_file.glob('*.csv').first raise 'File too large when extracted' if entry.size > MAX_SIZE puts entry.get_input_stream.read end ``` #### Notice about ::Zip::InputStream `::Zip::InputStream` usable for fast reading zip file content because it not read Central directory. But there is one exception when it is not working - General Purpose Flag Bit 3. > If bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written. The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure (optionally preceded by a 4-byte signature) immediately after the compressed data If `::Zip::InputStream` finds such entry in the zip archive it will raise an exception. ### Password Protection (Experimental) Rubyzip supports reading/writing zip files with traditional zip encryption (a.k.a. "ZipCrypto"). AES encryption is not yet supported. It can be used with buffer streams, e.g.: ```ruby Zip::OutputStream.write_buffer(::StringIO.new(''), Zip::TraditionalEncrypter.new('password')) do |out| out.put_next_entry("my_file.txt") out.write my_data end.string ``` This is an experimental feature and the interface for encryption may change in future versions. ## Known issues ### Modify docx file with rubyzip Use `write_buffer` instead `open`. Thanks to @jondruse ```ruby buffer = Zip::OutputStream.write_buffer do |out| @zip_file.entries.each do |e| unless [DOCUMENT_FILE_PATH, RELS_FILE_PATH].include?(e.name) out.put_next_entry(e.name) out.write e.get_input_stream.read end end out.put_next_entry(DOCUMENT_FILE_PATH) out.write xml_doc.to_xml(:indent => 0).gsub("\n","") out.put_next_entry(RELS_FILE_PATH) out.write rels.to_xml(:indent => 0).gsub("\n","") end File.open(new_path, "wb") {|f| f.write(buffer.string) } ``` ## Configuration ### Existing Files By default, rubyzip will not overwrite files if they already exist inside of the extracted path. To change this behavior, you may specify a configuration option like so: ```ruby Zip.on_exists_proc = true ``` If you're using rubyzip with rails, consider placing this snippet of code in an initializer file such as `config/initializers/rubyzip.rb` Additionally, if you want to configure rubyzip to overwrite existing files while creating a .zip file, you can do so with the following: ```ruby Zip.continue_on_exists_proc = true ``` ### Non-ASCII Names If you want to store non-english names and want to open them on Windows(pre 7) you need to set this option: ```ruby Zip.unicode_names = true ``` Sometimes file names inside zip contain non-ASCII characters. If you can assume which encoding was used for such names and want to be able to find such entries using `find_entry` then you can force assumed encoding like so: ```ruby Zip.force_entry_names_encoding = 'UTF-8' ``` Allowed encoding names are the same as accepted by `String#force_encoding` ### Date Validation Some zip files might have an invalid date format, which will raise a warning. You can hide this warning with the following setting: ```ruby Zip.warn_invalid_date = false ``` ### Size Validation By default (in rubyzip >= 2.0), rubyzip's `extract` method checks that an entry's reported uncompressed size is not (significantly) smaller than its actual size. This is to help you protect your application against [zip bombs](https://en.wikipedia.org/wiki/Zip_bomb). Before `extract`ing an entry, you should check that its size is in the range you expect. For example, if your application supports processing up to 100 files at once, each up to 10MiB, your zip extraction code might look like: ```ruby MAX_FILE_SIZE = 10 * 1024**2 # 10MiB MAX_FILES = 100 Zip::File.open('foo.zip') do |zip_file| num_files = 0 zip_file.each do |entry| num_files += 1 if entry.file? raise 'Too many extracted files' if num_files > MAX_FILES raise 'File too large when extracted' if entry.size > MAX_FILE_SIZE entry.extract end end ``` If you need to extract zip files that report incorrect uncompressed sizes and you really trust them not too be too large, you can disable this setting with ```ruby Zip.validate_entry_sizes = false ``` Note that if you use the lower level `Zip::InputStream` interface, `rubyzip` does *not* check the entry `size`s. In this case, the caller is responsible for making sure it does not read more data than expected from the input stream. ### Default Compression You can set the default compression level like so: ```ruby Zip.default_compression = Zlib::DEFAULT_COMPRESSION ``` It defaults to `Zlib::DEFAULT_COMPRESSION`. Possible values are `Zlib::BEST_COMPRESSION`, `Zlib::DEFAULT_COMPRESSION` and `Zlib::NO_COMPRESSION` ### Zip64 Support By default, Zip64 support is disabled for writing. To enable it do this: ```ruby Zip.write_zip64_support = true ``` _NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive. ### Block Form You can set multiple settings at the same time by using a block: ```ruby Zip.setup do |c| c.on_exists_proc = true c.continue_on_exists_proc = true c.unicode_names = true c.default_compression = Zlib::BEST_COMPRESSION end ``` ## Developing To run the test you need to do this: ``` bundle install rake ``` ## Website and Project Home http://github.com/rubyzip/rubyzip http://rdoc.info/github/rubyzip/rubyzip/master/frames ## Authors Alexander Simonov ( alex at simonov.me) Alan Harper ( alan at aussiegeek.net) Thomas Sondergaard (thomas at sondergaard.cc) Technorama Ltd. (oss-ruby-zip at technorama.net) extra-field support contributed by Tatsuki Sugiura (sugi at nemui.org) ## License Rubyzip is distributed under the same license as ruby. See http://www.ruby-lang.org/en/LICENSE.txt rubyzip-2.3.0/Rakefile000066400000000000000000000010341363314356200146710ustar00rootroot00000000000000require 'bundler/gem_tasks' require 'rake/testtask' require 'rubocop/rake_task' task default: :test Rake::TestTask.new(:test) do |test| test.libs << 'lib' test.libs << 'test' test.pattern = 'test/**/*_test.rb' test.verbose = true end RuboCop::RakeTask.new # Rake::TestTask.new(:zip64_full_test) do |test| # test.libs << File.join(File.dirname(__FILE__), 'lib') # test.libs << File.join(File.dirname(__FILE__), 'test') # test.pattern = File.join(File.dirname(__FILE__), 'test/zip64_full_test.rb') # test.verbose = true # end rubyzip-2.3.0/TODO000066400000000000000000000014731363314356200137230ustar00rootroot00000000000000 * ZipInputStream: Support zip-files with trailing data descriptors * Adjust rdoc stylesheet to advertise inherited methods if possible * Suggestion: Add ZipFile/ZipInputStream example that demonstrates extracting all entries. * Suggestion: ZipFile#extract destination should default to "." * Suggestion: ZipEntry should have extract(), get_input_stream() methods etc * (is buffering used anywhere with write?) * Inflater.sysread should pass the buffer to produce_input. * Implement ZipFsDir.glob * ZipFile.checkIntegrity method * non-MSDOS permission attributes ** See mail from Ned Konz to ruby-talk subj. "Re: SV: [ANN] Archive 0.2" * Packager version, required unpacker version in zip headers ** See mail from Ned Konz to ruby-talk subj. "Re: SV: [ANN] Archive 0.2" * implement storing attributes and ownership information rubyzip-2.3.0/bin/000077500000000000000000000000001363314356200137765ustar00rootroot00000000000000rubyzip-2.3.0/bin/console000077500000000000000000000001361363314356200153660ustar00rootroot00000000000000#!/usr/bin/env ruby require 'bundler/setup' require 'zip' require 'irb' IRB.start(__FILE__) rubyzip-2.3.0/lib/000077500000000000000000000000001363314356200137745ustar00rootroot00000000000000rubyzip-2.3.0/lib/zip.rb000066400000000000000000000035351363314356200151310ustar00rootroot00000000000000require 'English' require 'delegate' require 'singleton' require 'tempfile' require 'fileutils' require 'stringio' require 'zlib' require 'zip/constants' require 'zip/dos_time' require 'zip/ioextras' require 'rbconfig' require 'zip/entry' require 'zip/extra_field' require 'zip/entry_set' require 'zip/central_directory' require 'zip/file' require 'zip/input_stream' require 'zip/output_stream' require 'zip/decompressor' require 'zip/compressor' require 'zip/null_decompressor' require 'zip/null_compressor' require 'zip/null_input_stream' require 'zip/pass_thru_compressor' require 'zip/pass_thru_decompressor' require 'zip/crypto/decrypted_io' require 'zip/crypto/encryption' require 'zip/crypto/null_encryption' require 'zip/crypto/traditional_encryption' require 'zip/inflater' require 'zip/deflater' require 'zip/streamable_stream' require 'zip/streamable_directory' require 'zip/errors' module Zip extend self attr_accessor :unicode_names, :on_exists_proc, :continue_on_exists_proc, :sort_entries, :default_compression, :write_zip64_support, :warn_invalid_date, :case_insensitive_match, :force_entry_names_encoding, :validate_entry_sizes def reset! @_ran_once = false @unicode_names = false @on_exists_proc = false @continue_on_exists_proc = false @sort_entries = false @default_compression = ::Zlib::DEFAULT_COMPRESSION @write_zip64_support = false @warn_invalid_date = true @case_insensitive_match = false @validate_entry_sizes = true end def setup yield self unless @_ran_once @_ran_once = true end reset! end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/000077500000000000000000000000001363314356200145765ustar00rootroot00000000000000rubyzip-2.3.0/lib/zip/central_directory.rb000066400000000000000000000161771363314356200206530ustar00rootroot00000000000000module Zip class CentralDirectory include Enumerable END_OF_CDS = 0x06054b50 ZIP64_END_OF_CDS = 0x06064b50 ZIP64_EOCD_LOCATOR = 0x07064b50 MAX_END_OF_CDS_SIZE = 65_536 + 18 STATIC_EOCD_SIZE = 22 attr_reader :comment # Returns an Enumerable containing the entries. def entries @entry_set.entries end def initialize(entries = EntrySet.new, comment = '') #:nodoc: super() @entry_set = entries.kind_of?(EntrySet) ? entries : EntrySet.new(entries) @comment = comment end def write_to_stream(io) #:nodoc: cdir_offset = io.tell @entry_set.each { |entry| entry.write_c_dir_entry(io) } eocd_offset = io.tell cdir_size = eocd_offset - cdir_offset if ::Zip.write_zip64_support need_zip64_eocd = cdir_offset > 0xFFFFFFFF || cdir_size > 0xFFFFFFFF || @entry_set.size > 0xFFFF need_zip64_eocd ||= @entry_set.any? { |entry| entry.extra['Zip64'] } if need_zip64_eocd write_64_e_o_c_d(io, cdir_offset, cdir_size) write_64_eocd_locator(io, eocd_offset) end end write_e_o_c_d(io, cdir_offset, cdir_size) end def write_e_o_c_d(io, offset, cdir_size) #:nodoc: tmp = [ END_OF_CDS, 0, # @numberOfThisDisk 0, # @numberOfDiskWithStartOfCDir @entry_set ? [@entry_set.size, 0xFFFF].min : 0, @entry_set ? [@entry_set.size, 0xFFFF].min : 0, [cdir_size, 0xFFFFFFFF].min, [offset, 0xFFFFFFFF].min, @comment ? @comment.bytesize : 0 ] io << tmp.pack('VvvvvVVv') io << @comment end private :write_e_o_c_d def write_64_e_o_c_d(io, offset, cdir_size) #:nodoc: tmp = [ ZIP64_END_OF_CDS, 44, # size of zip64 end of central directory record (excludes signature and field itself) VERSION_MADE_BY, VERSION_NEEDED_TO_EXTRACT_ZIP64, 0, # @numberOfThisDisk 0, # @numberOfDiskWithStartOfCDir @entry_set ? @entry_set.size : 0, # number of entries on this disk @entry_set ? @entry_set.size : 0, # number of entries total cdir_size, # size of central directory offset # offset of start of central directory in its disk ] io << tmp.pack('VQ 'FAT'.freeze, FSTYPE_AMIGA => 'Amiga'.freeze, FSTYPE_VMS => 'VMS (Vax or Alpha AXP)'.freeze, FSTYPE_UNIX => 'Unix'.freeze, FSTYPE_VM_CMS => 'VM/CMS'.freeze, FSTYPE_ATARI => 'Atari ST'.freeze, FSTYPE_HPFS => 'OS/2 or NT HPFS'.freeze, FSTYPE_MAC => 'Macintosh'.freeze, FSTYPE_Z_SYSTEM => 'Z-System'.freeze, FSTYPE_CPM => 'CP/M'.freeze, FSTYPE_TOPS20 => 'TOPS-20'.freeze, FSTYPE_NTFS => 'NTFS'.freeze, FSTYPE_QDOS => 'SMS/QDOS'.freeze, FSTYPE_ACORN => 'Acorn RISC OS'.freeze, FSTYPE_VFAT => 'Win32 VFAT'.freeze, FSTYPE_MVS => 'MVS'.freeze, FSTYPE_BEOS => 'BeOS'.freeze, FSTYPE_TANDEM => 'Tandem NSK'.freeze, FSTYPE_THEOS => 'Theos'.freeze, FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)'.freeze, FSTYPE_ATHEOS => 'AtheOS'.freeze }.freeze COMPRESSION_METHOD_STORE = 0 COMPRESSION_METHOD_SHRINK = 1 COMPRESSION_METHOD_REDUCE_1 = 2 COMPRESSION_METHOD_REDUCE_2 = 3 COMPRESSION_METHOD_REDUCE_3 = 4 COMPRESSION_METHOD_REDUCE_4 = 5 COMPRESSION_METHOD_IMPLODE = 6 # RESERVED = 7 COMPRESSION_METHOD_DEFLATE = 8 COMPRESSION_METHOD_DEFLATE_64 = 9 COMPRESSION_METHOD_PKWARE_DCLI = 10 # RESERVED = 11 COMPRESSION_METHOD_BZIP2 = 12 # RESERVED = 13 COMPRESSION_METHOD_LZMA = 14 # RESERVED = 15 COMPRESSION_METHOD_IBM_CMPSC = 16 # RESERVED = 17 COMPRESSION_METHOD_IBM_TERSE = 18 COMPRESSION_METHOD_IBM_LZ77 = 19 COMPRESSION_METHOD_JPEG = 96 COMPRESSION_METHOD_WAVPACK = 97 COMPRESSION_METHOD_PPMD = 98 COMPRESSION_METHOD_AES = 99 COMPRESSION_METHODS = { COMPRESSION_METHOD_STORE => 'Store (no compression)', COMPRESSION_METHOD_SHRINK => 'Shrink', COMPRESSION_METHOD_REDUCE_1 => 'Reduce with compression factor 1', COMPRESSION_METHOD_REDUCE_2 => 'Reduce with compression factor 2', COMPRESSION_METHOD_REDUCE_3 => 'Reduce with compression factor 3', COMPRESSION_METHOD_REDUCE_4 => 'Reduce with compression factor 4', COMPRESSION_METHOD_IMPLODE => 'Implode', # RESERVED = 7 COMPRESSION_METHOD_DEFLATE => 'Deflate', COMPRESSION_METHOD_DEFLATE_64 => 'Deflate64(tm)', COMPRESSION_METHOD_PKWARE_DCLI => 'PKWARE Data Compression Library Imploding (old IBM TERSE)', # RESERVED = 11 COMPRESSION_METHOD_BZIP2 => 'BZIP2', # RESERVED = 13 COMPRESSION_METHOD_LZMA => 'LZMA', # RESERVED = 15 COMPRESSION_METHOD_IBM_CMPSC => 'IBM z/OS CMPSC Compression', # RESERVED = 17 COMPRESSION_METHOD_IBM_TERSE => 'IBM TERSE (new)', COMPRESSION_METHOD_IBM_LZ77 => 'IBM LZ77 z Architecture (PFS)', COMPRESSION_METHOD_JPEG => 'JPEG variant', COMPRESSION_METHOD_WAVPACK => 'WavPack compressed data', COMPRESSION_METHOD_PPMD => 'PPMd version I, Rev 1', COMPRESSION_METHOD_AES => 'AES encryption' }.freeze end rubyzip-2.3.0/lib/zip/crypto/000077500000000000000000000000001363314356200161165ustar00rootroot00000000000000rubyzip-2.3.0/lib/zip/crypto/decrypted_io.rb000066400000000000000000000013271363314356200211200ustar00rootroot00000000000000module Zip class DecryptedIo #:nodoc:all CHUNK_SIZE = 32_768 def initialize(io, decrypter) @io = io @decrypter = decrypter end def read(length = nil, outbuf = +'') return (length.nil? || length.zero? ? '' : nil) if eof while length.nil? || (buffer.bytesize < length) break if input_finished? buffer << produce_input end outbuf.replace(buffer.slice!(0...(length || output_buffer.bytesize))) end private def eof buffer.empty? && input_finished? end def buffer @buffer ||= +'' end def input_finished? @io.eof end def produce_input @decrypter.decrypt(@io.read(CHUNK_SIZE)) end end end rubyzip-2.3.0/lib/zip/crypto/encryption.rb000066400000000000000000000003471363314356200206410ustar00rootroot00000000000000module Zip class Encrypter #:nodoc:all end class Decrypter end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/crypto/null_encryption.rb000066400000000000000000000012411363314356200216650ustar00rootroot00000000000000module Zip module NullEncryption def header_bytesize 0 end def gp_flags 0 end end class NullEncrypter < Encrypter include NullEncryption def header(_mtime) '' end def encrypt(data) data end def data_descriptor(_crc32, _compressed_size, _uncomprssed_size) '' end def reset!; end end class NullDecrypter < Decrypter include NullEncryption def decrypt(data) data end def reset!(_header); end end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/crypto/traditional_encryption.rb000066400000000000000000000037361363314356200232400ustar00rootroot00000000000000module Zip module TraditionalEncryption def initialize(password) @password = password reset_keys! end def header_bytesize 12 end def gp_flags 0x0001 | 0x0008 end protected def reset_keys! @key0 = 0x12345678 @key1 = 0x23456789 @key2 = 0x34567890 @password.each_byte do |byte| update_keys(byte.chr) end end def update_keys(num) @key0 = ~Zlib.crc32(num, ~@key0) @key1 = ((@key1 + (@key0 & 0xff)) * 134_775_813 + 1) & 0xffffffff @key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2) end def decrypt_byte temp = (@key2 & 0xffff) | 2 ((temp * (temp ^ 1)) >> 8) & 0xff end end class TraditionalEncrypter < Encrypter include TraditionalEncryption def header(mtime) [].tap do |header| (header_bytesize - 2).times do header << Random.rand(0..255) end header << (mtime.to_binary_dos_time & 0xff) header << (mtime.to_binary_dos_time >> 8) end.map { |x| encode x }.pack('C*') end def encrypt(data) data.unpack('C*').map { |x| encode x }.pack('C*') end def data_descriptor(crc32, compressed_size, uncomprssed_size) [0x08074b50, crc32, compressed_size, uncomprssed_size].pack('VVVV') end def reset! reset_keys! end private def encode(num) t = decrypt_byte update_keys(num.chr) t ^ num end end class TraditionalDecrypter < Decrypter include TraditionalEncryption def decrypt(data) data.unpack('C*').map { |x| decode x }.pack('C*') end def reset!(header) reset_keys! header.each_byte do |x| decode x end end private def decode(num) num ^= decrypt_byte update_keys(num.chr) num end end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/decompressor.rb000066400000000000000000000014321363314356200176300ustar00rootroot00000000000000module Zip class Decompressor #:nodoc:all CHUNK_SIZE = 32_768 def self.decompressor_classes @decompressor_classes ||= {} end def self.register(compression_method, decompressor_class) decompressor_classes[compression_method] = decompressor_class end def self.find_by_compression_method(compression_method) decompressor_classes[compression_method] end attr_reader :input_stream attr_reader :decompressed_size def initialize(input_stream, decompressed_size = nil) super() @input_stream = input_stream @decompressed_size = decompressed_size end end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/deflater.rb000066400000000000000000000017071363314356200167160ustar00rootroot00000000000000module Zip class Deflater < Compressor #:nodoc:all def initialize(output_stream, level = Zip.default_compression, encrypter = NullEncrypter.new) super() @output_stream = output_stream @zlib_deflater = ::Zlib::Deflate.new(level, -::Zlib::MAX_WBITS) @size = 0 @crc = ::Zlib.crc32 @encrypter = encrypter end def <<(data) val = data.to_s @crc = Zlib.crc32(val, @crc) @size += val.bytesize buffer = @zlib_deflater.deflate(data) if buffer.empty? @output_stream else @output_stream << @encrypter.encrypt(buffer) end end def finish @output_stream << @encrypter.encrypt(@zlib_deflater.finish) until @zlib_deflater.finished? end attr_reader :size, :crc end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/dos_time.rb000066400000000000000000000027451363314356200167360ustar00rootroot00000000000000module Zip class DOSTime < Time #:nodoc:all # MS-DOS File Date and Time format as used in Interrupt 21H Function 57H: # Register CX, the Time: # Bits 0-4 2 second increments (0-29) # Bits 5-10 minutes (0-59) # bits 11-15 hours (0-24) # Register DX, the Date: # Bits 0-4 day (1-31) # bits 5-8 month (1-12) # bits 9-15 year (four digit year minus 1980) def to_binary_dos_time (sec / 2) + (min << 5) + (hour << 11) end def to_binary_dos_date day + (month << 5) + ((year - 1980) << 9) end # Dos time is only stored with two seconds accuracy def dos_equals(other) to_i / 2 == other.to_i / 2 end # Create a DOSTime instance from a vanilla Time instance. def self.from_time(time) local(time.year, time.month, time.day, time.hour, time.min, time.sec) end def self.parse_binary_dos_format(bin_dos_date, bin_dos_time) second = 2 * (0b11111 & bin_dos_time) minute = (0b11111100000 & bin_dos_time) >> 5 hour = (0b1111100000000000 & bin_dos_time) >> 11 day = (0b11111 & bin_dos_date) month = (0b111100000 & bin_dos_date) >> 5 year = ((0b1111111000000000 & bin_dos_date) >> 9) + 1980 begin local(year, month, day, hour, minute, second) end end end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/entry.rb000066400000000000000000000540561363314356200162760ustar00rootroot00000000000000require 'pathname' module Zip class Entry STORED = 0 DEFLATED = 8 # Language encoding flag (EFS) bit EFS = 0b100000000000 attr_accessor :comment, :compressed_size, :crc, :extra, :compression_method, :name, :size, :local_header_offset, :zipfile, :fstype, :external_file_attributes, :internal_file_attributes, :gp_flags, :header_signature, :follow_symlinks, :restore_times, :restore_permissions, :restore_ownership, :unix_uid, :unix_gid, :unix_perms, :dirty attr_reader :ftype, :filepath # :nodoc: def set_default_vars_values @local_header_offset = 0 @local_header_size = nil # not known until local entry is created or read @internal_file_attributes = 1 @external_file_attributes = 0 @header_signature = ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE @version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT @version = VERSION_MADE_BY @ftype = nil # unspecified or unknown @filepath = nil @gp_flags = 0 if ::Zip.unicode_names @gp_flags |= EFS @version = 63 end @follow_symlinks = false @restore_times = false @restore_permissions = false @restore_ownership = false # BUG: need an extra field to support uid/gid's @unix_uid = nil @unix_gid = nil @unix_perms = nil # @posix_acl = nil # @ntfs_acl = nil @dirty = false end def check_name(name) return unless name.start_with?('/') raise ::Zip::EntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /" end def initialize(*args) name = args[1] || '' check_name(name) set_default_vars_values @fstype = ::Zip::RUNNING_ON_WINDOWS ? ::Zip::FSTYPE_FAT : ::Zip::FSTYPE_UNIX @zipfile = args[0] || '' @name = name @comment = args[2] || '' @extra = args[3] || '' @compressed_size = args[4] || 0 @crc = args[5] || 0 @compression_method = args[6] || ::Zip::Entry::DEFLATED @size = args[7] || 0 @time = args[8] || ::Zip::DOSTime.now @ftype = name_is_directory? ? :directory : :file @extra = ::Zip::ExtraField.new(@extra.to_s) unless @extra.kind_of?(::Zip::ExtraField) end def encrypted? gp_flags & 1 == 1 end def incomplete? gp_flags & 8 == 8 end def time if @extra['UniversalTime'] @extra['UniversalTime'].mtime elsif @extra['NTFS'] @extra['NTFS'].mtime else # Standard time field in central directory has local time # under archive creator. Then, we can't get timezone. @time end end alias mtime time def time=(value) unless @extra.member?('UniversalTime') || @extra.member?('NTFS') @extra.create('UniversalTime') end (@extra['UniversalTime'] || @extra['NTFS']).mtime = value @time = value end def file_type_is?(type) raise InternalError, "current filetype is unknown: #{inspect}" unless @ftype @ftype == type end # Dynamic checkers %w[directory file symlink].each do |k| define_method "#{k}?" do file_type_is?(k.to_sym) end end def name_is_directory? #:nodoc:all @name.end_with?('/') end # Is the name a relative path, free of `..` patterns that could lead to # path traversal attacks? This does NOT handle symlinks; if the path # contains symlinks, this check is NOT enough to guarantee safety. def name_safe? cleanpath = Pathname.new(@name).cleanpath return false unless cleanpath.relative? root = ::File::SEPARATOR naive_expanded_path = ::File.join(root, cleanpath.to_s) ::File.absolute_path(cleanpath.to_s, root) == naive_expanded_path end def local_entry_offset #:nodoc:all local_header_offset + @local_header_size end def name_size @name ? @name.bytesize : 0 end def extra_size @extra ? @extra.local_size : 0 end def comment_size @comment ? @comment.bytesize : 0 end def calculate_local_header_size #:nodoc:all LOCAL_ENTRY_STATIC_HEADER_LENGTH + name_size + extra_size end # check before rewriting an entry (after file sizes are known) # that we didn't change the header size (and thus clobber file data or something) def verify_local_header_size! return if @local_header_size.nil? new_size = calculate_local_header_size raise Error, "local header size changed (#{@local_header_size} -> #{new_size})" if @local_header_size != new_size end def cdir_header_size #:nodoc:all CDIR_ENTRY_STATIC_HEADER_LENGTH + name_size + (@extra ? @extra.c_dir_size : 0) + comment_size end def next_header_offset #:nodoc:all local_entry_offset + compressed_size + data_descriptor_size end # Extracts entry to file dest_path (defaults to @name). # NB: The caller is responsible for making sure dest_path is safe, if it # is passed. def extract(dest_path = nil, &block) if dest_path.nil? && !name_safe? warn "WARNING: skipped '#{@name}' as unsafe." return self end dest_path ||= @name block ||= proc { ::Zip.on_exists_proc } raise "unknown file type #{inspect}" unless directory? || file? || symlink? __send__("create_#{@ftype}", dest_path, &block) self end def to_s @name end class << self def read_zip_short(io) # :nodoc: io.read(2).unpack1('v') end def read_zip_long(io) # :nodoc: io.read(4).unpack1('V') end def read_zip_64_long(io) # :nodoc: io.read(8).unpack1('Q<') end def read_c_dir_entry(io) #:nodoc:all path = if io.respond_to?(:path) io.path else io end entry = new(path) entry.read_c_dir_entry(io) entry rescue Error nil end def read_local_entry(io) entry = new(io) entry.read_local_entry(io) entry rescue Error nil end end def unpack_local_entry(buf) @header_signature, @version, @fstype, @gp_flags, @compression_method, @last_mod_time, @last_mod_date, @crc, @compressed_size, @size, @name_length, @extra_length = buf.unpack('VCCvvvvVVVvv') end def read_local_entry(io) #:nodoc:all @local_header_offset = io.tell static_sized_fields_buf = io.read(::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH) || '' unless static_sized_fields_buf.bytesize == ::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH raise Error, 'Premature end of file. Not enough data for zip entry local header' end unpack_local_entry(static_sized_fields_buf) unless @header_signature == ::Zip::LOCAL_ENTRY_SIGNATURE raise ::Zip::Error, "Zip local header magic not found at location '#{local_header_offset}'" end set_time(@last_mod_date, @last_mod_time) @name = io.read(@name_length) extra = io.read(@extra_length) @name.tr!('\\', '/') if ::Zip.force_entry_names_encoding @name.force_encoding(::Zip.force_entry_names_encoding) end if extra && extra.bytesize != @extra_length raise ::Zip::Error, 'Truncated local zip entry header' end if @extra.kind_of?(::Zip::ExtraField) @extra.merge(extra) if extra else @extra = ::Zip::ExtraField.new(extra) end parse_zip64_extra(true) @local_header_size = calculate_local_header_size end def pack_local_entry zip64 = @extra['Zip64'] [::Zip::LOCAL_ENTRY_SIGNATURE, @version_needed_to_extract, # version needed to extract @gp_flags, # @gp_flags @compression_method, @time.to_binary_dos_time, # @last_mod_time @time.to_binary_dos_date, # @last_mod_date @crc, zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size, zip64 && zip64.original_size ? 0xFFFFFFFF : @size, name_size, @extra ? @extra.local_size : 0].pack('VvvvvvVVVvv') end def write_local_entry(io, rewrite = false) #:nodoc:all prep_zip64_extra(true) verify_local_header_size! if rewrite @local_header_offset = io.tell io << pack_local_entry io << @name io << @extra.to_local_bin if @extra @local_header_size = io.tell - @local_header_offset end def unpack_c_dir_entry(buf) @header_signature, @version, # version of encoding software @fstype, # filesystem type @version_needed_to_extract, @gp_flags, @compression_method, @last_mod_time, @last_mod_date, @crc, @compressed_size, @size, @name_length, @extra_length, @comment_length, _, # diskNumberStart @internal_file_attributes, @external_file_attributes, @local_header_offset, @name, @extra, @comment = buf.unpack('VCCvvvvvVVVvvvvvVV') end def set_ftype_from_c_dir_entry @ftype = case @fstype when ::Zip::FSTYPE_UNIX @unix_perms = (@external_file_attributes >> 16) & 0o7777 case (@external_file_attributes >> 28) when ::Zip::FILE_TYPE_DIR :directory when ::Zip::FILE_TYPE_FILE :file when ::Zip::FILE_TYPE_SYMLINK :symlink else # best case guess for whether it is a file or not # Otherwise this would be set to unknown and that entry would never be able to extracted if name_is_directory? :directory else :file end end else if name_is_directory? :directory else :file end end end def check_c_dir_entry_static_header_length(buf) return if buf.bytesize == ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH raise Error, 'Premature end of file. Not enough data for zip cdir entry header' end def check_c_dir_entry_signature return if header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE raise Error, "Zip local header magic not found at location '#{local_header_offset}'" end def check_c_dir_entry_comment_size return if @comment && @comment.bytesize == @comment_length raise ::Zip::Error, 'Truncated cdir zip entry header' end def read_c_dir_extra_field(io) if @extra.kind_of?(::Zip::ExtraField) @extra.merge(io.read(@extra_length)) else @extra = ::Zip::ExtraField.new(io.read(@extra_length)) end end def read_c_dir_entry(io) #:nodoc:all static_sized_fields_buf = io.read(::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH) check_c_dir_entry_static_header_length(static_sized_fields_buf) unpack_c_dir_entry(static_sized_fields_buf) check_c_dir_entry_signature set_time(@last_mod_date, @last_mod_time) @name = io.read(@name_length) if ::Zip.force_entry_names_encoding @name.force_encoding(::Zip.force_entry_names_encoding) end read_c_dir_extra_field(io) @comment = io.read(@comment_length) check_c_dir_entry_comment_size set_ftype_from_c_dir_entry parse_zip64_extra(false) end def file_stat(path) # :nodoc: if @follow_symlinks ::File.stat(path) else ::File.lstat(path) end end def get_extra_attributes_from_path(path) # :nodoc: return if Zip::RUNNING_ON_WINDOWS stat = file_stat(path) @unix_uid = stat.uid @unix_gid = stat.gid @unix_perms = stat.mode & 0o7777 @time = ::Zip::DOSTime.from_time(stat.mtime) end def set_unix_attributes_on_path(dest_path) # ignore setuid/setgid bits by default. honor if @restore_ownership unix_perms_mask = 0o1777 unix_perms_mask = 0o7777 if @restore_ownership ::FileUtils.chmod(@unix_perms & unix_perms_mask, dest_path) if @restore_permissions && @unix_perms ::FileUtils.chown(@unix_uid, @unix_gid, dest_path) if @restore_ownership && @unix_uid && @unix_gid && ::Process.egid == 0 # Restore the timestamp on a file. This will either have come from the # original source file that was copied into the archive, or from the # creation date of the archive if there was no original source file. ::FileUtils.touch(dest_path, mtime: time) if @restore_times end def set_extra_attributes_on_path(dest_path) # :nodoc: return unless file? || directory? case @fstype when ::Zip::FSTYPE_UNIX set_unix_attributes_on_path(dest_path) end end def pack_c_dir_entry zip64 = @extra['Zip64'] [ @header_signature, @version, # version of encoding software @fstype, # filesystem type @version_needed_to_extract, # @versionNeededToExtract @gp_flags, # @gp_flags @compression_method, @time.to_binary_dos_time, # @last_mod_time @time.to_binary_dos_date, # @last_mod_date @crc, zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size, zip64 && zip64.original_size ? 0xFFFFFFFF : @size, name_size, @extra ? @extra.c_dir_size : 0, comment_size, zip64 && zip64.disk_start_number ? 0xFFFF : 0, # disk number start @internal_file_attributes, # file type (binary=0, text=1) @external_file_attributes, # native filesystem attributes zip64 && zip64.relative_header_offset ? 0xFFFFFFFF : @local_header_offset, @name, @extra, @comment ].pack('VCCvvvvvVVVvvvvvVV') end def write_c_dir_entry(io) #:nodoc:all prep_zip64_extra(false) case @fstype when ::Zip::FSTYPE_UNIX ft = case @ftype when :file @unix_perms ||= 0o644 ::Zip::FILE_TYPE_FILE when :directory @unix_perms ||= 0o755 ::Zip::FILE_TYPE_DIR when :symlink @unix_perms ||= 0o755 ::Zip::FILE_TYPE_SYMLINK end unless ft.nil? @external_file_attributes = (ft << 12 | (@unix_perms & 0o7777)) << 16 end end io << pack_c_dir_entry io << @name io << (@extra ? @extra.to_c_dir_bin : '') io << @comment end def ==(other) return false unless other.class == self.class # Compares contents of local entry and exposed fields keys_equal = %w[compression_method crc compressed_size size name extra filepath].all? do |k| other.__send__(k.to_sym) == __send__(k.to_sym) end keys_equal && time.dos_equals(other.time) end def <=>(other) to_s <=> other.to_s end # Returns an IO like object for the given ZipEntry. # Warning: may behave weird with symlinks. def get_input_stream(&block) if @ftype == :directory yield ::Zip::NullInputStream if block_given? ::Zip::NullInputStream elsif @filepath case @ftype when :file ::File.open(@filepath, 'rb', &block) when :symlink linkpath = ::File.readlink(@filepath) stringio = ::StringIO.new(linkpath) yield(stringio) if block_given? stringio else raise "unknown @file_type #{@ftype}" end else zis = ::Zip::InputStream.new(@zipfile, local_header_offset) zis.instance_variable_set(:@complete_entry, self) zis.get_next_entry if block_given? begin yield(zis) ensure zis.close end else zis end end end def gather_fileinfo_from_srcpath(src_path) # :nodoc: stat = file_stat(src_path) @ftype = case stat.ftype when 'file' if name_is_directory? raise ArgumentError, "entry name '#{newEntry}' indicates directory entry, but " \ "'#{src_path}' is not a directory" end :file when 'directory' @name += '/' unless name_is_directory? :directory when 'link' if name_is_directory? raise ArgumentError, "entry name '#{newEntry}' indicates directory entry, but " \ "'#{src_path}' is not a directory" end :symlink else raise "unknown file type: #{src_path.inspect} #{stat.inspect}" end @filepath = src_path get_extra_attributes_from_path(@filepath) end def write_to_zip_output_stream(zip_output_stream) #:nodoc:all if @ftype == :directory zip_output_stream.put_next_entry(self, nil, nil, ::Zip::Entry::STORED) elsif @filepath zip_output_stream.put_next_entry(self, nil, nil, compression_method || ::Zip::Entry::DEFLATED) get_input_stream { |is| ::Zip::IOExtras.copy_stream(zip_output_stream, is) } else zip_output_stream.copy_raw_entry(self) end end def parent_as_string entry_name = name.chomp('/') slash_index = entry_name.rindex('/') slash_index ? entry_name.slice(0, slash_index + 1) : nil end def get_raw_input_stream(&block) if @zipfile.respond_to?(:seek) && @zipfile.respond_to?(:read) yield @zipfile else ::File.open(@zipfile, 'rb', &block) end end def clean_up # By default, do nothing end private def set_time(binary_dos_date, binary_dos_time) @time = ::Zip::DOSTime.parse_binary_dos_format(binary_dos_date, binary_dos_time) rescue ArgumentError warn 'WARNING: invalid date/time in zip entry.' if ::Zip.warn_invalid_date end def create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exists_proc }) if ::File.exist?(dest_path) && !yield(self, dest_path) raise ::Zip::DestinationFileExistsError, "Destination '#{dest_path}' already exists" end ::File.open(dest_path, 'wb') do |os| get_input_stream do |is| bytes_written = 0 warned = false buf = +'' while (buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf)) os << buf bytes_written += buf.bytesize next unless bytes_written > size && !warned message = "entry '#{name}' should be #{size}B, but is larger when inflated." raise ::Zip::EntrySizeError, message if ::Zip.validate_entry_sizes warn "WARNING: #{message}" warned = true end end end set_extra_attributes_on_path(dest_path) end def create_directory(dest_path) return if ::File.directory?(dest_path) if ::File.exist?(dest_path) if block_given? && yield(self, dest_path) ::FileUtils.rm_f dest_path else raise ::Zip::DestinationFileExistsError, "Cannot create directory '#{dest_path}'. " \ 'A file already exists with that name' end end ::FileUtils.mkdir_p(dest_path) set_extra_attributes_on_path(dest_path) end # BUG: create_symlink() does not use &block def create_symlink(dest_path) # TODO: Symlinks pose security challenges. Symlink support temporarily # removed in view of https://github.com/rubyzip/rubyzip/issues/369 . warn "WARNING: skipped symlink '#{dest_path}'." end # apply missing data from the zip64 extra information field, if present # (required when file sizes exceed 2**32, but can be used for all files) def parse_zip64_extra(for_local_header) #:nodoc:all return if @extra['Zip64'].nil? if for_local_header @size, @compressed_size = @extra['Zip64'].parse(@size, @compressed_size) else @size, @compressed_size, @local_header_offset = @extra['Zip64'].parse(@size, @compressed_size, @local_header_offset) end end def data_descriptor_size (@gp_flags & 0x0008) > 0 ? 16 : 0 end # create a zip64 extra information field if we need one def prep_zip64_extra(for_local_header) #:nodoc:all return unless ::Zip.write_zip64_support need_zip64 = @size >= 0xFFFFFFFF || @compressed_size >= 0xFFFFFFFF need_zip64 ||= @local_header_offset >= 0xFFFFFFFF unless for_local_header if need_zip64 @version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT_ZIP64 @extra.delete('Zip64Placeholder') zip64 = @extra.create('Zip64') if for_local_header # local header always includes size and compressed size zip64.original_size = @size zip64.compressed_size = @compressed_size else # central directory entry entries include whichever fields are necessary zip64.original_size = @size if @size >= 0xFFFFFFFF zip64.compressed_size = @compressed_size if @compressed_size >= 0xFFFFFFFF zip64.relative_header_offset = @local_header_offset if @local_header_offset >= 0xFFFFFFFF end else @extra.delete('Zip64') # if this is a local header entry, create a placeholder # so we have room to write a zip64 extra field afterward # (we won't know if it's needed until the file data is written) if for_local_header @extra.create('Zip64Placeholder') else @extra.delete('Zip64Placeholder') end end end end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/entry_set.rb000066400000000000000000000033431363314356200171420ustar00rootroot00000000000000module Zip class EntrySet #:nodoc:all include Enumerable attr_accessor :entry_set, :entry_order def initialize(an_enumerable = []) super() @entry_set = {} an_enumerable.each { |o| push(o) } end def include?(entry) @entry_set.include?(to_key(entry)) end def find_entry(entry) @entry_set[to_key(entry)] end def <<(entry) @entry_set[to_key(entry)] = entry if entry end alias push << def size @entry_set.size end alias length size def delete(entry) entry if @entry_set.delete(to_key(entry)) end def each @entry_set = sorted_entries.dup.each do |_, value| yield(value) end end def entries sorted_entries.values end # deep clone def dup EntrySet.new(@entry_set.values.map(&:dup)) end def ==(other) return false unless other.kind_of?(EntrySet) @entry_set.values == other.entry_set.values end def parent(entry) @entry_set[to_key(entry.parent_as_string)] end def glob(pattern, flags = ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH | ::File::FNM_EXTGLOB) entries.map do |entry| next nil unless ::File.fnmatch(pattern, entry.name.chomp('/'), flags) yield(entry) if block_given? entry end.compact end protected def sorted_entries ::Zip.sort_entries ? Hash[@entry_set.sort] : @entry_set end private def to_key(entry) k = entry.to_s.chomp('/') k.downcase! if ::Zip.case_insensitive_match k end end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/errors.rb000066400000000000000000000012231363314356200164350ustar00rootroot00000000000000module Zip class Error < StandardError; end class EntryExistsError < Error; end class DestinationFileExistsError < Error; end class CompressionMethodError < Error; end class EntryNameError < Error; end class EntrySizeError < Error; end class InternalError < Error; end class GPFBit3Error < Error; end class DecompressionError < Error; end # Backwards compatibility with v1 (delete in v2) ZipError = Error ZipEntryExistsError = EntryExistsError ZipDestinationFileExistsError = DestinationFileExistsError ZipCompressionMethodError = CompressionMethodError ZipEntryNameError = EntryNameError ZipInternalError = InternalError end rubyzip-2.3.0/lib/zip/extra_field.rb000066400000000000000000000051441363314356200174150ustar00rootroot00000000000000module Zip class ExtraField < Hash ID_MAP = {} def initialize(binstr = nil) merge(binstr) if binstr end def extra_field_type_exist(binstr, id, len, index) field_name = ID_MAP[id].name if member?(field_name) self[field_name].merge(binstr[index, len + 4]) else field_obj = ID_MAP[id].new(binstr[index, len + 4]) self[field_name] = field_obj end end def extra_field_type_unknown(binstr, len, index) create_unknown_item unless self['Unknown'] if !len || len + 4 > binstr[index..-1].bytesize self['Unknown'] << binstr[index..-1] return end self['Unknown'] << binstr[index, len + 4] end def create_unknown_item s = +'' class << s alias_method :to_c_dir_bin, :to_s alias_method :to_local_bin, :to_s end self['Unknown'] = s end def merge(binstr) return if binstr.empty? i = 0 while i < binstr.bytesize id = binstr[i, 2] len = binstr[i + 2, 2].to_s.unpack1('v') if id && ID_MAP.member?(id) extra_field_type_exist(binstr, id, len, i) elsif id create_unknown_item unless self['Unknown'] break unless extra_field_type_unknown(binstr, len, i) end i += len + 4 end end def create(name) unless (field_class = ID_MAP.values.find { |k| k.name == name }) raise Error, "Unknown extra field '#{name}'" end self[name] = field_class.new end # place Unknown last, so "extra" data that is missing the proper signature/size # does not prevent known fields from being read back in def ordered_values result = [] each { |k, v| k == 'Unknown' ? result.push(v) : result.unshift(v) } result end def to_local_bin ordered_values.map! { |v| v.to_local_bin.force_encoding('BINARY') }.join end alias to_s to_local_bin def to_c_dir_bin ordered_values.map! { |v| v.to_c_dir_bin.force_encoding('BINARY') }.join end def c_dir_size to_c_dir_bin.bytesize end def local_size to_local_bin.bytesize end alias length local_size alias size local_size end end require 'zip/extra_field/generic' require 'zip/extra_field/universal_time' require 'zip/extra_field/old_unix' require 'zip/extra_field/unix' require 'zip/extra_field/zip64' require 'zip/extra_field/zip64_placeholder' require 'zip/extra_field/ntfs' # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/extra_field/000077500000000000000000000000001363314356200170645ustar00rootroot00000000000000rubyzip-2.3.0/lib/zip/extra_field/generic.rb000066400000000000000000000017331363314356200210310ustar00rootroot00000000000000module Zip class ExtraField::Generic def self.register_map return unless const_defined?(:HEADER_ID) ::Zip::ExtraField::ID_MAP[const_get(:HEADER_ID)] = self end def self.name @name ||= to_s.split('::')[-1] end # return field [size, content] or false def initial_parse(binstr) return false unless binstr if binstr[0, 2] != self.class.const_get(:HEADER_ID) warn 'WARNING: weird extra field header ID. Skip parsing it.' return false end [binstr[2, 2].unpack1('v'), binstr[4..-1]] end def ==(other) return false if self.class != other.class each do |k, v| return false if v != other[k] end true end def to_local_bin s = pack_for_local self.class.const_get(:HEADER_ID) + [s.bytesize].pack('v') << s end def to_c_dir_bin s = pack_for_c_dir self.class.const_get(:HEADER_ID) + [s.bytesize].pack('v') << s end end end rubyzip-2.3.0/lib/zip/extra_field/ntfs.rb000066400000000000000000000042101363314356200203600ustar00rootroot00000000000000module Zip # PKWARE NTFS Extra Field (0x000a) # Only Tag 0x0001 is supported class ExtraField::NTFS < ExtraField::Generic HEADER_ID = [0x000A].pack('v') register_map WINDOWS_TICK = 10_000_000.0 SEC_TO_UNIX_EPOCH = 11_644_473_600 def initialize(binstr = nil) @ctime = nil @mtime = nil @atime = nil binstr && merge(binstr) end attr_accessor :atime, :ctime, :mtime def merge(binstr) return if binstr.empty? size, content = initial_parse(binstr) (size && content) || return content = content[4..-1] tags = parse_tags(content) tag1 = tags[1] return unless tag1 ntfs_mtime, ntfs_atime, ntfs_ctime = tag1.unpack('Qmy.zip # (creating it if it doesn't exist) and adds an entry # first.txt and a directory entry a_dir # to it. # # require 'zip' # # Zip::File.open("my.zip", Zip::File::CREATE) { # |zipfile| # zipfile.get_output_stream("first.txt") { |f| f.puts "Hello from ZipFile" } # zipfile.mkdir("a_dir") # } # # The next example reopens my.zip writes the contents of # first.txt to standard out and deletes the entry from # the archive. # # require 'zip' # # Zip::File.open("my.zip", Zip::File::CREATE) { # |zipfile| # puts zipfile.read("first.txt") # zipfile.remove("first.txt") # } # # ZipFileSystem offers an alternative API that emulates ruby's # interface for accessing the filesystem, ie. the File and Dir classes. class File < CentralDirectory CREATE = true SPLIT_SIGNATURE = 0x08074b50 ZIP64_EOCD_SIGNATURE = 0x06064b50 MAX_SEGMENT_SIZE = 3_221_225_472 MIN_SEGMENT_SIZE = 65_536 DATA_BUFFER_SIZE = 8192 IO_METHODS = [:tell, :seek, :read, :eof, :close] DEFAULT_OPTIONS = { restore_ownership: false, restore_permissions: false, restore_times: false }.freeze attr_reader :name # default -> false. attr_accessor :restore_ownership # default -> false, but will be set to true in a future version. attr_accessor :restore_permissions # default -> false, but will be set to true in a future version. attr_accessor :restore_times # Returns the zip files comment, if it has one attr_accessor :comment # Opens a zip archive. Pass true as the second parameter to create # a new archive if it doesn't exist already. def initialize(path_or_io, create = false, buffer = false, options = {}) super() options = DEFAULT_OPTIONS.merge(options) @name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io @comment = '' @create = create ? true : false # allow any truthy value to mean true if ::File.size?(@name.to_s) # There is a file, which exists, that is associated with this zip. @create = false @file_permissions = ::File.stat(@name).mode if buffer read_from_stream(path_or_io) else ::File.open(@name, 'rb') do |f| read_from_stream(f) end end elsif buffer && path_or_io.size > 0 # This zip is probably a non-empty StringIO. read_from_stream(path_or_io) elsif @create # This zip is completely new/empty and is to be created. @entry_set = EntrySet.new elsif ::File.zero?(@name) # A file exists, but it is empty. raise Error, "File #{@name} has zero size. Did you mean to pass the create flag?" else # Everything is wrong. raise Error, "File #{@name} not found" end @stored_entries = @entry_set.dup @stored_comment = @comment @restore_ownership = options[:restore_ownership] @restore_permissions = options[:restore_permissions] @restore_times = options[:restore_times] end class << self # Similar to ::new. If a block is passed the Zip::File object is passed # to the block and is automatically closed afterwards, just as with # ruby's builtin File::open method. def open(file_name, create = false, options = {}) zf = ::Zip::File.new(file_name, create, false, options) return zf unless block_given? begin yield zf ensure zf.close end end # Same as #open. But outputs data to a buffer instead of a file def add_buffer io = ::StringIO.new('') zf = ::Zip::File.new(io, true, true) yield zf zf.write_buffer(io) end # Like #open, but reads zip archive contents from a String or open IO # stream, and outputs data to a buffer. # (This can be used to extract data from a # downloaded zip archive without first saving it to disk.) def open_buffer(io, options = {}) unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.kind_of?(String) raise "Zip::File.open_buffer expects a String or IO-like argument (responds to #{IO_METHODS.join(', ')}). Found: #{io.class}" end io = ::StringIO.new(io) if io.kind_of?(::String) # https://github.com/rubyzip/rubyzip/issues/119 io.binmode if io.respond_to?(:binmode) zf = ::Zip::File.new(io, true, true, options) return zf unless block_given? yield zf begin zf.write_buffer(io) rescue IOError => e raise unless e.message == 'not opened for writing' end end # Iterates over the contents of the ZipFile. This is more efficient # than using a ZipInputStream since this methods simply iterates # through the entries in the central directory structure in the archive # whereas ZipInputStream jumps through the entire archive accessing the # local entry headers (which contain the same information as the # central directory). def foreach(zip_file_name, &block) ::Zip::File.open(zip_file_name) do |zip_file| zip_file.each(&block) end end def get_segment_size_for_split(segment_size) if MIN_SEGMENT_SIZE > segment_size MIN_SEGMENT_SIZE elsif MAX_SEGMENT_SIZE < segment_size MAX_SEGMENT_SIZE else segment_size end end def get_partial_zip_file_name(zip_file_name, partial_zip_file_name) unless partial_zip_file_name.nil? partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/, partial_zip_file_name + ::File.extname(zip_file_name)) end partial_zip_file_name ||= zip_file_name partial_zip_file_name end def get_segment_count_for_split(zip_file_size, segment_size) (zip_file_size / segment_size).to_i + (zip_file_size % segment_size == 0 ? 0 : 1) end def put_split_signature(szip_file, segment_size) signature_packed = [SPLIT_SIGNATURE].pack('V') szip_file << signature_packed segment_size - signature_packed.size end # # TODO: Make the code more understandable # def save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count) ssegment_size = zip_file_size - zip_file.pos ssegment_size = segment_size if ssegment_size > segment_size szip_file_name = "#{partial_zip_file_name}.#{format('%03d', szip_file_index)}" ::File.open(szip_file_name, 'wb') do |szip_file| if szip_file_index == 1 ssegment_size = put_split_signature(szip_file, segment_size) end chunk_bytes = 0 until ssegment_size == chunk_bytes || zip_file.eof? segment_bytes_left = ssegment_size - chunk_bytes buffer_size = segment_bytes_left < DATA_BUFFER_SIZE ? segment_bytes_left : DATA_BUFFER_SIZE chunk = zip_file.read(buffer_size) chunk_bytes += buffer_size szip_file << chunk # Info for track splitting yield segment_count, szip_file_index, chunk_bytes, ssegment_size if block_given? end end end # Splits an archive into parts with segment size def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true, partial_zip_file_name = nil) raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name) raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name) zip_file_size = ::File.size(zip_file_name) segment_size = get_segment_size_for_split(segment_size) return if zip_file_size <= segment_size segment_count = get_segment_count_for_split(zip_file_size, segment_size) # Checking for correct zip structure ::Zip::File.open(zip_file_name) {} partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name) szip_file_index = 0 ::File.open(zip_file_name, 'rb') do |zip_file| until zip_file.eof? szip_file_index += 1 save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count) end end ::File.delete(zip_file_name) if delete_zip_file szip_file_index end end # Returns an input stream to the specified entry. If a block is passed # the stream object is passed to the block and the stream is automatically # closed afterwards just as with ruby's builtin File.open method. def get_input_stream(entry, &a_proc) get_entry(entry).get_input_stream(&a_proc) end # Returns an output stream to the specified entry. If entry is not an instance # of Zip::Entry, a new Zip::Entry will be initialized using the arguments # specified. If a block is passed the stream object is passed to the block and # the stream is automatically closed afterwards just as with ruby's builtin # File.open method. def get_output_stream(entry, permission_int = nil, comment = nil, extra = nil, compressed_size = nil, crc = nil, compression_method = nil, size = nil, time = nil, &a_proc) new_entry = if entry.kind_of?(Entry) entry else Entry.new(@name, entry.to_s, comment, extra, compressed_size, crc, compression_method, size, time) end if new_entry.directory? raise ArgumentError, "cannot open stream to directory entry - '#{new_entry}'" end new_entry.unix_perms = permission_int zip_streamable_entry = StreamableStream.new(new_entry) @entry_set << zip_streamable_entry zip_streamable_entry.get_output_stream(&a_proc) end # Returns the name of the zip archive def to_s @name end # Returns a string containing the contents of the specified entry def read(entry) get_input_stream(entry, &:read) end # Convenience method for adding the contents of a file to the archive def add(entry, src_path, &continue_on_exists_proc) continue_on_exists_proc ||= proc { ::Zip.continue_on_exists_proc } check_entry_exists(entry, continue_on_exists_proc, 'add') new_entry = entry.kind_of?(::Zip::Entry) ? entry : ::Zip::Entry.new(@name, entry.to_s) new_entry.gather_fileinfo_from_srcpath(src_path) new_entry.dirty = true @entry_set << new_entry end # Convenience method for adding the contents of a file to the archive # in Stored format (uncompressed) def add_stored(entry, src_path, &continue_on_exists_proc) entry = ::Zip::Entry.new(@name, entry.to_s, nil, nil, nil, nil, ::Zip::Entry::STORED) add(entry, src_path, &continue_on_exists_proc) end # Removes the specified entry. def remove(entry) @entry_set.delete(get_entry(entry)) end # Renames the specified entry. def rename(entry, new_name, &continue_on_exists_proc) found_entry = get_entry(entry) check_entry_exists(new_name, continue_on_exists_proc, 'rename') @entry_set.delete(found_entry) found_entry.name = new_name @entry_set << found_entry end # Replaces the specified entry with the contents of src_path (from # the file system). def replace(entry, src_path) check_file(src_path) remove(entry) add(entry, src_path) end # Extracts entry to file dest_path. def extract(entry, dest_path, &block) block ||= proc { ::Zip.on_exists_proc } found_entry = get_entry(entry) found_entry.extract(dest_path, &block) end # Commits changes that has been made since the previous commit to # the zip archive. def commit return if name.kind_of?(StringIO) || !commit_required? on_success_replace do |tmp_file| ::Zip::OutputStream.open(tmp_file) do |zos| @entry_set.each do |e| e.write_to_zip_output_stream(zos) e.dirty = false e.clean_up end zos.comment = comment end true end initialize(name) end # Write buffer write changes to buffer and return def write_buffer(io = ::StringIO.new('')) ::Zip::OutputStream.write_buffer(io) do |zos| @entry_set.each { |e| e.write_to_zip_output_stream(zos) } zos.comment = comment end end # Closes the zip file committing any changes that has been made. def close commit end # Returns true if any changes has been made to this archive since # the previous commit def commit_required? @entry_set.each do |e| return true if e.dirty end @comment != @stored_comment || @entry_set != @stored_entries || @create end # Searches for entry with the specified name. Returns nil if # no entry is found. See also get_entry def find_entry(entry_name) selected_entry = @entry_set.find_entry(entry_name) return if selected_entry.nil? selected_entry.restore_ownership = @restore_ownership selected_entry.restore_permissions = @restore_permissions selected_entry.restore_times = @restore_times selected_entry end # Searches for entries given a glob def glob(*args, &block) @entry_set.glob(*args, &block) end # Searches for an entry just as find_entry, but throws Errno::ENOENT # if no entry is found. def get_entry(entry) selected_entry = find_entry(entry) raise Errno::ENOENT, entry if selected_entry.nil? selected_entry end # Creates a directory def mkdir(entry_name, permission = 0o755) raise Errno::EEXIST, "File exists - #{entry_name}" if find_entry(entry_name) entry_name = entry_name.dup.to_s entry_name << '/' unless entry_name.end_with?('/') @entry_set << ::Zip::StreamableDirectory.new(@name, entry_name, nil, permission) end private def directory?(new_entry, src_path) path_is_directory = ::File.directory?(src_path) if new_entry.directory? && !path_is_directory raise ArgumentError, "entry name '#{new_entry}' indicates directory entry, but " \ "'#{src_path}' is not a directory" elsif !new_entry.directory? && path_is_directory new_entry.name += '/' end new_entry.directory? && path_is_directory end def check_entry_exists(entry_name, continue_on_exists_proc, proc_name) continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc } return unless @entry_set.include?(entry_name) if continue_on_exists_proc.call remove get_entry(entry_name) else raise ::Zip::EntryExistsError, proc_name + " failed. Entry #{entry_name} already exists" end end def check_file(path) raise Errno::ENOENT, path unless ::File.readable?(path) end def on_success_replace dirname, basename = ::File.split(name) ::Dir::Tmpname.create(basename, dirname) do |tmp_filename| begin if yield tmp_filename ::File.rename(tmp_filename, name) ::File.chmod(@file_permissions, name) unless @create end ensure ::File.unlink(tmp_filename) if ::File.exist?(tmp_filename) end end end end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/filesystem.rb000066400000000000000000000374211363314356200173160ustar00rootroot00000000000000require 'zip' module Zip # The ZipFileSystem API provides an API for accessing entries in # a zip archive that is similar to ruby's builtin File and Dir # classes. # # Requiring 'zip/filesystem' includes this module in Zip::File # making the methods in this module available on Zip::File objects. # # Using this API the following example creates a new zip file # my.zip containing a normal entry with the name # first.txt, a directory entry named mydir # and finally another normal entry named second.txt # # require 'zip/filesystem' # # Zip::File.open("my.zip", Zip::File::CREATE) { # |zipfile| # zipfile.file.open("first.txt", "w") { |f| f.puts "Hello world" } # zipfile.dir.mkdir("mydir") # zipfile.file.open("mydir/second.txt", "w") { |f| f.puts "Hello again" } # } # # Reading is as easy as writing, as the following example shows. The # example writes the contents of first.txt from zip archive # my.zip to standard out. # # require 'zip/filesystem' # # Zip::File.open("my.zip") { # |zipfile| # puts zipfile.file.read("first.txt") # } module FileSystem def initialize # :nodoc: mapped_zip = ZipFileNameMapper.new(self) @zip_fs_dir = ZipFsDir.new(mapped_zip) @zip_fs_file = ZipFsFile.new(mapped_zip) @zip_fs_dir.file = @zip_fs_file @zip_fs_file.dir = @zip_fs_dir end # Returns a ZipFsDir which is much like ruby's builtin Dir (class) # object, except it works on the Zip::File on which this method is # invoked def dir @zip_fs_dir end # Returns a ZipFsFile which is much like ruby's builtin File (class) # object, except it works on the Zip::File on which this method is # invoked def file @zip_fs_file end # Instances of this class are normally accessed via the accessor # Zip::File::file. An instance of ZipFsFile behaves like ruby's # builtin File (class) object, except it works on Zip::File entries. # # The individual methods are not documented due to their # similarity with the methods in File class ZipFsFile attr_writer :dir # protected :dir class ZipFsStat class << self def delegate_to_fs_file(*methods) methods.each do |method| class_eval <<-END_EVAL, __FILE__, __LINE__ + 1 def #{method} # def file? @zip_fs_file.#{method}(@entry_name) # @zip_fs_file.file?(@entry_name) end # end END_EVAL end end end def initialize(zip_fs_file, entry_name) @zip_fs_file = zip_fs_file @entry_name = entry_name end def kind_of?(type) super || type == ::File::Stat end delegate_to_fs_file :file?, :directory?, :pipe?, :chardev?, :symlink?, :socket?, :blockdev?, :readable?, :readable_real?, :writable?, :ctime, :writable_real?, :executable?, :executable_real?, :sticky?, :owned?, :grpowned?, :setuid?, :setgid?, :zero?, :size, :size?, :mtime, :atime def blocks nil end def get_entry @zip_fs_file.__send__(:get_entry, @entry_name) end private :get_entry def gid e = get_entry if e.extra.member? 'IUnix' e.extra['IUnix'].gid || 0 else 0 end end def uid e = get_entry if e.extra.member? 'IUnix' e.extra['IUnix'].uid || 0 else 0 end end def ino 0 end def dev 0 end def rdev 0 end def rdev_major 0 end def rdev_minor 0 end def ftype if file? 'file' elsif directory? 'directory' else raise StandardError, 'Unknown file type' end end def nlink 1 end def blksize nil end def mode e = get_entry if e.fstype == 3 e.external_file_attributes >> 16 else 33_206 # 33206 is equivalent to -rw-rw-rw- end end end def initialize(mapped_zip) @mapped_zip = mapped_zip end def get_entry(filename) unless exists?(filename) raise Errno::ENOENT, "No such file or directory - #{filename}" end @mapped_zip.find_entry(filename) end private :get_entry def unix_mode_cmp(filename, mode) e = get_entry(filename) e.fstype == 3 && ((e.external_file_attributes >> 16) & mode) != 0 rescue Errno::ENOENT false end private :unix_mode_cmp def exists?(filename) expand_path(filename) == '/' || !@mapped_zip.find_entry(filename).nil? end alias exist? exists? # Permissions not implemented, so if the file exists it is accessible alias owned? exists? alias grpowned? exists? def readable?(filename) unix_mode_cmp(filename, 0o444) end alias readable_real? readable? def writable?(filename) unix_mode_cmp(filename, 0o222) end alias writable_real? writable? def executable?(filename) unix_mode_cmp(filename, 0o111) end alias executable_real? executable? def setuid?(filename) unix_mode_cmp(filename, 0o4000) end def setgid?(filename) unix_mode_cmp(filename, 0o2000) end def sticky?(filename) unix_mode_cmp(filename, 0o1000) end def umask(*args) ::File.umask(*args) end def truncate(_filename, _len) raise StandardError, 'truncate not supported' end def directory?(filename) entry = @mapped_zip.find_entry(filename) expand_path(filename) == '/' || (!entry.nil? && entry.directory?) end def open(filename, mode = 'r', permissions = 0o644, &block) mode.delete!('b') # ignore b option case mode when 'r' @mapped_zip.get_input_stream(filename, &block) when 'w' @mapped_zip.get_output_stream(filename, permissions, &block) else raise StandardError, "openmode '#{mode} not supported" unless mode == 'r' end end def new(filename, mode = 'r') self.open(filename, mode) end def size(filename) @mapped_zip.get_entry(filename).size end # Returns nil for not found and nil for directories def size?(filename) entry = @mapped_zip.find_entry(filename) entry.nil? || entry.directory? ? nil : entry.size end def chown(owner, group, *filenames) filenames.each do |filename| e = get_entry(filename) e.extra.create('IUnix') unless e.extra.member?('IUnix') e.extra['IUnix'].uid = owner e.extra['IUnix'].gid = group end filenames.size end def chmod(mode, *filenames) filenames.each do |filename| e = get_entry(filename) e.fstype = 3 # force convertion filesystem type to unix e.unix_perms = mode e.external_file_attributes = mode << 16 e.dirty = true end filenames.size end def zero?(filename) sz = size(filename) sz.nil? || sz == 0 rescue Errno::ENOENT false end def file?(filename) entry = @mapped_zip.find_entry(filename) !entry.nil? && entry.file? end def dirname(filename) ::File.dirname(filename) end def basename(filename) ::File.basename(filename) end def split(filename) ::File.split(filename) end def join(*fragments) ::File.join(*fragments) end def utime(modified_time, *filenames) filenames.each do |filename| get_entry(filename).time = modified_time end end def mtime(filename) @mapped_zip.get_entry(filename).mtime end def atime(filename) e = get_entry(filename) if e.extra.member? 'UniversalTime' e.extra['UniversalTime'].atime elsif e.extra.member? 'NTFS' e.extra['NTFS'].atime end end def ctime(filename) e = get_entry(filename) if e.extra.member? 'UniversalTime' e.extra['UniversalTime'].ctime elsif e.extra.member? 'NTFS' e.extra['NTFS'].ctime end end def pipe?(_filename) false end def blockdev?(_filename) false end def chardev?(_filename) false end def symlink?(_filename) false end def socket?(_filename) false end def ftype(filename) @mapped_zip.get_entry(filename).directory? ? 'directory' : 'file' end def readlink(_filename) raise NotImplementedError, 'The readlink() function is not implemented' end def symlink(_filename, _symlink_name) raise NotImplementedError, 'The symlink() function is not implemented' end def link(_filename, _symlink_name) raise NotImplementedError, 'The link() function is not implemented' end def pipe raise NotImplementedError, 'The pipe() function is not implemented' end def stat(filename) raise Errno::ENOENT, filename unless exists?(filename) ZipFsStat.new(self, filename) end alias lstat stat def readlines(filename) self.open(filename, &:readlines) end def read(filename) @mapped_zip.read(filename) end def popen(*args, &a_proc) ::File.popen(*args, &a_proc) end def foreach(filename, sep = $INPUT_RECORD_SEPARATOR, &a_proc) self.open(filename) { |is| is.each_line(sep, &a_proc) } end def delete(*args) args.each do |filename| if directory?(filename) raise Errno::EISDIR, "Is a directory - \"#{filename}\"" end @mapped_zip.remove(filename) end end def rename(file_to_rename, new_name) @mapped_zip.rename(file_to_rename, new_name) { true } end alias unlink delete def expand_path(path) @mapped_zip.expand_path(path) end end # Instances of this class are normally accessed via the accessor # ZipFile::dir. An instance of ZipFsDir behaves like ruby's # builtin Dir (class) object, except it works on ZipFile entries. # # The individual methods are not documented due to their # similarity with the methods in Dir class ZipFsDir def initialize(mapped_zip) @mapped_zip = mapped_zip end attr_writer :file def new(directory_name) ZipFsDirIterator.new(entries(directory_name)) end def open(directory_name) dir_iter = new(directory_name) if block_given? begin yield(dir_iter) return nil ensure dir_iter.close end end dir_iter end def pwd @mapped_zip.pwd end alias getwd pwd def chdir(directory_name) unless @file.stat(directory_name).directory? raise Errno::EINVAL, "Invalid argument - #{directory_name}" end @mapped_zip.pwd = @file.expand_path(directory_name) end def entries(directory_name) entries = [] foreach(directory_name) { |e| entries << e } entries end def glob(*args, &block) @mapped_zip.glob(*args, &block) end def foreach(directory_name) unless @file.stat(directory_name).directory? raise Errno::ENOTDIR, directory_name end path = @file.expand_path(directory_name) path << '/' unless path.end_with?('/') path = Regexp.escape(path) subdir_entry_regex = Regexp.new("^#{path}([^/]+)$") @mapped_zip.each do |filename| match = subdir_entry_regex.match(filename) yield(match[1]) unless match.nil? end end def delete(entry_name) unless @file.stat(entry_name).directory? raise Errno::EINVAL, "Invalid argument - #{entry_name}" end @mapped_zip.remove(entry_name) end alias rmdir delete alias unlink delete def mkdir(entry_name, permissions = 0o755) @mapped_zip.mkdir(entry_name, permissions) end def chroot(*_args) raise NotImplementedError, 'The chroot() function is not implemented' end end class ZipFsDirIterator # :nodoc:all include Enumerable def initialize(filenames) @filenames = filenames @index = 0 end def close @filenames = nil end def each(&a_proc) raise IOError, 'closed directory' if @filenames.nil? @filenames.each(&a_proc) end def read raise IOError, 'closed directory' if @filenames.nil? @filenames[(@index += 1) - 1] end def rewind raise IOError, 'closed directory' if @filenames.nil? @index = 0 end def seek(position) raise IOError, 'closed directory' if @filenames.nil? @index = position end def tell raise IOError, 'closed directory' if @filenames.nil? @index end end # All access to Zip::File from ZipFsFile and ZipFsDir goes through a # ZipFileNameMapper, which has one responsibility: ensure class ZipFileNameMapper # :nodoc:all include Enumerable def initialize(zip_file) @zip_file = zip_file @pwd = '/' end attr_accessor :pwd def find_entry(filename) @zip_file.find_entry(expand_to_entry(filename)) end def get_entry(filename) @zip_file.get_entry(expand_to_entry(filename)) end def get_input_stream(filename, &a_proc) @zip_file.get_input_stream(expand_to_entry(filename), &a_proc) end def get_output_stream(filename, permissions = nil, &a_proc) @zip_file.get_output_stream( expand_to_entry(filename), permissions, &a_proc ) end def glob(pattern, *flags, &block) @zip_file.glob(expand_to_entry(pattern), *flags, &block) end def read(filename) @zip_file.read(expand_to_entry(filename)) end def remove(filename) @zip_file.remove(expand_to_entry(filename)) end def rename(filename, new_name, &continue_on_exists_proc) @zip_file.rename( expand_to_entry(filename), expand_to_entry(new_name), &continue_on_exists_proc ) end def mkdir(filename, permissions = 0o755) @zip_file.mkdir(expand_to_entry(filename), permissions) end # Turns entries into strings and adds leading / # and removes trailing slash on directories def each @zip_file.each do |e| yield('/' + e.to_s.chomp('/')) end end def expand_path(path) expanded = path.start_with?('/') ? path : ::File.join(@pwd, path) expanded.gsub!(/\/\.(\/|$)/, '') expanded.gsub!(/[^\/]+\/\.\.(\/|$)/, '') expanded.empty? ? '/' : expanded end private def expand_to_entry(path) expand_path(path)[1..-1] end end end class File include FileSystem end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/inflater.rb000066400000000000000000000023701363314356200167310ustar00rootroot00000000000000module Zip class Inflater < Decompressor #:nodoc:all def initialize(*args) super @buffer = +'' @zlib_inflater = ::Zlib::Inflate.new(-Zlib::MAX_WBITS) end def read(length = nil, outbuf = '') return (length.nil? || length.zero? ? '' : nil) if eof while length.nil? || (@buffer.bytesize < length) break if input_finished? @buffer << produce_input end outbuf.replace(@buffer.slice!(0...(length || @buffer.bytesize))) end def eof @buffer.empty? && input_finished? end alias eof? eof private def produce_input retried = 0 begin @zlib_inflater.inflate(input_stream.read(Decompressor::CHUNK_SIZE)) rescue Zlib::BufError raise if retried >= 5 # how many times should we retry? retried += 1 retry end rescue Zlib::Error raise(::Zip::DecompressionError, 'zlib error while inflating') end def input_finished? @zlib_inflater.finished? end end ::Zip::Decompressor.register(::Zip::COMPRESSION_METHOD_DEFLATE, ::Zip::Inflater) end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/input_stream.rb000066400000000000000000000127701363314356200176440ustar00rootroot00000000000000module Zip # InputStream is the basic class for reading zip entries in a # zip file. It is possible to create a InputStream object directly, # passing the zip file name to the constructor, but more often than not # the InputStream will be obtained from a File (perhaps using the # ZipFileSystem interface) object for a particular entry in the zip # archive. # # A InputStream inherits IOExtras::AbstractInputStream in order # to provide an IO-like interface for reading from a single zip # entry. Beyond methods for mimicking an IO-object it contains # the method get_next_entry for iterating through the entries of # an archive. get_next_entry returns a Entry object that describes # the zip entry the InputStream is currently reading from. # # Example that creates a zip archive with ZipOutputStream and reads it # back again with a InputStream. # # require 'zip' # # Zip::OutputStream.open("my.zip") do |io| # # io.put_next_entry("first_entry.txt") # io.write "Hello world!" # # io.put_next_entry("adir/first_entry.txt") # io.write "Hello again!" # end # # # Zip::InputStream.open("my.zip") do |io| # # while (entry = io.get_next_entry) # puts "Contents of #{entry.name}: '#{io.read}'" # end # end # # java.util.zip.ZipInputStream is the original inspiration for this # class. class InputStream CHUNK_SIZE = 32_768 include ::Zip::IOExtras::AbstractInputStream # Opens the indicated zip file. An exception is thrown # if the specified offset in the specified filename is # not a local zip entry header. # # @param context [String||IO||StringIO] file path or IO/StringIO object # @param offset [Integer] offset in the IO/StringIO def initialize(context, offset = 0, decrypter = nil) super() @archive_io = get_io(context, offset) @decompressor = ::Zip::NullDecompressor @decrypter = decrypter || ::Zip::NullDecrypter.new @current_entry = nil end def close @archive_io.close end # Returns a Entry object. It is necessary to call this # method on a newly created InputStream before reading from # the first entry in the archive. Returns nil when there are # no more entries. def get_next_entry @archive_io.seek(@current_entry.next_header_offset, IO::SEEK_SET) if @current_entry open_entry end # Rewinds the stream to the beginning of the current entry def rewind return if @current_entry.nil? @lineno = 0 @pos = 0 @archive_io.seek(@current_entry.local_header_offset, IO::SEEK_SET) open_entry end # Modeled after IO.sysread def sysread(length = nil, outbuf = '') @decompressor.read(length, outbuf) end class << self # Same as #initialize but if a block is passed the opened # stream is passed to the block and closed when the block # returns. def open(filename_or_io, offset = 0, decrypter = nil) zio = new(filename_or_io, offset, decrypter) return zio unless block_given? begin yield zio ensure zio.close if zio end end def open_buffer(filename_or_io, offset = 0) warn 'open_buffer is deprecated!!! Use open instead!' ::Zip::InputStream.open(filename_or_io, offset) end end protected def get_io(io_or_file, offset = 0) if io_or_file.respond_to?(:seek) io = io_or_file.dup io.seek(offset, ::IO::SEEK_SET) io else file = ::File.open(io_or_file, 'rb') file.seek(offset, ::IO::SEEK_SET) file end end def open_entry @current_entry = ::Zip::Entry.read_local_entry(@archive_io) if @current_entry && @current_entry.encrypted? && @decrypter.kind_of?(NullEncrypter) raise Error, 'password required to decode zip file' end if @current_entry && @current_entry.incomplete? && @current_entry.crc == 0 \ && @current_entry.compressed_size == 0 \ && @current_entry.size == 0 && !@complete_entry raise GPFBit3Error, 'General purpose flag Bit 3 is set so not possible to get proper info from local header.' \ 'Please use ::Zip::File instead of ::Zip::InputStream' end @decrypted_io = get_decrypted_io @decompressor = get_decompressor flush @current_entry end def get_decrypted_io header = @archive_io.read(@decrypter.header_bytesize) @decrypter.reset!(header) ::Zip::DecryptedIo.new(@archive_io, @decrypter) end def get_decompressor return ::Zip::NullDecompressor if @current_entry.nil? decompressed_size = if @current_entry.incomplete? && @current_entry.crc == 0 && @current_entry.size == 0 && @complete_entry @complete_entry.size else @current_entry.size end decompressor_class = ::Zip::Decompressor.find_by_compression_method(@current_entry.compression_method) if decompressor_class.nil? raise ::Zip::CompressionMethodError, "Unsupported compression method #{@current_entry.compression_method}" end decompressor_class.new(@decrypted_io, decompressed_size) end def produce_input @decompressor.read(CHUNK_SIZE) end def input_finished? @decompressor.eof end end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/ioextras.rb000066400000000000000000000016221363314356200167620ustar00rootroot00000000000000module Zip module IOExtras #:nodoc: CHUNK_SIZE = 131_072 RANGE_ALL = 0..-1 class << self def copy_stream(ostream, istream) ostream.write(istream.read(CHUNK_SIZE, '')) until istream.eof? end def copy_stream_n(ostream, istream, nbytes) toread = nbytes while toread > 0 && !istream.eof? tr = toread > CHUNK_SIZE ? CHUNK_SIZE : toread ostream.write(istream.read(tr, '')) toread -= tr end end end # Implements kind_of? in order to pretend to be an IO object module FakeIO def kind_of?(object) object == IO || super end end end end require 'zip/ioextras/abstract_input_stream' require 'zip/ioextras/abstract_output_stream' # Copyright (C) 2002-2004 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/ioextras/000077500000000000000000000000001363314356200164345ustar00rootroot00000000000000rubyzip-2.3.0/lib/zip/ioextras/abstract_input_stream.rb000066400000000000000000000070221363314356200233570ustar00rootroot00000000000000module Zip module IOExtras # Implements many of the convenience methods of IO # such as gets, getc, readline and readlines # depends on: input_finished?, produce_input and read module AbstractInputStream include Enumerable include FakeIO def initialize super @lineno = 0 @pos = 0 @output_buffer = '' end attr_accessor :lineno attr_reader :pos def read(number_of_bytes = nil, buf = '') tbuf = if @output_buffer.bytesize > 0 if number_of_bytes <= @output_buffer.bytesize @output_buffer.slice!(0, number_of_bytes) else number_of_bytes -= @output_buffer.bytesize if number_of_bytes rbuf = sysread(number_of_bytes, buf) out = @output_buffer out << rbuf if rbuf @output_buffer = '' out end else sysread(number_of_bytes, buf) end if tbuf.nil? || tbuf.empty? return nil if number_of_bytes return '' end @pos += tbuf.length if buf buf.replace(tbuf) else buf = tbuf end buf end def readlines(a_sep_string = $INPUT_RECORD_SEPARATOR) ret_val = [] each_line(a_sep_string) { |line| ret_val << line } ret_val end def gets(a_sep_string = $INPUT_RECORD_SEPARATOR, number_of_bytes = nil) @lineno = @lineno.next if number_of_bytes.respond_to?(:to_int) number_of_bytes = number_of_bytes.to_int a_sep_string = a_sep_string.to_str if a_sep_string elsif a_sep_string.respond_to?(:to_int) number_of_bytes = a_sep_string.to_int a_sep_string = $INPUT_RECORD_SEPARATOR else number_of_bytes = nil a_sep_string = a_sep_string.to_str if a_sep_string end return read(number_of_bytes) if a_sep_string.nil? a_sep_string = "#{$INPUT_RECORD_SEPARATOR}#{$INPUT_RECORD_SEPARATOR}" if a_sep_string.empty? buffer_index = 0 over_limit = (number_of_bytes && @output_buffer.bytesize >= number_of_bytes) while (match_index = @output_buffer.index(a_sep_string, buffer_index)).nil? && !over_limit buffer_index = [buffer_index, @output_buffer.bytesize - a_sep_string.bytesize].max return @output_buffer.empty? ? nil : flush if input_finished? @output_buffer << produce_input over_limit = (number_of_bytes && @output_buffer.bytesize >= number_of_bytes) end sep_index = [match_index + a_sep_string.bytesize, number_of_bytes || @output_buffer.bytesize].min @pos += sep_index @output_buffer.slice!(0...sep_index) end def ungetc(byte) @output_buffer = byte.chr + @output_buffer end def flush ret_val = @output_buffer @output_buffer = '' ret_val end def readline(a_sep_string = $INPUT_RECORD_SEPARATOR) ret_val = gets(a_sep_string) raise EOFError unless ret_val ret_val end def each_line(a_sep_string = $INPUT_RECORD_SEPARATOR) loop { yield readline(a_sep_string) } rescue EOFError # We just need to catch this; we don't need to handle it. end alias each each_line def eof @output_buffer.empty? && input_finished? end alias eof? eof end end end rubyzip-2.3.0/lib/zip/ioextras/abstract_output_stream.rb000066400000000000000000000020151363314356200235550ustar00rootroot00000000000000module Zip module IOExtras # Implements many of the output convenience methods of IO. # relies on << module AbstractOutputStream include FakeIO def write(data) self << data data.to_s.bytesize end def print(*params) self << params.join($OUTPUT_FIELD_SEPARATOR) << $OUTPUT_RECORD_SEPARATOR.to_s end def printf(a_format_string, *params) self << format(a_format_string, *params) end def putc(an_object) self << case an_object when Integer an_object.chr when String an_object else raise TypeError, 'putc: Only Integer and String supported' end an_object end def puts(*params) params << "\n" if params.empty? params.flatten.each do |element| val = element.to_s self << val self << "\n" unless val[-1, 1] == "\n" end end end end end rubyzip-2.3.0/lib/zip/null_compressor.rb000066400000000000000000000005371363314356200203560ustar00rootroot00000000000000module Zip class NullCompressor < Compressor #:nodoc:all include Singleton def <<(_data) raise IOError, 'closed stream' end attr_reader :size, :compressed_size end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/null_decompressor.rb000066400000000000000000000005341363314356200206640ustar00rootroot00000000000000module Zip module NullDecompressor #:nodoc:all module_function def read(_length = nil, _outbuf = nil) nil end def eof true end alias eof? eof end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/null_input_stream.rb000066400000000000000000000004521363314356200206700ustar00rootroot00000000000000module Zip module NullInputStream #:nodoc:all include ::Zip::NullDecompressor include ::Zip::IOExtras::AbstractInputStream end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/output_stream.rb000066400000000000000000000143631363314356200200450ustar00rootroot00000000000000module Zip # ZipOutputStream is the basic class for writing zip files. It is # possible to create a ZipOutputStream object directly, passing # the zip file name to the constructor, but more often than not # the ZipOutputStream will be obtained from a ZipFile (perhaps using the # ZipFileSystem interface) object for a particular entry in the zip # archive. # # A ZipOutputStream inherits IOExtras::AbstractOutputStream in order # to provide an IO-like interface for writing to a single zip # entry. Beyond methods for mimicking an IO-object it contains # the method put_next_entry that closes the current entry # and creates a new. # # Please refer to ZipInputStream for example code. # # java.util.zip.ZipOutputStream is the original inspiration for this # class. class OutputStream include ::Zip::IOExtras::AbstractOutputStream attr_accessor :comment # Opens the indicated zip file. If a file with that name already # exists it will be overwritten. def initialize(file_name, stream = false, encrypter = nil) super() @file_name = file_name @output_stream = if stream iostream = @file_name.dup iostream.reopen(@file_name) iostream.rewind iostream else ::File.new(@file_name, 'wb') end @entry_set = ::Zip::EntrySet.new @compressor = ::Zip::NullCompressor.instance @encrypter = encrypter || ::Zip::NullEncrypter.new @closed = false @current_entry = nil @comment = nil end # Same as #initialize but if a block is passed the opened # stream is passed to the block and closed when the block # returns. class << self def open(file_name, encrypter = nil) return new(file_name) unless block_given? zos = new(file_name, false, encrypter) yield zos ensure zos.close if zos end # Same as #open but writes to a filestream instead def write_buffer(io = ::StringIO.new(''), encrypter = nil) io.binmode if io.respond_to?(:binmode) zos = new(io, true, encrypter) yield zos zos.close_buffer end end # Closes the stream and writes the central directory to the zip file def close return if @closed finalize_current_entry update_local_headers write_central_directory @output_stream.close @closed = true end # Closes the stream and writes the central directory to the zip file def close_buffer return @output_stream if @closed finalize_current_entry update_local_headers write_central_directory @closed = true @output_stream end # Closes the current entry and opens a new for writing. # +entry+ can be a ZipEntry object or a string. def put_next_entry(entry_name, comment = nil, extra = nil, compression_method = Entry::DEFLATED, level = Zip.default_compression) raise Error, 'zip stream is closed' if @closed new_entry = if entry_name.kind_of?(Entry) entry_name else Entry.new(@file_name, entry_name.to_s) end new_entry.comment = comment unless comment.nil? unless extra.nil? new_entry.extra = extra.kind_of?(ExtraField) ? extra : ExtraField.new(extra.to_s) end new_entry.compression_method = compression_method unless compression_method.nil? init_next_entry(new_entry, level) @current_entry = new_entry end def copy_raw_entry(entry) entry = entry.dup raise Error, 'zip stream is closed' if @closed raise Error, 'entry is not a ZipEntry' unless entry.kind_of?(Entry) finalize_current_entry @entry_set << entry src_pos = entry.local_header_offset entry.write_local_entry(@output_stream) @compressor = NullCompressor.instance entry.get_raw_input_stream do |is| is.seek(src_pos, IO::SEEK_SET) ::Zip::Entry.read_local_entry(is) IOExtras.copy_stream_n(@output_stream, is, entry.compressed_size) end @compressor = NullCompressor.instance @current_entry = nil end private def finalize_current_entry return unless @current_entry finish @current_entry.compressed_size = @output_stream.tell - \ @current_entry.local_header_offset - \ @current_entry.calculate_local_header_size @current_entry.size = @compressor.size @current_entry.crc = @compressor.crc @output_stream << @encrypter.data_descriptor(@current_entry.crc, @current_entry.compressed_size, @current_entry.size) @current_entry.gp_flags |= @encrypter.gp_flags @current_entry = nil @compressor = ::Zip::NullCompressor.instance end def init_next_entry(entry, level = Zip.default_compression) finalize_current_entry @entry_set << entry entry.write_local_entry(@output_stream) @encrypter.reset! @output_stream << @encrypter.header(entry.mtime) @compressor = get_compressor(entry, level) end def get_compressor(entry, level) case entry.compression_method when Entry::DEFLATED ::Zip::Deflater.new(@output_stream, level, @encrypter) when Entry::STORED ::Zip::PassThruCompressor.new(@output_stream) else raise ::Zip::CompressionMethodError, "Invalid compression method: '#{entry.compression_method}'" end end def update_local_headers pos = @output_stream.pos @entry_set.each do |entry| @output_stream.pos = entry.local_header_offset entry.write_local_entry(@output_stream, true) end @output_stream.pos = pos end def write_central_directory cdir = CentralDirectory.new(@entry_set, @comment) cdir.write_to_stream(@output_stream) end protected def finish @compressor.finish end public # Modeled after IO.<< def <<(data) @compressor << data self end end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/pass_thru_compressor.rb000066400000000000000000000010211363314356200214010ustar00rootroot00000000000000module Zip class PassThruCompressor < Compressor #:nodoc:all def initialize(output_stream) super() @output_stream = output_stream @crc = Zlib.crc32 @size = 0 end def <<(data) val = data.to_s @crc = Zlib.crc32(val, @crc) @size += val.bytesize @output_stream << val end attr_reader :size, :crc end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/pass_thru_decompressor.rb000066400000000000000000000014211363314356200217160ustar00rootroot00000000000000module Zip class PassThruDecompressor < Decompressor #:nodoc:all def initialize(*args) super @read_so_far = 0 end def read(length = nil, outbuf = '') return (length.nil? || length.zero? ? '' : nil) if eof if length.nil? || (@read_so_far + length) > decompressed_size length = decompressed_size - @read_so_far end @read_so_far += length input_stream.read(length, outbuf) end def eof @read_so_far >= decompressed_size end alias eof? eof end ::Zip::Decompressor.register(::Zip::COMPRESSION_METHOD_STORE, ::Zip::PassThruDecompressor) end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/streamable_directory.rb000066400000000000000000000007061363314356200213310ustar00rootroot00000000000000module Zip class StreamableDirectory < Entry def initialize(zipfile, entry, src_path = nil, permission = nil) super(zipfile, entry) @ftype = :directory entry.get_extra_attributes_from_path(src_path) if src_path @unix_perms = permission if permission end end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/streamable_stream.rb000066400000000000000000000022601363314356200206150ustar00rootroot00000000000000module Zip class StreamableStream < DelegateClass(Entry) # :nodoc:all def initialize(entry) super(entry) @temp_file = Tempfile.new(::File.basename(name)) @temp_file.binmode end def get_output_stream if block_given? begin yield(@temp_file) ensure @temp_file.close end else @temp_file end end def get_input_stream unless @temp_file.closed? raise StandardError, "cannot open entry for reading while its open for writing - #{name}" end @temp_file.open # reopens tempfile from top @temp_file.binmode if block_given? begin yield(@temp_file) ensure @temp_file.close end else @temp_file end end def write_to_zip_output_stream(output_stream) output_stream.put_next_entry(self) get_input_stream { |is| ::Zip::IOExtras.copy_stream(output_stream, is) } end def clean_up @temp_file.unlink end end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/lib/zip/version.rb000066400000000000000000000000431363314356200166050ustar00rootroot00000000000000module Zip VERSION = '2.3.0' end rubyzip-2.3.0/rubyzip.gemspec000066400000000000000000000026441363314356200163050ustar00rootroot00000000000000lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'zip/version' Gem::Specification.new do |s| s.name = 'rubyzip' s.version = ::Zip::VERSION s.authors = ['Alexander Simonov'] s.email = ['alex@simonov.me'] s.homepage = 'http://github.com/rubyzip/rubyzip' s.platform = Gem::Platform::RUBY s.summary = 'rubyzip is a ruby module for reading and writing zip files' s.files = Dir.glob('{samples,lib}/**/*.rb') + %w[README.md TODO Rakefile] s.require_paths = ['lib'] s.license = 'BSD 2-Clause' s.metadata = { 'bug_tracker_uri' => 'https://github.com/rubyzip/rubyzip/issues', 'changelog_uri' => "https://github.com/rubyzip/rubyzip/blob/v#{s.version}/Changelog.md", 'documentation_uri' => "https://www.rubydoc.info/gems/rubyzip/#{s.version}", 'source_code_uri' => "https://github.com/rubyzip/rubyzip/tree/v#{s.version}", 'wiki_uri' => 'https://github.com/rubyzip/rubyzip/wiki' } s.required_ruby_version = '>= 2.4' s.add_development_dependency 'coveralls', '~> 0.7' s.add_development_dependency 'minitest', '~> 5.4' s.add_development_dependency 'pry', '~> 0.10' s.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3' s.add_development_dependency 'rubocop', '~> 0.79' end rubyzip-2.3.0/samples/000077500000000000000000000000001363314356200146725ustar00rootroot00000000000000rubyzip-2.3.0/samples/.cvsignore000066400000000000000000000000711363314356200166700ustar00rootroot00000000000000example.zip exampleout.zip filesystem.zip zipdialogui.rb rubyzip-2.3.0/samples/example.rb000077500000000000000000000051451363314356200166620ustar00rootroot00000000000000#!/usr/bin/env ruby $LOAD_PATH << '../lib' system('zip example.zip example.rb gtk_ruby_zip.rb') require 'zip' ####### Using ZipInputStream alone: ####### Zip::InputStream.open('example.zip') do |zis| entry = zis.get_next_entry print "First line of '#{entry.name} (#{entry.size} bytes): " puts "'#{zis.gets.chomp}'" entry = zis.get_next_entry print "First line of '#{entry.name} (#{entry.size} bytes): " puts "'#{zis.gets.chomp}'" end ####### Using ZipFile to read the directory of a zip file: ####### zf = Zip::File.new('example.zip') zf.each_with_index do |entry, index| puts "entry #{index} is #{entry.name}, size = #{entry.size}, compressed size = #{entry.compressed_size}" # use zf.get_input_stream(entry) to get a ZipInputStream for the entry # entry can be the ZipEntry object or any object which has a to_s method that # returns the name of the entry. end ####### Using ZipOutputStream to write a zip file: ####### Zip::OutputStream.open('exampleout.zip') do |zos| zos.put_next_entry('the first little entry') zos.puts 'Hello hello hello hello hello hello hello hello hello' zos.put_next_entry('the second little entry') zos.puts 'Hello again' # Use rubyzip or your zip client of choice to verify # the contents of exampleout.zip end ####### Using ZipFile to change a zip file: ####### Zip::File.open('exampleout.zip') do |zip_file| zip_file.add('thisFile.rb', 'example.rb') zip_file.rename('thisFile.rb', 'ILikeThisName.rb') zip_file.add('Again', 'example.rb') end # Lets check Zip::File.open('exampleout.zip') do |zip_file| puts "Changed zip file contains: #{zip_file.entries.join(', ')}" zip_file.remove('Again') puts "Without 'Again': #{zip_file.entries.join(', ')}" end ####### Using ZipFile to split a zip file: ####### # Creating large zip file for splitting Zip::OutputStream.open('large_zip_file.zip') do |zos| puts 'Creating zip file...' 10.times do |i| zos.put_next_entry("large_entry_#{i}.txt") zos.puts 'Hello' * 104_857_600 end end # Splitting created large zip file part_zips_count = Zip::File.split('large_zip_file.zip', 2_097_152, false) puts "Zip file splitted in #{part_zips_count} parts" # Track splitting an archive Zip::File.split('large_zip_file.zip', 1_048_576, true, 'part_zip_file') do |part_count, part_index, chunk_bytes, segment_bytes| puts "#{part_index} of #{part_count} part splitting: #{(chunk_bytes.to_f / segment_bytes * 100).to_i}%" end # For other examples, look at zip.rb and ziptest.rb # Copyright (C) 2002 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/samples/example_filesystem.rb000077500000000000000000000016351363314356200211260ustar00rootroot00000000000000#!/usr/bin/env ruby $LOAD_PATH << '../lib' require 'zip/filesystem' EXAMPLE_ZIP = 'filesystem.zip' File.delete(EXAMPLE_ZIP) if File.exist?(EXAMPLE_ZIP) Zip::File.open(EXAMPLE_ZIP, Zip::File::CREATE) do |zf| zf.file.open('file1.txt', 'w') { |os| os.write 'first file1.txt' } zf.dir.mkdir('dir1') zf.dir.chdir('dir1') zf.file.open('file1.txt', 'w') { |os| os.write 'second file1.txt' } puts zf.file.read('file1.txt') puts zf.file.read('../file1.txt') zf.dir.chdir('..') zf.file.open('file2.txt', 'w') { |os| os.write 'first file2.txt' } puts "Entries: #{zf.entries.join(', ')}" end Zip::File.open(EXAMPLE_ZIP) do |zf| puts "Entries from reloaded zip: #{zf.entries.join(', ')}" end # For other examples, look at zip.rb and ziptest.rb # Copyright (C) 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/samples/example_recursive.rb000066400000000000000000000030771363314356200207500ustar00rootroot00000000000000require 'zip' # This is a simple example which uses rubyzip to # recursively generate a zip file from the contents of # a specified directory. The directory itself is not # included in the archive, rather just its contents. # # Usage: # directory_to_zip = "/tmp/input" # output_file = "/tmp/out.zip" # zf = ZipFileGenerator.new(directory_to_zip, output_file) # zf.write() class ZipFileGenerator # Initialize with the directory to zip and the location of the output archive. def initialize(input_dir, output_file) @input_dir = input_dir @output_file = output_file end # Zip the input directory. def write entries = Dir.entries(@input_dir) - %w[. ..] ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile| write_entries entries, '', zipfile end end private # A helper method to make the recursion work. def write_entries(entries, path, zipfile) entries.each do |e| zipfile_path = path == '' ? e : File.join(path, e) disk_file_path = File.join(@input_dir, zipfile_path) if File.directory? disk_file_path recursively_deflate_directory(disk_file_path, zipfile, zipfile_path) else put_into_archive(disk_file_path, zipfile, zipfile_path) end end end def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path) zipfile.mkdir zipfile_path subdir = Dir.entries(disk_file_path) - %w[. ..] write_entries subdir, zipfile_path, zipfile end def put_into_archive(disk_file_path, zipfile, zipfile_path) zipfile.add(zipfile_path, disk_file_path) end end rubyzip-2.3.0/samples/gtk_ruby_zip.rb000077500000000000000000000042211363314356200177310ustar00rootroot00000000000000#!/usr/bin/env ruby $LOAD_PATH << '../lib' $VERBOSE = true require 'gtk' require 'zip' class MainApp < Gtk::Window def initialize super() set_usize(400, 256) set_title('rubyzip') signal_connect(Gtk::Window::SIGNAL_DESTROY) { Gtk.main_quit } box = Gtk::VBox.new(false, 0) add(box) @zipfile = nil @button_panel = ButtonPanel.new @button_panel.open_button.signal_connect(Gtk::Button::SIGNAL_CLICKED) do show_file_selector end @button_panel.extract_button.signal_connect(Gtk::Button::SIGNAL_CLICKED) do puts 'Not implemented!' end box.pack_start(@button_panel, false, false, 0) sw = Gtk::ScrolledWindow.new sw.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC) box.pack_start(sw, true, true, 0) @clist = Gtk::CList.new(%w[Name Size Compression]) @clist.set_selection_mode(Gtk::SELECTION_BROWSE) @clist.set_column_width(0, 120) @clist.set_column_width(1, 120) @clist.signal_connect(Gtk::CList::SIGNAL_SELECT_ROW) do |_w, row, _column, _event| @selected_row = row end sw.add(@clist) end class ButtonPanel < Gtk::HButtonBox attr_reader :open_button, :extract_button def initialize super set_layout(Gtk::BUTTONBOX_START) set_spacing(0) @open_button = Gtk::Button.new('Open archive') @extract_button = Gtk::Button.new('Extract entry') pack_start(@open_button) pack_start(@extract_button) end end def show_file_selector @file_selector = Gtk::FileSelection.new('Open zip file') @file_selector.show @file_selector.ok_button.signal_connect(Gtk::Button::SIGNAL_CLICKED) do open_zip(@file_selector.filename) @file_selector.destroy end @file_selector.cancel_button.signal_connect(Gtk::Button::SIGNAL_CLICKED) do @file_selector.destroy end end def open_zip(filename) @zipfile = Zip::File.open(filename) @clist.clear @zipfile.each do |entry| @clist.append([entry.name, entry.size.to_s, (100.0 * entry.compressedSize / entry.size).to_s + '%']) end end end main_app = MainApp.new main_app.show_all Gtk.main rubyzip-2.3.0/samples/qtzip.rb000077500000000000000000000036451363314356200164010ustar00rootroot00000000000000#!/usr/bin/env ruby $VERBOSE = true $LOAD_PATH << '../lib' require 'Qt' system('rbuic -o zipdialogui.rb zipdialogui.ui') require 'zipdialogui.rb' require 'zip' a = Qt::Application.new(ARGV) class ZipDialog < ZipDialogUI def initialize super() connect(child('add_button'), SIGNAL('clicked()'), self, SLOT('add_files()')) connect(child('extract_button'), SIGNAL('clicked()'), self, SLOT('extract_files()')) end def zipfile(&a_proc) Zip::File.open(@zip_filename, &a_proc) end def each(&a_proc) Zip::File.foreach(@zip_filename, &a_proc) end def refresh lv = child('entry_list_view') lv.clear each do |e| lv.insert_item(Qt::ListViewItem.new(lv, e.name, e.size.to_s)) end end def load(zipfile) @zip_filename = zipfile refresh end def add_files l = Qt::FileDialog.getOpenFileNames(nil, nil, self) zipfile do |zf| l.each do |path| zf.add(File.basename(path), path) end end refresh end def extract_files selected_items = [] unselected_items = [] lv_item = entry_list_view.first_child while lv_item if entry_list_view.is_selected(lv_item) selected_items << lv_item.text(0) else unselected_items << lv_item.text(0) end lv_item = lv_item.next_sibling end puts "selected_items.size = #{selected_items.size}" puts "unselected_items.size = #{unselected_items.size}" items = !selected_items.empty? ? selected_items : unselected_items puts "items.size = #{items.size}" d = Qt::FileDialog.get_existing_directory(nil, self) if !d puts 'No directory chosen' else zipfile { |zf| items.each { |e| zf.extract(e, File.join(d, e)) } } end end slots 'add_files()', 'extract_files()' end unless ARGV[0] puts "usage: #{$PROGRAM_NAME} zipname" exit end zd = ZipDialog.new zd.load(ARGV[0]) a.mainWidget = zd zd.show a.exec rubyzip-2.3.0/samples/write_simple.rb000077500000000000000000000002521363314356200177240ustar00rootroot00000000000000#!/usr/bin/env ruby $LOAD_PATH << '../lib' require 'zip' ::Zip::OutputStream.open('simple.zip') do |zos| zos.put_next_entry 'entry.txt' zos.puts 'Hello world' end rubyzip-2.3.0/samples/zipdialogui.ui000066400000000000000000000120371363314356200175540ustar00rootroot00000000000000 ZipDialogUI ZipDialogUI 0 0 416 397 Rubyzip true unnamed Entry true true Size true true entry_list_view 150 200 Manual Extended AllColumns layout2 unnamed add_button &Add... Alt+A true extract_button &Extract... Alt+E true Spacer1 Vertical Expanding 20 160 close_button &Close Alt+C true true close_button clicked() ZipDialogUI accept() rubyzip-2.3.0/samples/zipfind.rb000077500000000000000000000027361363314356200166750ustar00rootroot00000000000000#!/usr/bin/env ruby $VERBOSE = true $LOAD_PATH << '../lib' require 'zip' require 'find' module Zip module ZipFind def self.find(path, zip_file_pattern = /\.zip$/i) Find.find(path) do |filename| yield(filename) next unless zip_file_pattern.match(filename) && File.file?(filename) begin Zip::File.foreach(filename) do |entry| yield(filename + File::SEPARATOR + entry.to_s) end rescue Errno::EACCES => e puts e end end end def self.find_file(path, filename_pattern, zip_file_pattern = /\.zip$/i) find(path, zip_file_pattern) do |filename| yield(filename) if filename_pattern.match(filename) end end end end if $PROGRAM_NAME == __FILE__ module ZipFindConsoleRunner PATH_ARG_INDEX = 0 FILENAME_PATTERN_ARG_INDEX = 1 ZIPFILE_PATTERN_ARG_INDEX = 2 def self.run(args) check_args(args) Zip::ZipFind.find_file(args[PATH_ARG_INDEX], args[FILENAME_PATTERN_ARG_INDEX], args[ZIPFILE_PATTERN_ARG_INDEX]) do |filename| report_entry_found filename end end def self.check_args(args) return if args.size == 3 usage exit end def self.usage puts "Usage: #{$PROGRAM_NAME} PATH ZIPFILENAME_PATTERN FILNAME_PATTERN" end def self.report_entry_found(filename) puts filename end end ZipFindConsoleRunner.run(ARGV) end rubyzip-2.3.0/test/000077500000000000000000000000001363314356200142055ustar00rootroot00000000000000rubyzip-2.3.0/test/basic_zip_file_test.rb000066400000000000000000000030301363314356200205270ustar00rootroot00000000000000require 'test_helper' class BasicZipFileTest < MiniTest::Test include AssertEntry def setup @zip_file = ::Zip::File.new(TestZipFile::TEST_ZIP2.zip_name) end def test_entries assert_equal(TestZipFile::TEST_ZIP2.entry_names.sort, @zip_file.entries.entries.sort.map(&:name)) end def test_each count = 0 visited = {} @zip_file.each do |entry| assert(TestZipFile::TEST_ZIP2.entry_names.include?(entry.name)) assert(!visited.include?(entry.name)) visited[entry.name] = nil count = count.succ end assert_equal(TestZipFile::TEST_ZIP2.entry_names.length, count) end def test_foreach count = 0 visited = {} ::Zip::File.foreach(TestZipFile::TEST_ZIP2.zip_name) do |entry| assert(TestZipFile::TEST_ZIP2.entry_names.include?(entry.name)) assert(!visited.include?(entry.name)) visited[entry.name] = nil count = count.succ end assert_equal(TestZipFile::TEST_ZIP2.entry_names.length, count) end def test_get_input_stream count = 0 visited = {} @zip_file.each do |entry| assert_entry(entry.name, @zip_file.get_input_stream(entry), entry.name) assert(!visited.include?(entry.name)) visited[entry.name] = nil count = count.succ end assert_equal(TestZipFile::TEST_ZIP2.entry_names.length, count) end def test_get_input_stream_block name = @zip_file.entries.first.name @zip_file.get_input_stream(name) do |zis| assert_entry_contents_for_stream(name, zis, name) end end end rubyzip-2.3.0/test/bzip2_support_test.rb000066400000000000000000000004421363314356200204130ustar00rootroot00000000000000require 'test_helper' class Bzip2SupportTest < MiniTest::Test BZIP2_ZIP_TEST_FILE = 'test/data/zipWithBzip2Compression.zip' def test_read Zip::InputStream.open(BZIP2_ZIP_TEST_FILE) do |zis| assert_raises(Zip::CompressionMethodError) { zis.get_next_entry } end end end rubyzip-2.3.0/test/case_sensitivity_test.rb000066400000000000000000000046541363314356200211670ustar00rootroot00000000000000require 'test_helper' class ZipCaseSensitivityTest < MiniTest::Test include CommonZipFileFixture SRC_FILES = [['test/data/file1.txt', 'testfile.rb'], ['test/data/file2.txt', 'testFILE.rb']] def teardown ::Zip.case_insensitive_match = false end # Ensure that everything functions normally when +case_insensitive_match = false+ def test_add_case_sensitive ::Zip.case_insensitive_match = false SRC_FILES.each { |fn, _en| assert(::File.exist?(fn)) } zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) SRC_FILES.each { |fn, en| zf.add(en, fn) } zf.close zf_read = ::Zip::File.new(EMPTY_FILENAME) assert_equal(SRC_FILES.size, zf_read.entries.length) SRC_FILES.each_with_index do |a, i| assert_equal(a.last, zf_read.entries[i].name) AssertEntry.assert_contents(a.first, zf_read.get_input_stream(a.last, &:read)) end end # Ensure that names are treated case insensitively when adding files and +case_insensitive_match = false+ def test_add_case_insensitive ::Zip.case_insensitive_match = true SRC_FILES.each { |fn, _en| assert(::File.exist?(fn)) } zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) assert_raises Zip::EntryExistsError do SRC_FILES.each { |fn, en| zf.add(en, fn) } end end # Ensure that names are treated case insensitively when reading files and +case_insensitive_match = true+ def test_add_case_sensitive_read_case_insensitive ::Zip.case_insensitive_match = false SRC_FILES.each { |fn, _en| assert(::File.exist?(fn)) } zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) SRC_FILES.each { |fn, en| zf.add(en, fn) } zf.close ::Zip.case_insensitive_match = true zf_read = ::Zip::File.new(EMPTY_FILENAME) assert_equal(SRC_FILES.collect { |_fn, en| en.downcase }.uniq.size, zf_read.entries.length) assert_equal(SRC_FILES.last.last.downcase, zf_read.entries.first.name.downcase) AssertEntry.assert_contents( SRC_FILES.last.first, zf_read.get_input_stream(SRC_FILES.last.last, &:read) ) end private def assert_contains(zip_file, entry_name, filename = entry_name) refute_nil( zip_file.entries.detect { |e| e.name == entry_name }, "entry #{entry_name} not in #{zip_file.entries.join(', ')} in zip file #{zip_file}" ) assert_entry_contents(zip_file, entry_name, filename) if File.exist?(filename) end end rubyzip-2.3.0/test/central_directory_entry_test.rb000066400000000000000000000052351363314356200225330ustar00rootroot00000000000000require 'test_helper' class ZipCentralDirectoryEntryTest < MiniTest::Test def test_read_from_stream File.open('test/data/testDirectory.bin', 'rb') do |file| entry = ::Zip::Entry.read_c_dir_entry(file) assert_equal('longAscii.txt', entry.name) assert_equal(::Zip::Entry::DEFLATED, entry.compression_method) assert_equal(106_490, entry.size) assert_equal(3784, entry.compressed_size) assert_equal(0xfcd1799c, entry.crc) assert_equal('', entry.comment) entry = ::Zip::Entry.read_c_dir_entry(file) assert_equal('empty.txt', entry.name) assert_equal(::Zip::Entry::STORED, entry.compression_method) assert_equal(0, entry.size) assert_equal(0, entry.compressed_size) assert_equal(0x0, entry.crc) assert_equal('', entry.comment) entry = ::Zip::Entry.read_c_dir_entry(file) assert_equal('short.txt', entry.name) assert_equal(::Zip::Entry::STORED, entry.compression_method) assert_equal(6, entry.size) assert_equal(6, entry.compressed_size) assert_equal(0xbb76fe69, entry.crc) assert_equal('', entry.comment) entry = ::Zip::Entry.read_c_dir_entry(file) assert_equal('longBinary.bin', entry.name) assert_equal(::Zip::Entry::DEFLATED, entry.compression_method) assert_equal(1_000_024, entry.size) assert_equal(70_847, entry.compressed_size) assert_equal(0x10da7d59, entry.crc) assert_equal('', entry.comment) entry = ::Zip::Entry.read_c_dir_entry(file) assert_nil(entry) # Fields that are not check by this test: # version made by 2 bytes # version needed to extract 2 bytes # general purpose bit flag 2 bytes # last mod file time 2 bytes # last mod file date 2 bytes # compressed size 4 bytes # uncompressed size 4 bytes # disk number start 2 bytes # internal file attributes 2 bytes # external file attributes 4 bytes # relative offset of local header 4 bytes # file name (variable size) # extra field (variable size) # file comment (variable size) end end def test_read_entry_from_truncated_zip_file fragment = '' File.open('test/data/testDirectory.bin') { |f| fragment = f.read(12) } # cdir entry header is at least 46 bytes fragment.extend(IOizeString) entry = ::Zip::Entry.new entry.read_c_dir_entry(fragment) raise 'ZipError expected' rescue ::Zip::Error end end rubyzip-2.3.0/test/central_directory_test.rb000066400000000000000000000113651363314356200213130ustar00rootroot00000000000000require 'test_helper' class ZipCentralDirectoryTest < MiniTest::Test def teardown ::Zip.reset! end def test_read_from_stream ::File.open(TestZipFile::TEST_ZIP2.zip_name, 'rb') do |zip_file| cdir = ::Zip::CentralDirectory.read_from_stream(zip_file) assert_equal(TestZipFile::TEST_ZIP2.entry_names.size, cdir.size) assert(cdir.entries.sort.compare_enumerables(TestZipFile::TEST_ZIP2.entry_names.sort) do |cdir_entry, test_entry_name| cdir_entry.name == test_entry_name end) assert_equal(TestZipFile::TEST_ZIP2.comment, cdir.comment) end end def test_read_from_invalid_stream File.open('test/data/file2.txt', 'rb') do |zip_file| cdir = ::Zip::CentralDirectory.new cdir.read_from_stream(zip_file) end raise 'ZipError expected!' rescue ::Zip::Error end def test_read_from_truncated_zip_file fragment = '' File.open('test/data/testDirectory.bin', 'rb') { |f| fragment = f.read } fragment.slice!(12) # removed part of first cdir entry. eocd structure still complete fragment.extend(IOizeString) entry = ::Zip::CentralDirectory.new entry.read_from_stream(fragment) raise 'ZipError expected' rescue ::Zip::Error end def test_write_to_stream entries = [::Zip::Entry.new('file.zip', 'flimse', 'myComment', 'somethingExtra'), ::Zip::Entry.new('file.zip', 'secondEntryName'), ::Zip::Entry.new('file.zip', 'lastEntry.txt', 'Has a comment too')] cdir = ::Zip::CentralDirectory.new(entries, 'my zip comment') File.open('test/data/generated/cdirtest.bin', 'wb') do |f| cdir.write_to_stream(f) end cdir_readback = ::Zip::CentralDirectory.new File.open('test/data/generated/cdirtest.bin', 'rb') do |f| cdir_readback.read_from_stream(f) end assert_equal(cdir.entries.sort, cdir_readback.entries.sort) end def test_write64_to_stream ::Zip.write_zip64_support = true entries = [::Zip::Entry.new('file.zip', 'file1-little', 'comment1', '', 200, 101, ::Zip::Entry::STORED, 200), ::Zip::Entry.new('file.zip', 'file2-big', 'comment2', '', 18_000_000_000, 102, ::Zip::Entry::DEFLATED, 20_000_000_000), ::Zip::Entry.new('file.zip', 'file3-alsobig', 'comment3', '', 15_000_000_000, 103, ::Zip::Entry::DEFLATED, 21_000_000_000), ::Zip::Entry.new('file.zip', 'file4-little', 'comment4', '', 100, 104, ::Zip::Entry::DEFLATED, 121)] [0, 250, 18_000_000_300, 33_000_000_350].each_with_index do |offset, index| entries[index].local_header_offset = offset end cdir = ::Zip::CentralDirectory.new(entries, 'zip comment') File.open('test/data/generated/cdir64test.bin', 'wb') do |f| cdir.write_to_stream(f) end cdir_readback = ::Zip::CentralDirectory.new File.open('test/data/generated/cdir64test.bin', 'rb') do |f| cdir_readback.read_from_stream(f) end assert_equal(cdir.entries.sort, cdir_readback.entries.sort) assert_equal(::Zip::VERSION_NEEDED_TO_EXTRACT_ZIP64, cdir_readback.instance_variable_get(:@version_needed_for_extract)) end def test_equality cdir1 = ::Zip::CentralDirectory.new([::Zip::Entry.new('file.zip', 'flimse', nil, 'somethingExtra'), ::Zip::Entry.new('file.zip', 'secondEntryName'), ::Zip::Entry.new('file.zip', 'lastEntry.txt')], 'my zip comment') cdir2 = ::Zip::CentralDirectory.new([::Zip::Entry.new('file.zip', 'flimse', nil, 'somethingExtra'), ::Zip::Entry.new('file.zip', 'secondEntryName'), ::Zip::Entry.new('file.zip', 'lastEntry.txt')], 'my zip comment') cdir3 = ::Zip::CentralDirectory.new([::Zip::Entry.new('file.zip', 'flimse', nil, 'somethingExtra'), ::Zip::Entry.new('file.zip', 'secondEntryName'), ::Zip::Entry.new('file.zip', 'lastEntry.txt')], 'comment?') cdir4 = ::Zip::CentralDirectory.new([::Zip::Entry.new('file.zip', 'flimse', nil, 'somethingExtra'), ::Zip::Entry.new('file.zip', 'lastEntry.txt')], 'comment?') assert_equal(cdir1, cdir1) assert_equal(cdir1, cdir2) assert(cdir1 != cdir3) assert(cdir2 != cdir3) assert(cdir2 != cdir3) assert(cdir3 != cdir4) assert(cdir3 != 'hello') end end rubyzip-2.3.0/test/constants_test.rb000066400000000000000000000046151363314356200176130ustar00rootroot00000000000000require 'test_helper' class ConstantsTest < MiniTest::Test def test_compression_methods assert_equal(0, Zip::COMPRESSION_METHOD_STORE) assert_equal(1, Zip::COMPRESSION_METHOD_SHRINK) assert_equal(2, Zip::COMPRESSION_METHOD_REDUCE_1) assert_equal(3, Zip::COMPRESSION_METHOD_REDUCE_2) assert_equal(4, Zip::COMPRESSION_METHOD_REDUCE_3) assert_equal(5, Zip::COMPRESSION_METHOD_REDUCE_4) assert_equal(6, Zip::COMPRESSION_METHOD_IMPLODE) assert_equal(8, Zip::COMPRESSION_METHOD_DEFLATE) assert_equal(9, Zip::COMPRESSION_METHOD_DEFLATE_64) assert_equal(10, Zip::COMPRESSION_METHOD_PKWARE_DCLI) assert_equal(12, Zip::COMPRESSION_METHOD_BZIP2) assert_equal(14, Zip::COMPRESSION_METHOD_LZMA) assert_equal(16, Zip::COMPRESSION_METHOD_IBM_CMPSC) assert_equal(18, Zip::COMPRESSION_METHOD_IBM_TERSE) assert_equal(19, Zip::COMPRESSION_METHOD_IBM_LZ77) assert_equal(96, Zip::COMPRESSION_METHOD_JPEG) assert_equal(97, Zip::COMPRESSION_METHOD_WAVPACK) assert_equal(98, Zip::COMPRESSION_METHOD_PPMD) assert_equal(99, Zip::COMPRESSION_METHOD_AES) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_STORE]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_SHRINK]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_REDUCE_1]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_REDUCE_2]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_REDUCE_3]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_REDUCE_4]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_IMPLODE]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_DEFLATE]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_DEFLATE_64]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_PKWARE_DCLI]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_BZIP2]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_LZMA]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_IBM_CMPSC]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_IBM_TERSE]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_IBM_LZ77]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_JPEG]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_WAVPACK]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_PPMD]) assert(Zip::COMPRESSION_METHODS[Zip::COMPRESSION_METHOD_AES]) end end rubyzip-2.3.0/test/crypto/000077500000000000000000000000001363314356200155255ustar00rootroot00000000000000rubyzip-2.3.0/test/crypto/null_encryption_test.rb000066400000000000000000000020551363314356200223370ustar00rootroot00000000000000require 'test_helper' class NullEncrypterTest < MiniTest::Test def setup @encrypter = ::Zip::NullEncrypter.new end def test_header_bytesize assert_equal 0, @encrypter.header_bytesize end def test_gp_flags assert_equal 0, @encrypter.gp_flags end def test_header assert_empty @encrypter.header(nil) end def test_encrypt assert_nil @encrypter.encrypt(nil) ['', 'a' * 10, 0xffffffff].each do |data| assert_equal data, @encrypter.encrypt(data) end end def test_reset! assert_respond_to @encrypter, :reset! end end class NullDecrypterTest < MiniTest::Test def setup @decrypter = ::Zip::NullDecrypter.new end def test_header_bytesize assert_equal 0, @decrypter.header_bytesize end def test_gp_flags assert_equal 0, @decrypter.gp_flags end def test_decrypt assert_nil @decrypter.decrypt(nil) ['', 'a' * 10, 0xffffffff].each do |data| assert_equal data, @decrypter.decrypt(data) end end def test_reset! assert_respond_to @decrypter, :reset! end end rubyzip-2.3.0/test/crypto/traditional_encryption_test.rb000066400000000000000000000046441363314356200237050ustar00rootroot00000000000000require 'test_helper' class TraditionalEncrypterTest < MiniTest::Test def setup @mtime = ::Zip::DOSTime.new(2014, 12, 17, 15, 56, 24) @encrypter = ::Zip::TraditionalEncrypter.new('password') end def test_header_bytesize assert_equal 12, @encrypter.header_bytesize end def test_gp_flags assert_equal 9, @encrypter.gp_flags end def test_header @encrypter.reset! exepected = [239, 57, 234, 154, 246, 80, 83, 221, 74, 200, 121, 91].pack('C*') Random.stub(:rand, 1) do assert_equal exepected, @encrypter.header(@mtime) end end def test_encrypt @encrypter.reset! Random.stub(:rand, 1) { @encrypter.header(@mtime) } assert_raises(NoMethodError) { @encrypter.encrypt(nil) } assert_raises(NoMethodError) { @encrypter.encrypt(1) } assert_equal '', @encrypter.encrypt('') assert_equal [100, 218, 7, 114, 226, 82, 62, 93, 224, 62].pack('C*'), @encrypter.encrypt('a' * 10) end def test_reset! @encrypter.reset! Random.stub(:rand, 1) { @encrypter.header(@mtime) } [100, 218, 7, 114, 226, 82, 62, 93, 224, 62].map(&:chr).each do |c| assert_equal c, @encrypter.encrypt('a') end assert_equal 56.chr, @encrypter.encrypt('a') @encrypter.reset! Random.stub(:rand, 1) { @encrypter.header(@mtime) } [100, 218, 7, 114, 226, 82, 62, 93, 224, 62].map(&:chr).each do |c| assert_equal c, @encrypter.encrypt('a') end end end class TraditionalDecrypterTest < MiniTest::Test def setup @decrypter = ::Zip::TraditionalDecrypter.new('password') end def test_header_bytesize assert_equal 12, @decrypter.header_bytesize end def test_gp_flags assert_equal 9, @decrypter.gp_flags end def test_decrypt @decrypter.reset!([239, 57, 234, 154, 246, 80, 83, 221, 74, 200, 121, 91].pack('C*')) [100, 218, 7, 114, 226, 82, 62, 93, 224, 62].map(&:chr).each do |c| assert_equal 'a', @decrypter.decrypt(c) end end def test_reset! @decrypter.reset!([239, 57, 234, 154, 246, 80, 83, 221, 74, 200, 121, 91].pack('C*')) [100, 218, 7, 114, 226, 82, 62, 93, 224, 62].map(&:chr).each do |c| assert_equal 'a', @decrypter.decrypt(c) end assert_equal 91.chr, @decrypter.decrypt(2.chr) @decrypter.reset!([239, 57, 234, 154, 246, 80, 83, 221, 74, 200, 121, 91].pack('C*')) [100, 218, 7, 114, 226, 82, 62, 93, 224, 62].map(&:chr).each do |c| assert_equal 'a', @decrypter.decrypt(c) end end end rubyzip-2.3.0/test/data/000077500000000000000000000000001363314356200151165ustar00rootroot00000000000000rubyzip-2.3.0/test/data/WarnInvalidDate.zip000066400000000000000000000710241363314356200206620ustar00rootroot00000000000000PKꕡEOz[Content_Types].xml͗r0_ţ[VRJ)L^pš3) #HJ=kMM 5&YNW;)-XǵJ<Tsʔ|_yr&jpK{RA2h 3y mI Y l6V}A(FkJ]R)҇u*a]S'EhiYHŌ15gjxw`N?<#@"LbPou9EwS&IGq@»zUs<'@@4e]PKꕡE3docProps/app.xmlSMo1+OTUȻQEqh ֪׶lځ,$1ofs|P֔t2.(X̮ %! #Jz@o*ց & M c|61ݪnmٴ(>3x`$ȑ;%9|7M_xUQZuPd },9ыjeas6TbwԎB ٴ74__q;/\h%!T^ncdJJ0/3ZPM9;BiZ_ _ʣj{h$?%?D޼WDe$]q1hЉ'8 dOaΕ&#KӘ %V[\G|ǏЏtL ^L0yߔ\moELzVxxi'—ؙ׽v M?W鲏``PKꕡE]}docProps/core.xmluMK0JɽM R.x䂠a2 hw"UA;NU0iUEa}}*\ 43MceU1(z䁛Bb e̳No0jI՗L٭y ºk: e~X<ݨeYzF~?<6\nGR`L 1O9*KgK\~W6}PKꕡE?cU ppt/presentation.xmlՖێ0_dNQ^Ti+E0$HF<}m`cUyf /]C+Ys#C5;m"G*JB9@ݶݴ$0E:ڄ Yv㺲8CC3oZEC- 3z^Uuxqi!u+?ϸM)-IrLI}7`KZ R^J8u N#vg{Ƹăwv9_FѤmo*:ޝQcyzP[" ԭ! ^a4xh|P UoЩQmb\4+wJ =Ȋy׏sӺהJs%4Go /"/ {j5 ba%0%A(@(])Ph MVJ-Q4! ?^)Qd QhD%J,QE ^)Qb WJZlB0X)Q0PKꕡES|?/ppt/presProps.xml 0E%doS]nĵ N@2 3s8ނ v\V6G-Ob#gA;vmjf x Q4ȍr95J b,!R04̵W^`7Os8 .s(/ li{ORתPKꕡEоRppt/tableStyles.xmlKo0 ͣjZJ=t@*Vu F&%-`4,Ҥ' UdQؓ=xIi0oK68˙HK'O{T|4 ~~> ofSQ 2מ RU]Q"KA86ņu\ Ld`Yld8Y!Λ[\R0K@; L˴ "٪E7O'ߣ K ! Hh&L IøP7zx)ߌӄ݇c) ^z4JҌ?4Τg?oX>/ޭFC;8H35P´թg0e,֩h!J(oT6Xd"ur.*fx)^ u8m.ŧ0wm|C:X]猦[4i"7qJ6GYǜxtz3i&l~-š4OZ(U_ {} {7x NK W"I}^ %Uy):ǚGmS!Uqtb])F\t.\gLY=z}KuaR_~\7g[B'V媶, t91B W%D7#w"] jܷM>wrΓ;:W L{jvr rhZu*fNtLm2@{cy&ȽU~eR#*-@(6, &6=ܱ wAܫ5>r6ɽ>;O^53A"*n¾ PQ̿Bk>5AWk4Z}s'}rH=Q4>PMԷl8n Cř21yߦ\l@᠞wKVw"IWW Zi'V4Iþb6NGe r/ګ_, }s ]@'yǓ89}}-}(Mi}I(JQG_WN)N-fOw’h+zĉ6뿬 4OE,Dg_O}z-St-/׫h~ܾPKꕡE7.ppt/notesSlides/notesSlide1.xmlSN0wAy\"`m?6%{֏@Ejfgg;wx6 Mg65}=:i[5߁ W3b_ɚ!JZϬCڢ>q#{q2 -; SpmՇB/uQ<$loۻHn='L Yא_R/2;ůf e5Q7 ˆŕ8)!SSHvNď y= X[V \"d㳇܈Qjb㮦ȺlP4BIcoQ&S(+!uX ͷ_ey .m2의NA6)) ˔rtKD.JDq_PKꕡE!ppt/slideLayouts/slideLayout1.xmlXێ6A}]F/dwzH[B$Q%)"@~|I)R: uXc ǔ߽?䙱')bb71?>-Gip 2Zy$| @GZ CF(MDrlYF%r5fMVΰQ`OȈRLOZ*r]xدb=(޵=_%TPQ/ ] i!hl)0Tڣa)I7* j9#ֺSZ'<ժV]VJ X5]zd/ҥ}ﴕ2i BF+FR.s`B@0d*4D }^f "N%]|ǵ# t݁8&A"ӁvžCz(NQP͐P%Pl(AP$3 JءD=uQռo0vZ/ *C1IheS SlNn0~d}m-Zl>҃#GGSg64;EsbɰHsLw#0ux ӕZ"b/)_nwro)SBLµ%-K>p%%~1|sVoKS%ŒǒN!Bt 8rDySk Ƀ>|_/q ﱪnG^?WK|iigR󣯶X?u{UjυPKꕡE2D|O"ppt/slideLayouts/slideLayout10.xmlWn8]+,cu ?Lvg$*Jr[3/%)ʼnG/KJTWS*iQNM0RT)aS 9-&=5 )εWINKθ2.KU$wPd~/~I 7gy ].i]Iр^Pm53Մ ǐ $)aHqt(M*UE9@ 0R}%,0 Yɚ6f=HC6LԌRJJ"*7&]Wkn=fG3ݺ!)LCj!Lv,@;[rߔ6EƙdZs_6Nؤ+HsDB#Fv0 v:Z+"I[U+[VԪ#Њ朥׹T,`b50;c\VBYJ̊KEFl6[K> lM"[+ީ 6y!;!y7 '(S1m/>Srem>iXxzb›E7/x|n')0m-u:Aj=hŎ97C˝i&k%?%XaĚ)EWuysǠs=<+Iw Q4 FC/-Co_^82}*CxF+@!uVprwJIu6ƊsxnF^[&+4˭8Dc~vc{Jʄ +HqDR!)JBG6D.mc(pFv;vhZtȺ6,C7~C?6]gxgH)Na഼?wpYg%cFtZs*ke% j ] bdxh{ACaG?zq;a=lir0n'AW;q;l׋;q'=nߋUNznji{; U<|DD\vsɔ(h. >1%,[tw{0t>t7 ΂A%q| .UYYyzlzA_jŎb8][^(aHוh3,+wCeuW,tΣ.x~c<4nL\ze WtQS5MQ@2/n \n~3 m?PKꕡE]h!ppt/slideLayouts/slideLayout4.xmlXn6~Bj6D$>#RPHʱ7km'EJk[)D>w-dtW0*f_RA aO=λ|, Y@NB)><qL,gJ?|Ԟ+ⅾx,ڋogy^fxTY'4zY\vxKX Idz+fM@`P*,ŔIS{B/ɨ$+[q~d`ΨҁH폂RRR{h=ke3@֔K$-C: pEuθܷ\#;2+B=K3k7Zy3'6]vy <17psky4l ;>FvCF]:Dly0%s}͊xO_g+jk, p(VI;SMxVI8LvQ2 ᳥ 4vGa l}_-q41D~F9\ l3F x?^ b]' ѥRV U"D_ZGW#zX눓p;^w ޱ 8ϋH9gx~Kfy0Lnn 4M$I4o!3 V*@d')cM9{;ݹ_j(tN&=3cbEJ]]=·$HyIh4 7;$0-8 P6 :>ϟÿ 0q @~nxI7U=qgyg_^hCvPKꕡE KȮO!ppt/slideLayouts/slideLayout5.xmlYr6~ v@Hy6I7Ӥ Dpvvf_}} :Y'^;G:K2aO, NKiFi? I drP|%P+R ydMl HIʭֈ5Xm\ <)YVSZկ-\)\$r,4kr3T@,+MsӽR &xY}2iLzZ-z1ovhY|S[ͫ| Qå7=Nv]"R pi71lŸ܊ڮbRNRle&#E rvc=ch]j  =yq.,6bdVlR2.ԏF7lO38IhO PJ6.jaRy4'n v'f[zyJB4!؃E!Q=duت:Z~@#ܔ W y3 JU 3?)xp&6iQp]e|L#ggJ{QBGhfpl ]8%}/߾33+RܬTșzOTDqP3џ5"}kZPl? PKꕡEcP<C !ppt/slideLayouts/slideLayout6.xmlVr06y M~d,LɗZt]dcst5j3JgzX׻6CoGwuy!x' Kَٕ`އ-0mfǨ~@ڏ3"4Rym+T2dr²( r3BC`(' pW' jVfk E c Fى#z;<%d+$:'~ՙxwۣuOŸDat EF5a:P -;uv ]My)?3%EE:}v>`P" cML&r&;m@5%nuI&( ]zPKꕡE$hT!ppt/slideLayouts/slideLayout7.xmlVMr0](xB24)%cOmIdt&jY!!4 6٠Oծ,-*lu(K8f~(gtr./D yTnRIFK:\PkD$e?@%Ι˷4:IURmI {B[ I1[$Foq 3[#7Xi*ل^u]`u*pè5n V*Lq1;Ii-')Vb)v)Z6Qch<dk̸榗'}L$:Js.e}Τ76ѝvkKF$[i|j6-Z/q8ͱβ xAtpD?"Y0p~EBu" Y4/B)q^"6ݔ}N}qT(GX'B/M1p#k~Ng`]ϼ^؟yp:f@X%M%K?Aa]’Mad%n˟| 3u;-ji7aKv9.&JA,ڞwŴLd6C^Nɥ QLu c45?'i$j<gu9 7ͅ"4%/(b[i 4%0 cpd5$-GR ʗud&RW+EJ!P.f,)zNDf66e)Fg^<K.w%GI$V` Em+te_.c&tCAs;;=!Z=dlC6=9,MOH0DD uT&آ2VYh- ' ug!%iЛ\2< j$]kS?27w|7w R$6d[A&oWØ*|.19ԏjbl7wӳf̝0{vd4>fdІٳ-LS}[Uu:'U5j*6*Si$f6#Ml˙Mk_{{kܼ@adt׏ ȑH_f@$eBrVscC+>ٰDPVcrnRiVqJ R1NKX[0Q œ EnږJ"˂R WiƫFyL L%ܳX){!b^Yqnira1'W-x Z3jdyJ6b=g2EʦzT~υr]ZC,4kj2Tjl,Z*Os'ғ@ xE}ZAn(5W/ѵD^En{ռm.}y[2S_ӥ}ic6U5RRMd\-<%z}; ]r\;mGnPu'OZD+ljGB&SQ;z=TW9z:V6/=[S%W?}]fQ4sBɋI_d&Dz!UP&QLDz/WTm&ٸ4eyY؃bv?Q9a8n)z2]ַ N[8z@5;f'#!Nvزhho?A{=h'{A wAm[^:4:;^dŬŕ$ ZN+xjZ덡MIhG7\8m??(Ciq5[VdbOy-=<@@I  * ZS=RӪתȗϨચWD-sqo5y\M&8yxdEדxѥsu9(X/ 0,β'8, `3l ;]! Ş1VwY^Hےז|Y+ϳeSzTyqdGeZy44Y xWMgª&_{gvLN-7߭TʅRYdj΄Jz}ӟPl^PKꕡE6྘.!ppt/slideMasters/slideMaster1.xmlZkr6 QDrf&M"! D t&g-$]Hɲ#%C ,v>_\Θʊo:lӠy\$Y>훿^ {iT aENˣ%oI%(7`:"}3<*NTϊ06)p(4Ai\eGCM2aL iDpɈp,- Ychw{NiKN# gq]=`o*-Cs9<1]PpE=ðsG |g%]u-g&gZQsV" –I܇]8Jsk{nrW@ڮB{i2¦ #u6 %Ì ']qr)gukV=uN80tzI/(lFtO3yjC $CyM Bn὇BpWCF[CbNO>s0M0x7B{HT,9_!&*~JIJ!!1^p?憒uv'<#jL1|7㒺VWʷJ5ρ!poxp]SׂwFCIG$j/xIe`WƂ8CL݁ OSulZ}bNziGS t&:^n-nց6s%/i}W/U%]bCӽm*۝H>~wvk+c?#HeMibgY}%M0xh=Fj:e(ըdg'rHy14NoDG'+ ԹW/XMZ>jn}UuyŌ+_zkCWְJF}КCQ3ʆgVs^־$r9poĘKȋ·m;O/© ZTzZ\f^͔z^wܾO[1]`g˓k8;鈮 ߗ,(<0|^oPKꕡEl`;ppt/theme/theme1.xmlXKs0+1dB2NgJ:= [~$DB2X H!JHv]"wo2^!yscQ8f_A1@%Cs pnY hp4`hfUE#. kII VL[-v*@OIGpRFb&iWtk:ZE fzf7i"g*(%lJD#vyB2Ri")P ;h1LFB<*vx.A}A({AǾ>Q}`ӱ_5h[BA":fN'|?%nKJkfR][>1e3- c?uc3ců/"c qYS(o͈;uW ?:> 77?)rf-LI3%76╱@KX|YeqNt )e Tpw =xP)D[cfA/CJ?-ws,쵬˖upYeq/8V!7k8MF0y:Sm9&2Ň(M%bCHPKꕡEtH2ppt/theme/theme2.xmlYMo6 to/evHlڴAf葖h5% $Է=0vö-Kku:a(٦b:q +:@,RƵ1CGDHʓSZqI|$l;w;W 'f1skʚc8(13R~ٜ21(mLZbNQ}I:L#NEwsÝZO+vZo׬d3~}_ݫ嗋iJxl P~8F_z V+|[6Jx MFmZ3ȐVx̔\#grD2(9G1tp R ft h&lɷ|B4UmC!SNz_,7p/>C?xiT&7O|ŷ_c |K ӘHt}md .Fя05)Pg4tOE% f؂&e >_R cE-Q\rζt3ezavbl1>ooB:SNDJj19IB!{K}%*tmL.Ӂݠ1ebS]!ƾKH l, +:+[513lJL_rT0zFsGLJބa.ePtdCœ.u"Vi؏R=JrdkNuھKȞ ٝ(Zw 4yߑW[ZKt^;};\o]m=-DB.QNoMߡ$sk^#`FiQoVG-ӡ%!ՒNJ(8F6nP όt!BE+˛#.zRӈ}3P]kRDSwה,salPc(ѶÅ8t4AEbLWr4[9Ʌڧ!:!{f՚|2*L]rDX?޵~EnR8BN͵U y'ƒ`.qYa4}Qz3.-y+?jS8|4n*|6o|f%D,o9qv$ޗ9|ή/q^ٞٮvK52z]8(pL;>t_PKꕡEkppt/_rels/presentation.xml.relsAN0Eb͞8-P B A9I&c[S( IT.ۯVzRXd904-.nQ!v~F-CF9bÐ&w9 2ēVX;Y2W=`v+smUmѢ 'Uh+}A@u,Z q~ʣ8q@Hi!b`/ =eaef'g9V:5Y_G 1 ,rX0B>S_q)8H2FcJX#hepm yYUE&:U4a$|i Ac@YχPKꕡE*ppt/notesSlides/_rels/notesSlide1.xml.relsj1 _h}!'K(dRN6R C%}惪zہ!9&="G3+ ~J3jۑ)1 aR-OIhAΐ:Grۺzˀ5Ӝz0k0@. 9dNHwߣOJTW67j̭޾PKꕡEk5,ppt/slideLayouts/_rels/slideLayout1.xml.relsϽ 0W "E8klbތ-88547%vke ^~ApFoq 4pl .(g C&d"2B02*ybOj[U;Mq su)D>P<:KWLz\WK,FmPKꕡEk5-ppt/slideLayouts/_rels/slideLayout10.xml.relsϽ 0W "E8klbތ-88547%vke ^~ApFoq 4pl .(g C&d"2B02*ybOj[U;Mq su)D>P<:KWLz\WK,FmPKꕡEk5-ppt/slideLayouts/_rels/slideLayout11.xml.relsϽ 0W "E8klbތ-88547%vke ^~ApFoq 4pl .(g C&d"2B02*ybOj[U;Mq su)D>P<:KWLz\WK,FmPKꕡEk5,ppt/slideLayouts/_rels/slideLayout2.xml.relsϽ 0W "E8klbތ-88547%vke ^~ApFoq 4pl .(g C&d"2B02*ybOj[U;Mq su)D>P<:KWLz\WK,FmPKꕡEk5,ppt/slideLayouts/_rels/slideLayout3.xml.relsϽ 0W "E8klbތ-88547%vke ^~ApFoq 4pl .(g C&d"2B02*ybOj[U;Mq su)D>P<:KWLz\WK,FmPKꕡEk5,ppt/slideLayouts/_rels/slideLayout4.xml.relsϽ 0W "E8klbތ-88547%vke ^~ApFoq 4pl .(g C&d"2B02*ybOj[U;Mq su)D>P<:KWLz\WK,FmPKꕡEk5,ppt/slideLayouts/_rels/slideLayout5.xml.relsϽ 0W "E8klbތ-88547%vke ^~ApFoq 4pl .(g C&d"2B02*ybOj[U;Mq su)D>P<:KWLz\WK,FmPKꕡEk5,ppt/slideLayouts/_rels/slideLayout6.xml.relsϽ 0W "E8klbތ-88547%vke ^~ApFoq 4pl .(g C&d"2B02*ybOj[U;Mq su)D>P<:KWLz\WK,FmPKꕡEk5,ppt/slideLayouts/_rels/slideLayout7.xml.relsϽ 0W "E8klbތ-88547%vke ^~ApFoq 4pl .(g C&d"2B02*ybOj[U;Mq su)D>P<:KWLz\WK,FmPKꕡEk5,ppt/slideLayouts/_rels/slideLayout8.xml.relsϽ 0W "E8klbތ-88547%vke ^~ApFoq 4pl .(g C&d"2B02*ybOj[U;Mq su)D>P<:KWLz\WK,FmPKꕡEk5,ppt/slideLayouts/_rels/slideLayout9.xml.relsϽ 0W "E8klbތ-88547%vke ^~ApFoq 4pl .(g C&d"2B02*ybOj[U;Mq su)D>P<:KWLz\WK,FmPKꕡEY,ppt/slideMasters/_rels/slideMaster1.xml.relsKj0ૈײqJlB UI ڒR߾C]f#cVs:?-r`i xj`>Hh0a ,fh/>sUMk$Cܺ[e XfK#]ulӶ“Q uxЂqhEbtYvxTdDzRVd+J*%[S)نRI*JYm)e۔)َRKɊ͓61ſkq>Y|1~_PKꕡEK% ppt/slides/_rels/slide1.xml.relsAk0 }v(n/cPik4׽l ÎzzӨ(Kl (b}%,$p>hROdITeXJI/ƈhB1M󄥎7 {2y6yɀ5S|-Ӝ/ukt׉<02O8kX= Z/5flƱ|ޓW~姖Y}aPKꕡE{ppt/theme/_rels/theme1.xml.relsUA! ᫐Ѕ10zf)._-5I'x)k~N#  ^)c{=k-K e*xGi1['cCPKꕡEOz[Content_Types].xmlPKꕡE3!docProps/app.xmlPKꕡE]}docProps/core.xmlPKꕡE?cU "ppt/presentation.xmlPKꕡES|?/Rppt/presProps.xmlPKꕡEоR%ppt/tableStyles.xmlPKꕡEG+|? ppt/viewProps.xmlPKꕡEhO  _rels/.relsPKꕡEe`)9%!ppt/notesMasters/notesMaster1.xmlPKꕡE7.ppt/notesSlides/notesSlide1.xmlPKꕡE!ppt/slideLayouts/slideLayout1.xmlPKꕡE2D|O"ppt/slideLayouts/slideLayout10.xmlPKꕡEe4F3"ppt/slideLayouts/slideLayout11.xmlPKꕡEV!c! ppt/slideLayouts/slideLayout2.xmlPKꕡE9!j$ppt/slideLayouts/slideLayout3.xmlPKꕡE]h!(ppt/slideLayouts/slideLayout4.xmlPKꕡE KȮO!,ppt/slideLayouts/slideLayout5.xmlPKꕡEcP<C !1ppt/slideLayouts/slideLayout6.xmlPKꕡE$hT!4ppt/slideLayouts/slideLayout7.xmlPKꕡEZ! 8ppt/slideLayouts/slideLayout8.xmlPKꕡE~1s!<ppt/slideLayouts/slideLayout9.xmlPKꕡE6྘.!Appt/slideMasters/slideMaster1.xmlPKꕡEZOHppt/slides/slide1.xmlPKꕡEl`;_Jppt/theme/theme1.xmlPKꕡEtH2Nppt/theme/theme2.xmlPKꕡEkSppt/_rels/presentation.xml.relsPKꕡEuֳ.,0Uppt/notesMasters/_rels/notesMaster1.xml.relsPKꕡE*-Vppt/notesSlides/_rels/notesSlide1.xml.relsPKꕡEk5,a}b73Z|/TRAILINGSTUFF rubyzip-2.3.0/test/data/file1.txt.deflatedData000066400000000000000000000007421363314356200212230ustar00rootroot00000000000000Tn0}+56IƚBMi1Xulu =6@]?F?VzOlVm8Z0m?!@*.ɓ{ DtH\tF&ۧ 3T9CJA1'y~N }7eɎavT5* xqĜAɎ7eJuU^\0&Ptzuj\#DJow; 4?# t'S:=롗zQ*F!+^'nw/}Oy~J {<$s[lʳ#2241\Tuͨ=tV $!Q4.|# ƨyeQtJ.h R`j& &{8I&YF0ě:Ël0:@J?Ga1Y>a}b73Z|/TRAILINGSTUFF rubyzip-2.3.0/test/data/file2.txt000066400000000000000000001204221363314356200166610ustar00rootroot00000000000000#!/usr/bin/env ruby $VERBOSE = true require 'rubyunit' require 'zip' include Zip Dir.chdir "test" class AbstractInputStreamTest < RUNIT::TestCase # AbstractInputStream subclass that provides a read method TEST_LINES = [ "Hello world#{$/}", "this is the second line#{$/}", "this is the last line"] TEST_STRING = TEST_LINES.join class TestAbstractInputStream include AbstractInputStream def initialize(aString) @contents = aString @readPointer = 0 end def read(charsToRead) retVal=@contents[@readPointer, charsToRead] @readPointer+=charsToRead return retVal end def produceInput read(100) end def inputFinished? @contents[@readPointer] == nil end end def setup @io = TestAbstractInputStream.new(TEST_STRING) end def test_gets assert_equals(TEST_LINES[0], @io.gets) assert_equals(TEST_LINES[1], @io.gets) assert_equals(TEST_LINES[2], @io.gets) assert_equals(nil, @io.gets) end def test_getsMultiCharSeperator assert_equals("Hell", @io.gets("ll")) assert_equals("o world#{$/}this is the second l", @io.gets("d l")) end def test_each_line lineNumber=0 @io.each_line { |line| assert_equals(TEST_LINES[lineNumber], line) lineNumber+=1 } end def test_readlines assert_equals(TEST_LINES, @io.readlines) end def test_readline test_gets begin @io.readline fail "EOFError expected" rescue EOFError end end end class ZipEntryTest < RUNIT::TestCase TEST_ZIPFILE = "someZipFile.zip" TEST_COMMENT = "a comment" TEST_COMPRESSED_SIZE = 1234 TEST_CRC = 325324 TEST_EXTRA = "Some data here" TEST_COMPRESSIONMETHOD = ZipEntry::DEFLATED TEST_NAME = "entry name" TEST_SIZE = 8432 TEST_ISDIRECTORY = false def test_constructorAndGetters entry = ZipEntry.new(TEST_ZIPFILE, TEST_NAME, TEST_COMMENT, TEST_EXTRA, TEST_COMPRESSED_SIZE, TEST_CRC, TEST_COMPRESSIONMETHOD, TEST_SIZE) assert_equals(TEST_COMMENT, entry.comment) assert_equals(TEST_COMPRESSED_SIZE, entry.compressedSize) assert_equals(TEST_CRC, entry.crc) assert_equals(TEST_EXTRA, entry.extra) assert_equals(TEST_COMPRESSIONMETHOD, entry.compressionMethod) assert_equals(TEST_NAME, entry.name) assert_equals(TEST_SIZE, entry.size) assert_equals(TEST_ISDIRECTORY, entry.isDirectory) end def test_equality entry1 = ZipEntry.new("file.zip", "name", "isNotCompared", "something extra", 123, 1234, ZipEntry::DEFLATED, 10000) entry2 = ZipEntry.new("file.zip", "name", "isNotComparedXXX", "something extra", 123, 1234, ZipEntry::DEFLATED, 10000) entry3 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", "something extra", 123, 1234, ZipEntry::DEFLATED, 10000) entry4 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", "something extraXX", 123, 1234, ZipEntry::DEFLATED, 10000) entry5 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", "something extraXX", 12, 1234, ZipEntry::DEFLATED, 10000) entry6 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", "something extraXX", 12, 123, ZipEntry::DEFLATED, 10000) entry7 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", "something extraXX", 12, 123, ZipEntry::STORED, 10000) entry8 = ZipEntry.new("file.zip", "name2", "isNotComparedXXX", "something extraXX", 12, 123, ZipEntry::STORED, 100000) assert_equals(entry1, entry1) assert_equals(entry1, entry2) assert(entry2 != entry3) assert(entry3 != entry4) assert(entry4 != entry5) assert(entry5 != entry6) assert(entry6 != entry7) assert(entry7 != entry8) assert(entry7 != "hello") assert(entry7 != 12) end end module IOizeString attr_reader :tell def read(count = nil) @tell ||= 0 count = size unless count retVal = slice(@tell, count) @tell += count return retVal end def seek(index, offset) @tell ||= 0 case offset when IO::SEEK_END newPos = size + index when IO::SEEK_SET newPos = index when IO::SEEK_CUR newPos = @tell + index else raise "Error in test method IOizeString::seek" end if (newPos < 0 || newPos >= size) raise Errno::EINVAL else @tell=newPos end end def reset @tell = 0 end end class ZipLocalEntryTest < RUNIT::TestCase def test_readLocalEntryHeaderOfFirstTestZipEntry File.open(TestZipFile::TEST_ZIP3.zipName) { |file| entry = ZipEntry.readLocalEntry(file) assert_equal("", entry.comment) # Differs from windows and unix because of CR LF # assert_equal(480, entry.compressedSize) # assert_equal(0x2a27930f, entry.crc) # extra field is 21 bytes long # probably contains some unix attrutes or something # disabled: assert_equal(nil, entry.extra) assert_equal(ZipEntry::DEFLATED, entry.compressionMethod) assert_equal(TestZipFile::TEST_ZIP3.entryNames[0], entry.name) assert_equal(File.size(TestZipFile::TEST_ZIP3.entryNames[0]), entry.size) assert(! entry.isDirectory) } end def test_readLocalEntryFromNonZipFile File.open("ziptest.rb") { |file| assert_equals(nil, ZipEntry.readLocalEntry(file)) } end def test_readLocalEntryFromTruncatedZipFile zipFragment="" File.open(TestZipFile::TEST_ZIP2.zipName) { |f| zipFragment = f.read(12) } # local header is at least 30 bytes zipFragment.extend(IOizeString).reset entry = ZipEntry.new entry.readLocalEntry(zipFragment) fail "ZipError expected" rescue ZipError end def test_writeEntry entry = ZipEntry.new("file.zip", "entryName", "my little comment", "thisIsSomeExtraInformation", 100, 987654, ZipEntry::DEFLATED, 400) writeToFile("localEntryHeader.bin", "centralEntryHeader.bin", entry) entryReadLocal, entryReadCentral = readFromFile("localEntryHeader.bin", "centralEntryHeader.bin") compareLocalEntryHeaders(entry, entryReadLocal) compareCDirEntryHeaders(entry, entryReadCentral) end private def compareLocalEntryHeaders(entry1, entry2) assert_equals(entry1.compressedSize , entry2.compressedSize) assert_equals(entry1.crc , entry2.crc) assert_equals(entry1.extra , entry2.extra) assert_equals(entry1.compressionMethod, entry2.compressionMethod) assert_equals(entry1.name , entry2.name) assert_equals(entry1.size , entry2.size) assert_equals(entry1.localHeaderOffset, entry2.localHeaderOffset) end def compareCDirEntryHeaders(entry1, entry2) compareLocalEntryHeaders(entry1, entry2) assert_equals(entry1.comment, entry2.comment) end def writeToFile(localFileName, centralFileName, entry) File.open(localFileName, "wb") { |f| entry.writeLocalEntry(f) } File.open(centralFileName, "wb") { |f| entry.writeCDirEntry(f) } end def readFromFile(localFileName, centralFileName) localEntry = nil cdirEntry = nil File.open(localFileName, "rb") { |f| localEntry = ZipEntry.readLocalEntry(f) } File.open(centralFileName, "rb") { |f| cdirEntry = ZipEntry.readCDirEntry(f) } return [localEntry, cdirEntry] end end module DecompressorTests # expects @refText and @decompressor def test_readEverything assert_equals(@refText, @decompressor.read) end def test_readInChunks chunkSize = 5 while (decompressedChunk = @decompressor.read(chunkSize)) assert_equals(@refText.slice!(0, chunkSize), decompressedChunk) end assert_equals(0, @refText.size) end end class InflaterTest < RUNIT::TestCase include DecompressorTests def setup @file = File.new("file1.txt.deflatedData", "rb") @refText="" File.open("file1.txt") { |f| @refText = f.read } @decompressor = Inflater.new(@file) end def teardown @file.close end end class PassThruDecompressorTest < RUNIT::TestCase include DecompressorTests TEST_FILE="file1.txt" def setup @file = File.new(TEST_FILE) @refText="" File.open(TEST_FILE) { |f| @refText = f.read } @decompressor = PassThruDecompressor.new(@file, File.size(TEST_FILE)) end def teardown @file.close end end module AssertEntry def assertNextEntry(filename, zis) assertEntry(filename, zis, zis.getNextEntry.name) end def assertEntry(filename, zis, entryName) assert_equals(filename, entryName) assertEntryContentsForStream(filename, zis, entryName) end def assertEntryContentsForStream(filename, zis, entryName) File.open(filename, "rb") { |file| expected = file.read actual = zis.read if (expected != actual) if (expected.length > 400 || actual.length > 400) zipEntryFilename=entryName+".zipEntry" File.open(zipEntryFilename, "wb") { |file| file << actual } fail("File '#{filename}' is different from '#{zipEntryFilename}'") else assert_equals(expected, actual) end end } end def AssertEntry.assertContents(filename, aString) fileContents = "" File.open(filename, "rb") { |f| fileContents = f.read } if (fileContents != aString) if (expected.length > 400 || actual.length > 400) stringFile = filename + ".other" File.open(stringFile, "wb") { |f| f << aString } fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") else assert_equals(expected, actual) end end end def assertStreamContents(zis, testZipFile) assert(zis != nil) testZipFile.entryNames.each { |entryName| assertNextEntry(entryName, zis) } assert_equals(nil, zis.getNextEntry) end def assertTestZipContents(testZipFile) ZipInputStream.open(testZipFile.zipName) { |zis| assertStreamContents(zis, testZipFile) } end def assertEntryContents(zipFile, entryName, filename = entryName.to_s) zis = zipFile.getInputStream(entryName) assertEntryContentsForStream(filename, zis, entryName) ensure zis.close if zis end end class ZipInputStreamTest < RUNIT::TestCase include AssertEntry def test_new zis = ZipInputStream.new(TestZipFile::TEST_ZIP2.zipName) assertStreamContents(zis, TestZipFile::TEST_ZIP2) zis.close end def test_openWithBlock ZipInputStream.open(TestZipFile::TEST_ZIP2.zipName) { |zis| assertStreamContents(zis, TestZipFile::TEST_ZIP2) } end def test_openWithoutBlock zis = ZipInputStream.open(TestZipFile::TEST_ZIP2.zipName) assertStreamContents(zis, TestZipFile::TEST_ZIP2) end def test_incompleteReads ZipInputStream.open(TestZipFile::TEST_ZIP2.zipName) { |zis| entry = zis.getNextEntry assert_equals(TestZipFile::TEST_ZIP2.entryNames[0], entry.name) assert zis.gets.length > 0 entry = zis.getNextEntry assert_equals(TestZipFile::TEST_ZIP2.entryNames[1], entry.name) assert_equals(0, entry.size) assert_equals(nil, zis.gets) entry = zis.getNextEntry assert_equals(TestZipFile::TEST_ZIP2.entryNames[2], entry.name) assert zis.gets.length > 0 entry = zis.getNextEntry assert_equals(TestZipFile::TEST_ZIP2.entryNames[3], entry.name) assert zis.gets.length > 0 } end end class TestFiles RANDOM_ASCII_FILE1 = "randomAscii1.txt" RANDOM_ASCII_FILE2 = "randomAscii2.txt" RANDOM_ASCII_FILE3 = "randomAscii3.txt" RANDOM_BINARY_FILE1 = "randomBinary1.bin" RANDOM_BINARY_FILE2 = "randomBinary2.bin" EMPTY_TEST_DIR = "emptytestdir" ASCII_TEST_FILES = [ RANDOM_ASCII_FILE1, RANDOM_ASCII_FILE2, RANDOM_ASCII_FILE3 ] BINARY_TEST_FILES = [ RANDOM_BINARY_FILE1, RANDOM_BINARY_FILE2 ] TEST_DIRECTORIES = [ EMPTY_TEST_DIR ] TEST_FILES = [ ASCII_TEST_FILES, BINARY_TEST_FILES, EMPTY_TEST_DIR ].flatten! def TestFiles.createTestFiles(recreate) if (recreate || ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) ASCII_TEST_FILES.each_with_index { |filename, index| createRandomAscii(filename, 1E4 * (index+1)) } BINARY_TEST_FILES.each_with_index { |filename, index| createRandomBinary(filename, 1E4 * (index+1)) } ensureDir(EMPTY_TEST_DIR) end end private def TestFiles.createRandomAscii(filename, size) File.open(filename, "wb") { |file| while (file.tell < size) file << rand end } end def TestFiles.createRandomBinary(filename, size) File.open(filename, "wb") { |file| while (file.tell < size) file << rand.to_a.pack("V") end } end def TestFiles.ensureDir(name) if File.exists?(name) return if File.stat(name).directory? File.delete(name) end Dir.mkdir(name) end end # For representation and creation of # test data class TestZipFile attr_accessor :zipName, :entryNames, :comment def initialize(zipName, entryNames, comment = "") @zipName=zipName @entryNames=entryNames @comment = comment end def TestZipFile.createTestZips(recreate) files = Dir.entries(".") if (recreate || ! (files.index(TEST_ZIP1.zipName) && files.index(TEST_ZIP2.zipName) && files.index(TEST_ZIP3.zipName) && files.index(TEST_ZIP4.zipName) && files.index("empty.txt") && files.index("short.txt") && files.index("longAscii.txt") && files.index("longBinary.bin") )) raise "failed to create test zip '#{TEST_ZIP1.zipName}'" unless system("zip #{TEST_ZIP1.zipName} ziptest.rb") raise "failed to remove entry from '#{TEST_ZIP1.zipName}'" unless system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") File.open("empty.txt", "w") {} File.open("short.txt", "w") { |file| file << "ABCDEF" } ziptestTxt="" File.open("ziptest.rb") { |file| ziptestTxt=file.read } File.open("longAscii.txt", "w") { |file| while (file.tell < 1E5) file << ziptestTxt end } testBinaryPattern="" File.open("empty.zip") { |file| testBinaryPattern=file.read } testBinaryPattern *= 4 File.open("longBinary.bin", "wb") { |file| while (file.tell < 3E5) file << testBinaryPattern << rand end } raise "failed to create test zip '#{TEST_ZIP2.zipName}'" unless system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zipName}'" unless system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") raise "failed to create test zip '#{TEST_ZIP3.zipName}'" unless system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") raise "failed to create test zip '#{TEST_ZIP4.zipName}'" unless system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") end rescue raise $!.to_s + "\n\nziptest.rb requires the Info-ZIP program 'zip' in the path\n" + "to create test data. If you don't have it you can download\n" + "the necessary test files at http://sf.net/projects/rubyzip." end TEST_ZIP1 = TestZipFile.new("empty.zip", []) TEST_ZIP2 = TestZipFile.new("4entry.zip", %w{ longAscii.txt empty.txt short.txt longBinary.bin}, "my zip comment") TEST_ZIP3 = TestZipFile.new("test1.zip", %w{ file1.txt }) TEST_ZIP4 = TestZipFile.new("zipWithDir.zip", [ "file1.txt", TestFiles::EMPTY_TEST_DIR]) end class AbstractOutputStreamTest < RUNIT::TestCase class TestOutputStream include AbstractOutputStream attr_accessor :buffer def initialize @buffer = "" end def << (data) @buffer << data self end end def setup @outputStream = TestOutputStream.new @origCommaSep = $, @origOutputSep = $\ end def teardown $, = @origCommaSep $\ = @origOutputSep end def test_write count = @outputStream.write("a little string") assert_equals("a little string", @outputStream.buffer) assert_equals("a little string".length, count) count = @outputStream.write(". a little more") assert_equals("a little string. a little more", @outputStream.buffer) assert_equals(". a little more".length, count) end def test_print $\ = nil # record separator set to nil @outputStream.print("hello") assert_equals("hello", @outputStream.buffer) @outputStream.print(" world.") assert_equals("hello world.", @outputStream.buffer) @outputStream.print(" You ok ", "out ", "there?") assert_equals("hello world. You ok out there?", @outputStream.buffer) $\ = "\n" @outputStream.print assert_equals("hello world. You ok out there?\n", @outputStream.buffer) @outputStream.print("I sure hope so!") assert_equals("hello world. You ok out there?\nI sure hope so!\n", @outputStream.buffer) $, = "X" @outputStream.buffer = "" @outputStream.print("monkey", "duck", "zebra") assert_equals("monkeyXduckXzebra\n", @outputStream.buffer) $\ = nil @outputStream.buffer = "" @outputStream.print(20) assert_equals("20", @outputStream.buffer) end def test_printf @outputStream.printf("%d %04x", 123, 123) assert_equals("123 007b", @outputStream.buffer) end def test_putc @outputStream.putc("A") assert_equals("A", @outputStream.buffer) @outputStream.putc(65) assert_equals("AA", @outputStream.buffer) end def test_puts @outputStream.puts assert_equals("\n", @outputStream.buffer) @outputStream.puts("hello", "world") assert_equals("\nhello\nworld\n", @outputStream.buffer) @outputStream.buffer = "" @outputStream.puts("hello\n", "world\n") assert_equals("hello\nworld\n", @outputStream.buffer) @outputStream.buffer = "" @outputStream.puts(["hello\n", "world\n"]) assert_equals("hello\nworld\n", @outputStream.buffer) @outputStream.buffer = "" @outputStream.puts(["hello\n", "world\n"], "bingo") assert_equals("hello\nworld\nbingo\n", @outputStream.buffer) @outputStream.buffer = "" @outputStream.puts(16, 20, 50, "hello") assert_equals("16\n20\n50\nhello\n", @outputStream.buffer) end end module CrcTest def runCrcTest(compressorClass) str = "Here's a nice little text to compute the crc for! Ho hum, it is nice nice nice nice indeed." fakeOut = AbstractOutputStreamTest::TestOutputStream.new deflater = compressorClass.new(fakeOut) deflater << str assert_equals(0x919920fc, deflater.crc) end end class PassThruCompressorTest < RUNIT::TestCase include CrcTest def test_size File.open("dummy.txt", "wb") { |file| compressor = PassThruCompressor.new(file) assert_equals(0, compressor.size) t1 = "hello world" t2 = "" t3 = "bingo" compressor << t1 assert_equals(compressor.size, t1.size) compressor << t2 assert_equals(compressor.size, t1.size + t2.size) compressor << t3 assert_equals(compressor.size, t1.size + t2.size + t3.size) } end def test_crc runCrcTest(PassThruCompressor) end end class DeflaterTest < RUNIT::TestCase include CrcTest def test_outputOperator txt = loadFile("ziptest.rb") deflate(txt, "deflatertest.bin") inflatedTxt = inflate("deflatertest.bin") assert_equals(txt, inflatedTxt) end private def loadFile(fileName) txt = nil File.open(fileName, "rb") { |f| txt = f.read } end def deflate(data, fileName) File.open(fileName, "wb") { |file| deflater = Deflater.new(file) deflater << data deflater.finish assert_equals(deflater.size, data.size) file << "trailing data for zlib with -MAX_WBITS" } end def inflate(fileName) txt = nil File.open(fileName, "rb") { |file| inflater = Inflater.new(file) txt = inflater.read } end def test_crc runCrcTest(Deflater) end end class ZipOutputStreamTest < RUNIT::TestCase include AssertEntry TEST_ZIP = TestZipFile::TEST_ZIP2.clone TEST_ZIP.zipName = "output.zip" def test_new zos = ZipOutputStream.new(TEST_ZIP.zipName) zos.comment = TEST_ZIP.comment writeTestZip(zos) zos.close assertTestZipContents(TEST_ZIP) end def test_open ZipOutputStream.open(TEST_ZIP.zipName) { |zos| zos.comment = TEST_ZIP.comment writeTestZip(zos) } assertTestZipContents(TEST_ZIP) end def test_writingToClosedStream assertIOErrorInClosedStream { |zos| zos << "hello world" } assertIOErrorInClosedStream { |zos| zos.puts "hello world" } assertIOErrorInClosedStream { |zos| zos.write "hello world" } end def test_cannotOpenFile name = TestFiles::EMPTY_TEST_DIR begin zos = ZipOutputStream.open(name) rescue Exception assert($!.kind_of?(Errno::EISDIR) || # Linux $!.kind_of?(Errno::EEXIST) || # Windows/cygwin $!.kind_of?(Errno::EACCES), # Windows "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") end end def assertIOErrorInClosedStream assert_exception(IOError) { zos = ZipOutputStream.new("test_putOnClosedStream.zip") zos.close yield zos } end def writeTestZip(zos) TEST_ZIP.entryNames.each { |entryName| zos.putNextEntry(entryName) File.open(entryName, "rb") { |f| zos.write(f.read) } } end end module Enumerable def compareEnumerables(otherEnumerable) otherAsArray = otherEnumerable.to_a index=0 each_with_index { |element, index| return false unless yield(element, otherAsArray[index]) } return index+1 == otherAsArray.size end end class ZipCentralDirectoryEntryTest < RUNIT::TestCase def test_readFromStream File.open("testDirectory.bin", "rb") { |file| entry = ZipEntry.readCDirEntry(file) assert_equals("longAscii.txt", entry.name) assert_equals(ZipEntry::DEFLATED, entry.compressionMethod) assert_equals(106490, entry.size) assert_equals(3784, entry.compressedSize) assert_equals(0xfcd1799c, entry.crc) assert_equals("", entry.comment) entry = ZipEntry.readCDirEntry(file) assert_equals("empty.txt", entry.name) assert_equals(ZipEntry::STORED, entry.compressionMethod) assert_equals(0, entry.size) assert_equals(0, entry.compressedSize) assert_equals(0x0, entry.crc) assert_equals("", entry.comment) entry = ZipEntry.readCDirEntry(file) assert_equals("short.txt", entry.name) assert_equals(ZipEntry::STORED, entry.compressionMethod) assert_equals(6, entry.size) assert_equals(6, entry.compressedSize) assert_equals(0xbb76fe69, entry.crc) assert_equals("", entry.comment) entry = ZipEntry.readCDirEntry(file) assert_equals("longBinary.bin", entry.name) assert_equals(ZipEntry::DEFLATED, entry.compressionMethod) assert_equals(1000024, entry.size) assert_equals(70847, entry.compressedSize) assert_equals(0x10da7d59, entry.crc) assert_equals("", entry.comment) entry = ZipEntry.readCDirEntry(file) assert_equals(nil, entry) # Fields that are not check by this test: # version made by 2 bytes # version needed to extract 2 bytes # general purpose bit flag 2 bytes # last mod file time 2 bytes # last mod file date 2 bytes # compressed size 4 bytes # uncompressed size 4 bytes # disk number start 2 bytes # internal file attributes 2 bytes # external file attributes 4 bytes # relative offset of local header 4 bytes # file name (variable size) # extra field (variable size) # file comment (variable size) } end def test_ReadEntryFromTruncatedZipFile fragment="" File.open("testDirectory.bin") { |f| fragment = f.read(12) } # cdir entry header is at least 46 bytes fragment.extend(IOizeString) entry = ZipEntry.new entry.readCDirEntry(fragment) fail "ZipError expected" rescue ZipError end end class ZipCentralDirectoryTest < RUNIT::TestCase def test_readFromStream File.open(TestZipFile::TEST_ZIP2.zipName, "rb") { |zipFile| cdir = ZipCentralDirectory.readFromStream(zipFile) assert_equals(TestZipFile::TEST_ZIP2.entryNames.size, cdir.size) assert(cdir.compareEnumerables(TestZipFile::TEST_ZIP2.entryNames) { |cdirEntry, testEntryName| cdirEntry.name == testEntryName }) assert_equals(TestZipFile::TEST_ZIP2.comment, cdir.comment) } end def test_readFromInvalidStream File.open("ziptest.rb", "rb") { |zipFile| cdir = ZipCentralDirectory.new cdir.readFromStream(zipFile) } fail "ZipError expected!" rescue ZipError end def test_ReadFromTruncatedZipFile fragment="" File.open("testDirectory.bin") { |f| fragment = f.read } fragment.slice!(12) # removed part of first cdir entry. eocd structure still complete fragment.extend(IOizeString) entry = ZipCentralDirectory.new entry.readFromStream(fragment) fail "ZipError expected" rescue ZipError end def test_writeToStream entries = [ ZipEntry.new("file.zip", "flimse", "myComment", "somethingExtra"), ZipEntry.new("file.zip", "secondEntryName"), ZipEntry.new("file.zip", "lastEntry.txt", "Has a comment too") ] cdir = ZipCentralDirectory.new(entries, "my zip comment") File.open("cdirtest.bin", "wb") { |f| cdir.writeToStream(f) } cdirReadback = ZipCentralDirectory.new File.open("cdirtest.bin", "rb") { |f| cdirReadback.readFromStream(f) } assert_equals(cdir.entries, cdirReadback.entries) end def test_equality cdir1 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, "somethingExtra"), ZipEntry.new("file.zip", "secondEntryName"), ZipEntry.new("file.zip", "lastEntry.txt") ], "my zip comment") cdir2 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, "somethingExtra"), ZipEntry.new("file.zip", "secondEntryName"), ZipEntry.new("file.zip", "lastEntry.txt") ], "my zip comment") cdir3 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, "somethingExtra"), ZipEntry.new("file.zip", "secondEntryName"), ZipEntry.new("file.zip", "lastEntry.txt") ], "comment?") cdir4 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, "somethingExtra"), ZipEntry.new("file.zip", "lastEntry.txt") ], "comment?") assert_equals(cdir1, cdir1) assert_equals(cdir1, cdir2) assert(cdir1 != cdir3) assert(cdir2 != cdir3) assert(cdir2 != cdir3) assert(cdir3 != cdir4) assert(cdir3 != "hello") end end class BasicZipFileTest < RUNIT::TestCase include AssertEntry def setup @zipFile = ZipFile.new(TestZipFile::TEST_ZIP2.zipName) @testEntryNameIndex=0 end def nextTestEntryName retVal=TestZipFile::TEST_ZIP2.entryNames[@testEntryNameIndex] @testEntryNameIndex+=1 return retVal end def test_entries assert_equals(TestZipFile::TEST_ZIP2.entryNames, @zipFile.entries.map {|e| e.name} ) end def test_each @zipFile.each { |entry| assert_equals(nextTestEntryName, entry.name) } assert_equals(4, @testEntryNameIndex) end def test_foreach ZipFile.foreach(TestZipFile::TEST_ZIP2.zipName) { |entry| assert_equals(nextTestEntryName, entry.name) } assert_equals(4, @testEntryNameIndex) end def test_getInputStream @zipFile.each { |entry| assertEntry(nextTestEntryName, @zipFile.getInputStream(entry), entry.name) } assert_equals(4, @testEntryNameIndex) end def test_getInputStreamBlock fileAndEntryName = @zipFile.entries.first.name @zipFile.getInputStream(fileAndEntryName) { |zis| assertEntryContentsForStream(fileAndEntryName, zis, fileAndEntryName) } end end class CommonZipFileFixture < RUNIT::TestCase include AssertEntry EMPTY_FILENAME = "emptyZipFile.zip" TEST_ZIP = TestZipFile::TEST_ZIP2.clone TEST_ZIP.zipName = "4entry_copy.zip" def setup File.delete(EMPTY_FILENAME) if File.exists?(EMPTY_FILENAME) File.copy(TestZipFile::TEST_ZIP2.zipName, TEST_ZIP.zipName) end end class ZipFileTest < CommonZipFileFixture def test_createFromScratch comment = "a short comment" zf = ZipFile.new(EMPTY_FILENAME, ZipFile::CREATE) zf.comment = comment zf.close zfRead = ZipFile.new(EMPTY_FILENAME) assert_equals(comment, zfRead.comment) assert_equals(0, zfRead.entries.length) end def test_add srcFile = "ziptest.rb" entryName = "newEntryName.rb" assert(File.exists? srcFile) zf = ZipFile.new(EMPTY_FILENAME, ZipFile::CREATE) zf.add(entryName, srcFile) zf.close zfRead = ZipFile.new(EMPTY_FILENAME) assert_equals("", zfRead.comment) assert_equals(1, zfRead.entries.length) assert_equals(entryName, zfRead.entries.first.name) AssertEntry.assertContents(srcFile, zfRead.getInputStream(entryName) { |zis| zis.read }) end def test_addExistingEntryName assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { |zf| zf.add(zf.entries.first.name, "ziptest.rb") } } end def test_addExistingEntryNameReplace gotCalled = false replacedEntry = nil ZipFile.open(TEST_ZIP.zipName) { |zf| replacedEntry = zf.entries.first.name zf.add(replacedEntry, "ziptest.rb") { gotCalled = true; true } } assert(gotCalled) ZipFile.open(TEST_ZIP.zipName) { |zf| assertContains(zf, replacedEntry, "ziptest.rb") } end def test_addDirectory ZipFile.open(TEST_ZIP.zipName) { |zf| zf.add(TestFiles::EMPTY_TEST_DIR, TestFiles::EMPTY_TEST_DIR) } ZipFile.open(TEST_ZIP.zipName) { |zf| dirEntry = zf.entries.detect { |e| e.name == TestFiles::EMPTY_TEST_DIR+"/" } assert(dirEntry.isDirectory) } end def test_remove entryToRemove, *remainingEntries = TEST_ZIP.entryNames File.copy(TestZipFile::TEST_ZIP2.zipName, TEST_ZIP.zipName) zf = ZipFile.new(TEST_ZIP.zipName) assert(zf.entries.map { |e| e.name }.include?(entryToRemove)) zf.remove(entryToRemove) assert(! zf.entries.map { |e| e.name }.include?(entryToRemove)) assert_equals(zf.entries.map {|x| x.name }.sort, remainingEntries.sort) zf.close zfRead = ZipFile.new(TEST_ZIP.zipName) assert(! zfRead.entries.map { |e| e.name }.include?(entryToRemove)) assert_equals(zfRead.entries.map {|x| x.name }.sort, remainingEntries.sort) zfRead.close end def test_rename entryToRename, *remainingEntries = TEST_ZIP.entryNames zf = ZipFile.new(TEST_ZIP.zipName) assert(zf.entries.map { |e| e.name }.include? entryToRename) newName = "changed name" assert(! zf.entries.map { |e| e.name }.include?(newName)) zf.rename(entryToRename, newName) assert(zf.entries.map { |e| e.name }.include? newName) zf.close zfRead = ZipFile.new(TEST_ZIP.zipName) assert(zfRead.entries.map { |e| e.name }.include? newName) zfRead.close end def test_renameToExistingEntry oldEntries = nil ZipFile.open(TEST_ZIP.zipName) { |zf| oldEntries = zf.entries } assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { |zf| zf.rename(zf.entries[0], zf.entries[1].name) } } ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, zf.entries.map{ |e| e.name }) } end def test_renameToExistingEntryOverwrite oldEntries = nil ZipFile.open(TEST_ZIP.zipName) { |zf| oldEntries = zf.entries } gotCalled = false ZipFile.open(TEST_ZIP.zipName) { |zf| zf.rename(zf.entries[0], zf.entries[1].name) { gotCalled = true; true } } assert(gotCalled) oldEntries.delete_at(0) ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, zf.entries.map{ |e| e.name }) } end def test_renameNonEntry nonEntry = "bogusEntry" targetEntry = "targetEntryName" zf = ZipFile.new(TEST_ZIP.zipName) assert(! zf.entries.include?(nonEntry)) assert_exception(ZipNoSuchEntryError) { zf.rename(nonEntry, targetEntry) } zf.commit assert(! zf.entries.include?(targetEntry)) ensure zf.close end def test_renameEntryToExistingEntry entry1, entry2, *remaining = TEST_ZIP.entryNames zf = ZipFile.new(TEST_ZIP.zipName) assert_exception(ZipEntryExistsError) { zf.rename(entry1, entry2) } ensure zf.close end def test_replace unchangedEntries = TEST_ZIP.entryNames.dup entryToReplace = unchangedEntries.delete_at(2) newEntrySrcFilename = "ziptest.rb" zf = ZipFile.new(TEST_ZIP.zipName) zf.replace(entryToReplace, newEntrySrcFilename) zf.close zfRead = ZipFile.new(TEST_ZIP.zipName) AssertEntry::assertContents(newEntrySrcFilename, zfRead.getInputStream(entryToReplace) { |is| is.read }) zfRead.close end def test_replaceNonEntry entryToReplace = "nonExistingEntryname" ZipFile.open(TEST_ZIP.zipName) { |zf| assert_exception(ZipNoSuchEntryError) { zf.replace(entryToReplace, "ziptest.rb") } } end def test_commit newName = "renamedFirst" zf = ZipFile.new(TEST_ZIP.zipName) oldName = zf.entries.first zf.rename(oldName, newName) zf.commit zfRead = ZipFile.new(TEST_ZIP.zipName) assert(zfRead.entries.detect { |e| e.name == newName } != nil) assert(zfRead.entries.detect { |e| e.name == oldName } == nil) zfRead.close zf.close end # This test tests that after commit, you # can delete the file you used to add the entry to the zip file # with def test_commitUseZipEntry File.copy(TestFiles::RANDOM_ASCII_FILE1, "okToDelete.txt") zf = ZipFile.open(TEST_ZIP.zipName) zf.add("okToDelete.txt", "okToDelete.txt") assertContains(zf, "okToDelete.txt") zf.commit File.move("okToDelete.txt", "okToDeleteMoved.txt") assertContains(zf, "okToDelete.txt", "okToDeleteMoved.txt") end # def test_close # zf = ZipFile.new(TEST_ZIP.zipName) # zf.close # assert_exception(IOError) { # zf.extract(TEST_ZIP.entryNames.first, "hullubullu") # } # end def test_compound1 renamedName = "renamedName" originalEntries = [] begin zf = ZipFile.new(TEST_ZIP.zipName) originalEntries = zf.entries.dup assertNotContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, TestFiles::RANDOM_ASCII_FILE1) assertContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.rename(zf.entries[0], renamedName) assertContains(zf, renamedName) TestFiles::BINARY_TEST_FILES.each { |filename| zf.add(filename, filename) assertContains(zf, filename) } assertContains(zf, originalEntries.last.to_s) zf.remove(originalEntries.last.to_s) assertNotContains(zf, originalEntries.last.to_s) ensure zf.close end begin zfRead = ZipFile.new(TEST_ZIP.zipName) assertContains(zfRead, TestFiles::RANDOM_ASCII_FILE1) assertContains(zfRead, renamedName) TestFiles::BINARY_TEST_FILES.each { |filename| assertContains(zfRead, filename) } assertNotContains(zfRead, originalEntries.last.to_s) ensure zfRead.close end end def test_compound2 begin zf = ZipFile.new(TEST_ZIP.zipName) originalEntries = zf.entries.dup originalEntries.each { |entry| zf.remove(entry) assertNotContains(zf, entry) } assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each { |filename| zf.add(filename, filename) assertContains(zf, filename) } assert_equals(zf.entries.map { |e| e.name }, TestFiles::ASCII_TEST_FILES) zf.rename(TestFiles::ASCII_TEST_FILES[0], "newName") assertNotContains(zf, TestFiles::ASCII_TEST_FILES[0]) assertContains(zf, "newName") ensure zf.close end begin zfRead = ZipFile.new(TEST_ZIP.zipName) asciiTestFiles = TestFiles::ASCII_TEST_FILES.dup asciiTestFiles.shift asciiTestFiles.each { |filename| assertContains(zf, filename) } assertContains(zf, "newName") ensure zfRead.close end end private def assertContains(zf, entryName, filename = entryName) assert(zf.entries.detect { |e| e.name == entryName} != nil, "entry #{entryName} not in #{zf.entries.join(', ')} in zip file #{zf}") assertEntryContents(zf, entryName, filename) if File.exists?(filename) end def assertNotContains(zf, entryName) assert(zf.entries.detect { |e| e.name == entryName} == nil, "entry #{entryName} in #{zf.entries.join(', ')} in zip file #{zf}") end end class ZipFileExtractTest < CommonZipFileFixture EXTRACTED_FILENAME = "extEntry" ENTRY_TO_EXTRACT, *REMAINING_ENTRIES = TEST_ZIP.entryNames.reverse def setup super File.delete(EXTRACTED_FILENAME) if File.exists?(EXTRACTED_FILENAME) end def test_extract ZipFile.open(TEST_ZIP.zipName) { |zf| zf.extract(ENTRY_TO_EXTRACT, EXTRACTED_FILENAME) assert(File.exists? EXTRACTED_FILENAME) AssertEntry::assertContents(EXTRACTED_FILENAME, zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) } end def test_extractExists writtenText = "written text" File.open(EXTRACTED_FILENAME, "w") { |f| f.write(writtenText) } assert_exception(ZipDestinationFileExistsError) { ZipFile.open(TEST_ZIP.zipName) { |zf| zf.extract(zf.entries.first, EXTRACTED_FILENAME) } } File.open(EXTRACTED_FILENAME, "r") { |f| assert_equals(writtenText, f.read) } end def test_extractExistsOverwrite writtenText = "written text" File.open(EXTRACTED_FILENAME, "w") { |f| f.write(writtenText) } gotCalled = false ZipFile.open(TEST_ZIP.zipName) { |zf| zf.extract(zf.entries.first, EXTRACTED_FILENAME) { gotCalled = true; true } } assert(gotCalled) File.open(EXTRACTED_FILENAME, "r") { |f| assert(writtenText != f.read) } end def test_extractNonEntry zf = ZipFile.new(TEST_ZIP.zipName) assert_exception(ZipNoSuchEntryError) { zf.extract("nonExistingEntry", "nonExistingEntry") } ensure zf.close if zf end def test_extractNonEntry2 outFile = "outfile" assert_exception(ZipNoSuchEntryError) { zf = ZipFile.new(TEST_ZIP.zipName) nonEntry = "hotdog-diddelidoo" assert(! zf.entries.include?(nonEntry)) zf.extract(nonEntry, outFile) zf.close } assert(! File.exists?(outFile)) end end class ZipFileExtractDirectoryTest < CommonZipFileFixture TEST_OUT_NAME = "emptyOutDir" def openZip(&aProc) assert(aProc != nil) ZipFile.open(TestZipFile::TEST_ZIP4.zipName, &aProc) end def extractTestDir(&aProc) openZip { |zf| zf.extract(TestFiles::EMPTY_TEST_DIR, TEST_OUT_NAME, &aProc) } end def setup super Dir.rmdir(TEST_OUT_NAME) if File.directory? TEST_OUT_NAME File.delete(TEST_OUT_NAME) if File.exists? TEST_OUT_NAME end def test_extractDirectory extractTestDir assert(File.directory? TEST_OUT_NAME) end def test_extractDirectoryExistsAsDir Dir.mkdir TEST_OUT_NAME extractTestDir assert(File.directory? TEST_OUT_NAME) end def test_extractDirectoryExistsAsFile File.open(TEST_OUT_NAME, "w") { |f| f.puts "something" } assert_exception(ZipDestinationFileExistsError) { extractTestDir } end def test_extractDirectoryExistsAsFileOverwrite File.open(TEST_OUT_NAME, "w") { |f| f.puts "something" } gotCalled = false extractTestDir { |entry, destPath| gotCalled = true assert_equals(TEST_OUT_NAME, destPath) assert(entry.isDirectory) true } assert(gotCalled) assert(File.directory? TEST_OUT_NAME) end end TestFiles::createTestFiles(ARGV.index("recreate") != nil || ARGV.index("recreateonly") != nil) TestZipFile::createTestZips(ARGV.index("recreate") != nil || ARGV.index("recreateonly") != nil) exit if ARGV.index("recreateonly") != nil #require 'runit/cui/testrunner' #RUNIT::CUI::TestRunner.run(ZipFileTest.suite) # Copyright (C) 2002 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. rubyzip-2.3.0/test/data/globTest.zip000066400000000000000000000021721363314356200174270ustar00rootroot00000000000000PK ;@ globTest/UX OOPK L@ globTest/foo/UX OOPK L@globTest/foo/bar/UX OOPK Ʀ@globTest/foo/bar/baz/UX OOPK O@globTest/foo/bar/baz/foo/UX OOPK Ʀ@globTest/foo/bar/baz/foo.txtUX OOPK 4@globTest/foo.txtUX 䵶O䵶OPK ;@globTest/food.txtUX OOPK ;@ @AglobTest/UXOOPK L@ @A7globTest/foo/UXOOPK L@ @ArglobTest/foo/bar/UXOOPK Ʀ@ @AglobTest/foo/bar/baz/UXOOPK O@ @AglobTest/foo/bar/baz/foo/UXOOPK Ʀ@ @;globTest/foo/bar/baz/foo.txtUXOOPK 4@ @globTest/foo.txtUX䵶O䵶OPK ;@ @globTest/food.txtUXOOPKbrubyzip-2.3.0/test/data/globTest/000077500000000000000000000000001363314356200167015ustar00rootroot00000000000000rubyzip-2.3.0/test/data/globTest/foo.txt000066400000000000000000000000001363314356200202130ustar00rootroot00000000000000rubyzip-2.3.0/test/data/globTest/foo/000077500000000000000000000000001363314356200174645ustar00rootroot00000000000000rubyzip-2.3.0/test/data/globTest/foo/bar/000077500000000000000000000000001363314356200202305ustar00rootroot00000000000000rubyzip-2.3.0/test/data/globTest/foo/bar/baz/000077500000000000000000000000001363314356200210045ustar00rootroot00000000000000rubyzip-2.3.0/test/data/globTest/foo/bar/baz/foo.txt000066400000000000000000000000001363314356200223160ustar00rootroot00000000000000rubyzip-2.3.0/test/data/globTest/food.txt000066400000000000000000000000001363314356200203570ustar00rootroot00000000000000rubyzip-2.3.0/test/data/gpbit3stored.zip000066400000000000000000000002041363314356200202470ustar00rootroot00000000000000PKU|Lfoo.txtfoo PKe2~PKU|Le2~foo.txtPK59rubyzip-2.3.0/test/data/mimetype000066400000000000000000000000241363314356200166660ustar00rootroot00000000000000application/epub+ziprubyzip-2.3.0/test/data/notzippedruby.rb000077500000000000000000000001161363314356200203620ustar00rootroot00000000000000#!/usr/bin/env ruby class NotZippedRuby def return_true true end end rubyzip-2.3.0/test/data/ntfs.zip000066400000000000000000000002451363314356200166150ustar00rootroot00000000000000PKë+EVY data.txt +I+V(I-.PK4ë+EVY $data.txt BBBPKZ5rubyzip-2.3.0/test/data/oddExtraField.zip000066400000000000000000000077021363314356200203660ustar00rootroot00000000000000PK }=Gcases/UT NN VTN Vux PKZ=G%a cases/1.inUT  N VTN Vux RPP*,M-VR0qJRKҼdTRi^&PkNjnj^IX>($ ҭ JJ`͎ ] tP\#T1*΋UPKZ=G8/> cases/1.outUT  N VTN Vux RPP*,M-VR0qJRK̼[Z\-)*MPK\=G_W cases/2.inUT N VTN Vux RPP*,M-VR0qJRK#0'4//17(T T蚓WRO,JIt+(DE0x,X#H&8/V PK\=Gp#Y0> cases/2.outUT N VTN Vux RPP*,M-VR0qJRK#0 ?3-J-.ɖrrPK]=G\ cases/3.inUT N VTN Vux RPP*,M-VR0qJRKc0'4//17(T T蚓WRO,JIt+(DE0x,X#H&zp~W-PK]=G$2? cases/3.outUT N VTN Vux RPP*,M-VR0qJRKc0 ?3$gdsSjPK}=Gh%k cases/4.inUT NN VTN Vux RPP*,M-VR0qJRK0'4//17(T T蚓WRO,JIt+(DE0x,X#H&!*&M@" \\PK}=G2? cases/4.outUT NN VTN Vux RPP*,M-VR0qJRK0 ?3$gdsSjPKZb6GcfX DockerfileUT kVN Vux s U(,ϳ23 SN,R-P/-./I-J()$g(`Jp)D+\24E9@y\PK|t=GjU#= mapper.pyUT  VN Vux Rn0}WܑATiJ42J&M<0 [6&UI:s]7T@c/7^ 6H}l4ɟZB7Ѡ*0B&(:pPnnPמ-,3TfK%{wsz/w챤ʰg/8u !K N2X%C>I}6qY2Yd1l2|ESq]='x5Vx3_.@7Mw(_$kHكD#U%NnqhUQR GOypqg1'937jNU6RȱQǓN ?4ĉpcYz/-!'wkki:o>c 7܋doΑ."*c'k*PK6G2,->mapreduced.ymlUT VN Vux j0w?ANv k!]PK$N${l"q~;m؄.~x 8QKgZK!V9*aL,j0N9" 6BLQAN> <3jP`{-߃fH r TX:^==|>6 *bGXsv˞\I8+jWr/w"]\ed<EFM]5A1>T5?E=2?ܨZ~G[e?PK|t=GO reducer.pyUT  VN Vux UM0W !ɶnRT-BwhUUqC'GSJCx< cases/1.outUT N Vux PK\=G_W Ycases/2.inUTN Vux PK\=Gp#Y0> cases/2.outUTN Vux PK]=G\ icases/3.inUTN Vux PK]=G$2?  cases/3.outUTN Vux PK}=Gh%k cases/4.inUTNN Vux PK}=G2? /cases/4.outUTNN Vux PKZb6GcfX DockerfileUTkVux PK|t=GjU#= Bmapper.pyUT Vux PK6G2,->Emapreduced.ymlUTVux PK|t=GO reducer.pyUT Vux PK  rubyzip-2.3.0/test/data/path_traversal/000077500000000000000000000000001363314356200201355ustar00rootroot00000000000000rubyzip-2.3.0/test/data/path_traversal/Makefile000066400000000000000000000005621363314356200216000ustar00rootroot00000000000000# Based on 'relative2' in https://github.com/jwilk/path-traversal-samples, # but create the local `tmp` folder before adding the symlink. Otherwise # we may bail out before we get to trying to create the file. all: relative1.zip relative1.zip: rm -f $(@) mkdir -p -m 755 tmp/tmp umask 022 && echo moo > moo cd tmp && zip -X ../$(@) tmp tmp/../../moo rm -rf tmp moo rubyzip-2.3.0/test/data/path_traversal/jwilk/000077500000000000000000000000001363314356200212555ustar00rootroot00000000000000rubyzip-2.3.0/test/data/path_traversal/jwilk/README.md000066400000000000000000000001631363314356200225340ustar00rootroot00000000000000# Path Traversal Samples Copied from https://github.com/jwilk/path-traversal-samples on 2018-08-26. License: MIT rubyzip-2.3.0/test/data/path_traversal/jwilk/absolute1.zip000066400000000000000000000001661363314356200237030ustar00rootroot00000000000000PK L3/tmp/moomoo PK L3/tmp/mooPK6*rubyzip-2.3.0/test/data/path_traversal/jwilk/absolute2.zip000066400000000000000000000001701363314356200236770ustar00rootroot00000000000000PK L3 //tmp/moomoo PK L3 //tmp/mooPK7+rubyzip-2.3.0/test/data/path_traversal/jwilk/dirsymlink.zip000066400000000000000000000003121363314356200241620ustar00rootroot00000000000000PK L.Ļ tmp/tmpPK L3tmp/moomoo PK L.Ļ tmpPK L3%tmp/mooPKfNrubyzip-2.3.0/test/data/path_traversal/jwilk/dirsymlink2a.zip000066400000000000000000000004371363314356200244150ustar00rootroot00000000000000PK LBcur.PK Lf+parcur/..PK L3par/moomoo PK LBcurPK Lf+"parPK L3Ipar/mooPKrrubyzip-2.3.0/test/data/path_traversal/jwilk/dirsymlink2b.zip000066400000000000000000000004431363314356200244130ustar00rootroot00000000000000PK LBcur.PK Lcur/par..PK L3par/moomoo PK LBcurPK L"cur/parPK L3Ipar/mooPKrrubyzip-2.3.0/test/data/path_traversal/jwilk/relative0.zip000066400000000000000000000001621363314356200236730ustar00rootroot00000000000000PK L3../moomoo PK L3../mooPK4(rubyzip-2.3.0/test/data/path_traversal/jwilk/relative2.zip000066400000000000000000000002001363314356200236660ustar00rootroot00000000000000PK L3 tmp/../../moomoo PK L3 tmp/../../mooPK;/rubyzip-2.3.0/test/data/path_traversal/jwilk/symlink.zip000066400000000000000000000003061363314356200234660ustar00rootroot00000000000000PK LZKݗmoo/tmp/mooPK L3moomoo PK LZKݗmooPK L3)mooPKbNrubyzip-2.3.0/test/data/path_traversal/relative1.zip000066400000000000000000000003241363314356200225540ustar00rootroot00000000000000PK rMtmp/PK rM3 tmp/../../moomoo PK rMAtmp/PK rM3 "tmp/../../mooPKmQrubyzip-2.3.0/test/data/path_traversal/tilde.zip000066400000000000000000000011011363314356200217530ustar00rootroot00000000000000PK=yaN~tilde~UX {\FKy\+IIPK"sPK ncN __MACOSX/UX {\{\PK=yaN__MACOSX/._~tilde~UX {\FKy\c`cg`b`MLVVP'" 0CBL@l!.XPWXXWPo``ahmllljQjmafkkfdhknh`dnhPKUBPK=yaN"s @~tilde~UX{\FKy\PK ncN @AM__MACOSX/UX{\{\PK=yaNUB @__MACOSX/._~tilde~UX{\FKy\PK[rubyzip-2.3.0/test/data/path_traversal/tuzovakaoff/000077500000000000000000000000001363314356200224745ustar00rootroot00000000000000rubyzip-2.3.0/test/data/path_traversal/tuzovakaoff/README.md000066400000000000000000000001471363314356200237550ustar00rootroot00000000000000# Path Traversal Samples Copied from https://github.com/tuzovakaoff/zip_path_traversal on 2018-08-25. rubyzip-2.3.0/test/data/path_traversal/tuzovakaoff/absolutepath.zip000066400000000000000000000004411363314356200257120ustar00rootroot00000000000000PK L/tmp/UX ![z![PKL /tmp/file.txtUX ![z![+I(PK'7PK L @A/tmp/UX![z![PKL'7 @3/tmp/file.txtUX![z![PKrubyzip-2.3.0/test/data/path_traversal/tuzovakaoff/symlink.zip000066400000000000000000000005121363314356200247040ustar00rootroot00000000000000PK [kL\KNpathUT B"[B"[ux ../../../../../tmpPK ]L path/file.txtUT +"[=C"[ux PK [kL\KNpathUTB"[ux PK ]L Ppath/file.txtUT+"[ux PKrubyzip-2.3.0/test/data/rubycode.zip000066400000000000000000000011511363314356200174540ustar00rootroot00000000000000PK, MWzippedruby1.rbUxUT Ʋ<SV/-.OO+S(*MJI,.V,(HM r)()JԼ^.0PK,͢NYzippedruby2.rbUxUT <SV/-.OO+S(*MJI,.V,(HM r)()%`Լ^.0PK], aResource.txtUxUT </KWHH,12" i9PK, MW zippedruby1.rbUxUTƲ kBm  !"#$%&'()*+,-./0123456789:;<=>?@ACDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~R FC4lWorkbookhSummaryInformation(DocumentSummaryInformation8 7g\p Julia Vogel Ba==0\LE8@"1Arial1Calibri1Calibri1Calibri1h>Cambria1,>Calibri1>Calibri1>Calibri1Calibri1Calibri1<Calibri1>Calibri1?Calibri14Calibri14Calibri1 Calibri1 Calibri1Arial1Calibri1Calibri1 Calibri1 Arial1Arial3#,##0\ " ";\-#,##0\ " "=#,##0\ " ";[Red]\-#,##0\ " "?#,##0.00\ " ";\-#,##0.00\ " "I"#,##0.00\ " ";[Red]\-#,##0.00\ " "q*6_-* #,##0\ " "_-;\-* #,##0\ " "_-;_-* "-"\ " "_-;_-@_-k)3_-* #,##0\ _ _-;\-* #,##0\ _ _-;_-* "-"\ _ _-;_-@_-,>_-* #,##0.00\ " "_-;\-* #,##0.00\ " "_-;_-* "-"??\ " "_-;_-@_-{+;_-* #,##0.00\ _ _-;\-* #,##0.00\ _ _-;_-* "-"??\ _ _-;_-@_-                                                                         a              P  P     `   ff ||9c}-}1 _ _-}A}2 _ _-#0.0}A}3 _ _-?#0.0}A}4 _ _-23#0.0}-}5 _ _-}A}- a_ _-#0.0}A}0 _ _-#0.0}A}/ e_ _-#0.0}}* ??v_ _-̙#0.0-;_-?\ _ @_-@ hA}}( ???_ _-#0.0???-;_-????\ _ ???@_-@ ???hA}}) }_ _-#0.0-;_-?\ _ @_-@ hA}A}6 }_ _-#0.0}}8 _ _-#0.0???-;_-????\ _ ???@_-@ ???hA}-}7 _ _-}x}._ _-#0???-; ????\ ???@_}-}, _ _-}U}+ _ _-#0-; }A}" _ _-#0}A} _ _-ef#0}A} _ _-L#0}A} _ _-23#0}A}# _ _-#0}A} _ _-ef#0}A} _ _-L#0}A} _ _-23#0}A}$ _ _-#0}A} _ _-ef#0}A} _ _-L#0}A} _ _-23#0}A}% _ _-#0}A} _ _-ef#0}A} _ _-L#0}A} _ _-23#0}A}& _ _-#0}A} _ _-ef#0}A} _ _-L#0}A}  _ _-23#0}A}' _ _- #0}A} _ _-ef #0}A} _ _-L #0}A}! _ _-23 #020 % - Akzent1O20 % - Akzent1 ef %20 % - Akzent2O"20 % - Akzent2 ef %20 % - Akzent3O&20 % - Akzent3 ef %20 % - Akzent4O*20 % - Akzent4 ef %20 % - Akzent5O.20 % - Akzent5 ef %20 % - Akzent6O220 % - Akzent6  ef %40 % - Akzent1O40 % - Akzent1 L %40 % - Akzent2O#40 % - Akzent2 L渷 %40 % - Akzent3O'40 % - Akzent3 L %40 % - Akzent4O+40 % - Akzent4 L %40 % - Akzent5O/40 % - Akzent5 L %40 % - Akzent6O340 % - Akzent6  Lմ %60 % - Akzent1O 60 % - Akzent1 23 %60 % - Akzent2O$60 % - Akzent2 23ږ %60 % - Akzent3O(60 % - Akzent3 23כ %60 % - Akzent4O,60 % - Akzent4 23 % 60 % - Akzent5O060 % - Akzent5 23 %!60 % - Akzent6O460 % - Akzent6  23 % "Akzent1AAkzent1 O % #Akzent2A!Akzent2 PM % $Akzent3A%Akzent3 Y % %Akzent4A)Akzent4 d % &Akzent5A-Akzent5 K % 'Akzent6A1Akzent6  F % (AusgabeyAusgabe  ???%????????? ???) Berechnung Berechnung  }%  *EingabeyEingabe ̙ ??v%  +ErgebnisSErgebnis %OO,Erklrender TextG5Erklrender Text %-Gut9Gut  a% .Hinweish Hinweis   /NeutralANeutral  e% 0SchlechtCSchlecht  %&Standard 1Titel1Titel I}%2 berschrift 1O berschrift 1 I}%O3 berschrift 2O berschrift 2 I}%?4 berschrift 3O berschrift 3 I}%235 berschrift 4A berschrift 4 I}%6Verknpfte ZelleUVerknpfte Zelle }%7Warnender TextC Warnender Text %8Zelle berprfenZelle berprfen  %????????? ???XTableStyleMedium9PivotStyleMedium483ffff̙̙3f3fff3f3f33333f33333\`Z5 Worksheet111"  HandelsketteStrasse u HausnummerPLZOrtHaarpflege Abteilung (Insel)Haarpflege RegalHead & Shoulders Regal Pantene RegalWeitere Markenweiteres Aktionsregaljob_id geo_locationDMBahnhofstrae 1136433 Bad Salzungenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/94/6f5cf69cd2512982a9c9b2fd103314cf-IMG_1274.JPG?1448553788https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/95/6f5cf69cd2512982a9c9b2fd103314cf-IMG_1274.JPG?1448553788https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/96/9eb5a3e2a1a2a970a6ee796dc5ef279f-IMG_1269.JPG?1448553788https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/97/0d334be0fa39c05e47dc1cd9db0905bb-IMG_1270.JPG?1448553788https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/98/5075915f00960bf5ed37770819296d95-IMG_1272.JPG?1448553788Mller Speiemarkt 455411Bingen am Rheinhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/99/8834bc2290fc9eb343cac0106f05e711-2015-11-26_16.58.22.jpg?1448553813https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/100/31dd4b37ace062c4141a5ce4eb210b36-2015-11-26_16.59.10.jpg?1448553813https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/101/1ab7c5a78c4cae5b164444b9a1922436-2015-11-26_16.59.38.jpg?1448553813https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/102/ad6c9d5f29c2398fad98222cd219b0a8-2015-11-26_16.59.59.jpg?1448553813https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/103/4dfbcbedd4d24cb1cbd5f1b183300bb0-2015-11-26_17.00.48.jpg?1448553813https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/104/f7b0e68f423bda8b68bc47b312e2124a-2015-11-26_17.01.06.jpg?1448553813RossmannMarkt 6https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/105/a96bdf82f3d271e9b4cf7c529e0384f2-IMG_1268.JPG?1448554107https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/106/09e93da6617e5f276b7d6a274f88a642-IMG_1267.JPG?1448554107https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/107/df53ec0d889f9feda099c89daf6472e6-IMG_1265.JPG?1448554107https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/108/b31da5e551a60336126ef620e69a935c-IMG_1264.JPG?1448554107https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/109/9726c614a516e9d40430b6ced20a57fa-IMG_1266.JPG?144855410753934494Rigaer Str. 6710246Berlinhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/174/843c3e1849206b3106b217bd2274e401-WP_20151127_08_58_03_Pro.jpg?1448611815https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/175/2eb853cab086fd17defdd68633f8a484-WP_20151127_08_58_23_Pro.jpg?1448611815https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/176/de7a9f9434354d2ddf107c1270782570-WP_20151127_09_00_08_Pro.jpg?1448611815https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/177/65da3f0af9b7fb48057d3cb4ab524542-WP_20151127_09_06_05_Pro.jpg?1448611815https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/178/f4fd8c8889b083ca4adb13fed2ee7ec2-WP_20151127_09_00_32_Pro.jpg?14486118155393448152.5156;13.4685Kaufland Ladestrasse 130890 Barsinghausenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/206/c4a33d1c8b56a45c77737a91ceb8d6e9-IMAG0108.jpg?1448626435https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/207/27c22a2248f2c083dc996dd014342f16-IMAG0103.jpg?1448626435https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/208/3d710fd040ff2423db05fffa7adf35e9-IMAG0102.jpg?1448626435https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/209/963bcc858844afd9c30c012461464fc0-IMAG0100.jpg?1448626435https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/213/5ffc739a901a4b2c04bb3b1cc2ea2044-IMAG0104.jpg?1448626435https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/214/54dcd60f8333fab10acdcdcc44d08675-IMAG0107.jpg?14486264355393444652.3679;9.73154https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/212/dd1cf5f966ea82636b51bc028e5fae6e-IMAG0105.jpg?1448626435Schwarzwaldstrasse 7879117 Freiburg https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/220/335e7b97b5aa3032f4c37cbeab1d788a-1448626985124-1147602018.jpg?1448627458https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/221/90bc2ef8ded55c069f0eb63cd93f34f1-1448627047330-20168316.jpg?1448627458https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/222/d0b3137dfa25efe4823e209bc70d190b-14486270812692117723915.jpg?1448627458https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/223/82e55adddfd1286cff5b02f00fe3f2ff-14486271369421287853112.jpg?1448627458https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/224/b32a13d154285a5f0133ba159d68083d-14486271560571502819166.jpg?14486274585393875147.9884;7.86927Lorenzstrae 25-2995028Hofhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/225/d69f8f92e4a674df5b08a3d786db3ad0-image.jpg?1448634039https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/226/739723c12a6e21dcd5d5b8422e410db1-image.jpg?1448634039https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/227/f3998f4a8c32047442cd2836c5329ded-image.jpg?1448634039https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/228/7518375ea8018d61885523664259b039-image.jpg?1448634040https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/229/273ae7f091e75301fddbbbfc1dac3e22-image.jpg?14486340405393455550.3191;11.9216berseeboulevard 4-1020457Hamburghttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/230/e52b0c83d11662afea76c996e834b8d3-IMG_3908.JPG?1448644151https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/231/fbd167c7ad3496fa9f09db27ea8ffc55-IMG_3910.JPG?1448644151https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/232/fcf8c8f0ed7cbb0dccfb59e8829f11c4-IMG_3911.JPG?1448644151https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/233/d83d8dbdfa2d8702ad38320b1f39750e-IMG_3912.JPG?1448644151https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/234/d0fde4e28406d333560ce4ddbaa0c06a-IMG_3914.JPG?1448644151https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/235/4b66454c6387bc02abe432973b0061e8-IMG_3915.JPG?14486441515393448453.5381;10.0334Edeka Lemkestr. 1031787Hamelnhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/244/4e0f990cc9301dc3b09868948e1bb75e-IMG_20151128_160648.jpg?1448726436https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/245/a5753ba147673dc856a945ea4b507803-IMG_20151128_160755.jpg?1448726436https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/246/75f41da9eae566778669332f47635788-IMG_20151128_160828.jpg?1448726436https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/247/722ba3a5f7bcecc8999b74a2f2ac9da6-IMG_20151128_160808.jpg?1448726436https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/248/4e597124207966416676b51a950b16dd-IMG_20151128_160835.jpg?14487264365393445252.1228;9.34537Leimbacher Str. 16https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/254/59c370d7d296434b635c0c8d6be50226-IMG_1281.JPG?1448732989https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/255/179feb130161a857f1e6e8c5955173bc-IMG_1280.JPG?1448732989https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/256/52860e2d0cae9161df3640a3a67ceb9b-IMG_1276.JPG?1448732989https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/257/c7b48fb0e695555fea281edb25c874e8-IMG_1275.JPG?1448732989https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/258/f8bdc2329ff7201eb748f4b6a3b87e38-IMG_1277.JPG?1448732989< 53934495 50.8102;10.22Osterstr. 42-4331785https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/259/ab82be4353221febfa09c08341cfefa5-IMG_20151128_181528.jpg?1448738610https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/260/24e4c05f564ca157f4139f157b478a6e-IMG_20151128_181552.jpg?1448738610https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/261/8cc512c63219c973f97664044e19c4e3-IMG_20151128_181600.jpg?1448738610https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/262/a82a3c23ef200f889ed65e2a85ebfcfb-IMG_20151128_181613.jpg?1448738610https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/263/7b4dee4d3296ab73e6132e8656d3d29a-IMG_20151128_181732.jpg?14487386105393445852.1099;9.3633 Pferdemarkt 1https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/264/4c67904ccd709c2cc1a26bf939eaefab-IMG_20151128_184528.jpg?1448738923https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/265/926e39f089f900e6c63541f2062548b7-IMG_20151128_184507.jpg?1448738923https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/266/79297ec606011cb61e9567d99634b828-IMG_20151128_184157.jpg?1448738923https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/267/de860bac552a79e06c17446937aab160-IMG_20151128_184240.jpg?1448738923https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/268/ca239da4088f30dc430d6ab0ecc75997-IMG_20151128_184232.jpg?14487389235393446352.1099;9.36333https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/269/9feff0c31badb20c1bfa39c01b7d6a97-IMG_20151128_191821.jpg?1448739311https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/270/53275c45f997eae8e2153930779f22f9-IMG_20151128_191805.jpg?1448739311https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/271/bbe9964838a963340cb77ee9aee55eb9-IMG_20151128_191523.jpg?1448739311https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/272/1c304ec49a76d9b47acf3e41da801046-IMG_20151128_191001.jpg?1448739311https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/273/acae60e199f2f699ce56bbb111a8ee31-IMG_20151128_190953.jpg?144873931153934473 Kapellenatr 652066Aachenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/290/3d15f9a9e4f607319ef753e50ad36ca0-image.jpeg?1448974731https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/291/3d15f9a9e4f607319ef753e50ad36ca0-image.jpeg?1448974731https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/292/87c2c901b0ca347b99bd0ad1d53bdfda-image.jpeg?1448974731https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/293/0ef0ffbe01fb6d9ef707359e7b90810e-image.jpeg?1448974731https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/294/af9ab89e379e0874db260b7b3c9b66d7-image.jpeg?1448974731https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/295/3eeef05fdefccba4a427fe18732290a8-image.jpeg?14489747315399328650.7612;6.09299Kirchstrae 9647198Duisburghttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/307/8bf055ac9336800264e4db2fff5b9315-20151201_174943.jpg?1448992234https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/308/8bf055ac9336800264e4db2fff5b9315-20151201_174943.jpg?1448992234https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/309/b6f3e6a80fffa1900d1a77e034dbfc03-20151201_175038.jpg?1448992234https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/310/eddf8f31d1a936816da19c44cc653db4-20151201_175105.jpg?1448992234https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/311/491fe51ce88780e18c7e68ffa443c59c-20151201_175124.jpg?14489922345399326651.4621;6.65455 Benzstrae 771272 Renningenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/347/55451e5bcfca517af8689bb0040af6dd-image.jpeg?1449146032https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/348/2a03bfc767c314a1997d66f8b25d5758-image.jpeg?1449146032https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/349/47578cac5d780508174aef0b38e1a923-image.jpeg?1449146032https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/350/4fd55f6597bececc39e43016a6828d23-image.jpeg?1449146032https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/351/9cb5a9ed49c041a24d6bdbe2c03af687-image.jpeg?14491460325408717648.7775;8.9334 Heidering 1006120 Halle/ Saalehttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/358/b8903f240f0ddad60282a6f6e9277878-IMG_20151203_125706.jpg?1449146356https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/359/1d6a91df01aa9584172f80d33891acdb-IMG_20151203_125824.jpg?1449146356https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/360/c675f0f02a7613b57a484f9dc42e56fe-IMG_20151203_125854.jpg?1449146356https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/361/825e1c6b67875f3a6ffe1150e180b1c7-IMG_20151203_125921.jpg?1449146356https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/362/76d9649d44991b8f23f8ac306a99ef6a-IMG_20151203_125957.jpg?1449146356https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/363/ce3e02c5d30c278ef73ead24bcb38a7d-IMG_20151203_130145.jpg?14491463565408720251.5178;11.9159 L15 10-1168161Mannheimhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/413/d136ee5f169b82d8f42fd9c744c40be7-1449159905529-113008358.jpg?1449160134https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/414/eda731332850d2830b20ce4632b10710-1449159930294-1100544130.jpg?1449160134https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/415/e294df9f9deb35c6b9bd3fc80815b755-1449159962738-1711893879.jpg?1449160134https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/416/a73eb1ae3868a531b3258e3519ab2a0d-1449159988365-1592724700.jpg?1449160134https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/417/11186ae41521e6f0d15cc8a73f010950-1449160014564-2031061473.jpg?1449160134https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/418/b4a2dac04e5d45f976784a77f8e301c9-14491600393661773907961.jpg?14491601345408725649.4813;8.47055Beuerbacher Landstr. 165520 Bad Camberghttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/437/2fbfa96eaef46b3131ef61b243701b90-WP_20151203_19_40_58_Raw.jpg?1449168591https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/438/2fbfa96eaef46b3131ef61b243701b90-WP_20151203_19_40_58_Raw.jpg?1449168591https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/439/5277945c8bd2f332791a4d684ad88a16-WP_20151203_19_41_15_Raw.jpg?1449168591https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/440/e724b75994aebd21d5cef6b26df4f327-WP_20151203_19_42_08_Raw.jpg?1449168592https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/441/36046f9ce21e949e920435935edec7a3-WP_20151203_19_41_25_Raw.jpg?14491685925408729650.2949;8.25398 Lindenstr. 453489Sinzighttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/452/152df636a7cac1af16bc3ba90dc8cfd7-2015-12-04_09.04.09.jpg?1449216483https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/453/6267741e9a8db16d62913da92a254dc9-2015-12-04_09.04.27.jpg?1449216483https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/454/e1e1d12c4a5eb70ae0e08d183af9db67-2015-12-04_09.04.45.jpg?1449216483https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/455/a7ce48d73d31b9d670cb99d823ae41f6-2015-12-04_09.04.59.jpg?1449216483https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/456/797cd8e6c6ffc4c23251d22d6b03e080-2015-12-04_09.05.16.jpg?14492164835408735750.5384;7.2613Industriestrasse 10https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/457/e6e2c8a30d073< 8307369d63ec3d2d7ee-tmp_18644-20151204_092816-674526260.jpg?1449218396https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/458/e6e2c8a30d0738307369d63ec3d2d7ee-tmp_18644-20151204_092816-601823544.jpg?1449218396https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/459/c041dd3b4178fb5006cc595ffa4e258c-tmp_18644-20151204_092849-1015446586.jpg?1449218396https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/460/e3d98c6c6a8968b16f80eb3a516a1762-tmp_18644-20151204_092830-1258134960.jpg?1449218396https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/461/02fc98a00f515c07c5e65f8af0fef111-tmp_18644-20151204_092842-903837381.jpg?1449218396Bruckenstrasse 8358513 Ldenscheidhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/484/02f31f8d0c83f2a75d696a620b79bda7-1449226868153191735792.jpg?1449226900https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/485/61de55f17b901fcc88841c4ab37d0586-1449226718211-972948836.jpg?1449226900https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/486/e6eb1c2928b414ce0a091800e005056a-1449226734472-671242624.jpg?1449226900https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/487/8738f44fc1113182bee66973f3774863-1449226746134-1630274132.jpg?1449226900https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/488/ed31e14a10bd429918b6fad9a894ba84-1449226775759-610812088.jpg?1449226900https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/489/e0dde8b36bf539b79d1e5b1bf3bb692a-1449226787812-2044772493.jpg?14492269005408722551.2344;7.69746Grunerstrasse 2010179https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/539/3cc74bcd49eec6b4c5988bb1f7473434-image.jpeg?1449253081https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/540/ae2f8648e7d2fa71a279de62ffcebaaa-image.jpeg?1449253081https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/541/4ec50b26e969ccfb5d6a9d05e33bc1cf-image.jpeg?1449253081https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/542/ac589be934f19fce8ac3cd3ebd731d94-image.jpeg?1449253081https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/543/7d9fc1397d6f94fe66fd59939569b1e1-image.jpeg?1449253081https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/544/b5945d679c14afc9ec413906b3b1aa0e-image.jpeg?14492530815408719952.5213;13.3874 Weienauer Str. 1588212 Ravensburghttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/545/fd5474ba7ffef92b16a94701011eeec9-%C3%BCbersicht.png?1449261021https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/546/fd5474ba7ffef92b16a94701011eeec9-%C3%BCbersicht.png?1449261021https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/547/9432bbd2c136c4fb8714dc0c37ca9fcd-Head_.png?1449261021https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/548/3a955797f85df96e268599dc55484be1-pantene.png?1449261021https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/549/f7deabac453d715ccd9414dc01a0f22e-elvtial.png?1449261021https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/550/6fb16bbbef7f0ebcaf303851293e9492-image.png?14492610215408733347.8286;9.64825 Werkstr. 1731789https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/551/a6a10cad52f0893b1da37f29a4a19551-IMG_20151204_193047.jpg?1449262054https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/552/10d6f6f07201934518d0dbbeeb700d76-IMG_20151204_193057.jpg?1449262054https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/553/32d87dc661993a1cf7a17a2a49d5a881-IMG_20151204_193012.jpg?1449262054https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/554/705c92896220bbbaa4dfdacd33671b54-IMG_20151204_193025.jpg?1449262054https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/555/b7830babc4e97f9fce2dfc9fc92b1294-IMG_20151204_193033.jpg?14492620545399324552.1099;9.36331Mnsterstrae 7a46397Bocholthttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/577/f93119d0e6526b4b1987649cdee1b7de-WP_20151205_11_52_24_Pro.jpg?1449313544https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/578/f93119d0e6526b4b1987649cdee1b7de-WP_20151205_11_52_24_Pro.jpg?1449313544https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/579/8a7f7cf8c7d87d7ddfa7c4991bfcb8ad-WP_20151205_12_00_26_Pro.jpg?1449313544https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/580/10d9e30cc67e49a98a86014f4395d4f3-WP_20151205_12_03_49_Pro.jpg?1449313544https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/581/c0e272598927c332a7bdd3c82a4b0a2b-WP_20151205_11_59_14_Pro.jpg?14493135445408722051.8395;6.62296 Sdstrae 6266386 St.Ingberthttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/627/2d8a34fbde1526e62267d1ed85bda949-20151205_174930.jpg?1449338488https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/628/2d8a34fbde1526e62267d1ed85bda949-20151205_174930.jpg?1449338488https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/629/99364e74e021ce31b02bf8fb6b22c182-20151205_174940.jpg?1449338488https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/630/06de77cab0486bb97969b0c720b219c2-20151205_175043.jpg?1449338488https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/631/4d7ab452e0aaeb78bcf8d9dd7eeebc34-20151205_175209.jpg?14493384885408735949.2668;7.12276Breite Strae 1323552Lbeckhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/632/818db61013312dce83ed34b7fcdfb0b5-WP_20151205_13_00_31_Pro.jpg?1449342679https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/633/faa385792a1ecf7f67daf09f33a929d8-WP_20151205_12_59_25_Pro.jpg?1449342679https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/634/a9669994a096ddd08048937878fc7b83-WP_20151205_13_00_37_Pro.jpg?1449342679https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/635/d819774d9b5132a489601d096b9f42d4-WP_20151205_13_01_06_Pro.jpg?1449342679https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/636/57deed7f9e15dc5d65470787012dc0d1-WP_20151205_13_01_15_Pro.jpg?14493426795410518353.8606;10.6703Falkenseer Chaussee 19813589https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/654/dc5766c52ea1b07d20c8118baa8df036-WP_20151208_001.jpg?1449584290https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/655/c14b46477dbc70fe8f69fe550f813a62-WP_20151208_005.jpg?1449584290https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/656/9fb143c08c84c84aa56539574037026f-WP_20151208_002.jpg?1449584290https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/657/39a5498645889cddde71955f496d81ab-WP_20151208_004.jpg?1449584290https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/658/ca697fa38831daef7273d881d9619a22-WP_20151208_006.jpg?1449584290https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/659/505f169925eb0d23497562f08a52bd37-WP_20151208_007.jpg?14495842905410480552.5488;13.1615Pforzheimerstrasse 36070499 Stuttgarthttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/671/73d7ab82cbadd72f60b4f05964620855-image.jpg?1449679015https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/672/982a24c8b2372e96701cb1c5a7ff5139-image.jpg?1449679015https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/673/5b042c510d74a62f4b6af468986ba377-image.jpg?1449679015https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/674/d0b60cb938712d0f81bc9971955417e1-image.jpg?1449679015https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/675/5214fdc62e08e5030ad856477807e2c1-image.jpg?1449679015https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/676/00< 163940d077d8c6ca82beb518c8bb03-image.jpg?14496790155420302148.8138;9.11468Sankt-Pltener-Strae 29 70469https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/682/a5adbb0528c724d7ade6146c87e68f6c-image.jpg?1449680044https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/683/c9d9aefce11c59bb8343195ee70c7b36-image.jpg?1449680044https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/684/b62bfd186fe2fc9493eefef7bf5b7657-image.jpg?1449680044https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/685/7c9f4a3dd9b0e40a04db514404c50032-image.jpg?1449680044https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/686/8e42a232590520a81887feea0bc2a523-image.jpg?1449680044https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/687/4238feee1daec0cc062796e8ee795d7f-image.jpg?14496800445420302248.8099;9.16004Hcklinger Weg 6621335 Lneburg https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/768/519104dbabe2ec46476d401bdfe0d87c-image.jpg?1449747088https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/769/43eb9c755c2e7a5303d569124444f378-image.jpg?1449747088https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/770/9bb8b9e27566b5271995226bcf7e2d3a-image.jpg?1449747088https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/771/1fd76485daccf70ecc6f37c0af378edd-image.jpg?1449747088https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/772/c5e74a9a2cdefcc7c00c018bf6547279-image.jpg?14497470885420314153.2216;10.3845'Rathaus-Galerie Friedrich-Ebert Platz 151373 Leverkusen https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/773/91569869649e9ebdf697a5b73b6071d2-image.jpeg?1449747619https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/774/91569869649e9ebdf697a5b73b6071d2-image.jpeg?1449747619https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/775/2687f918af44b65a6035e1d327c8c710-image.jpeg?1449747619https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/776/434a01097f12f449dc2c0f2b4cd35037-image.jpeg?1449747619https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/777/5929b414d82a997821242a54a096e0ba-image.jpeg?1449747619https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/778/6d4d84bbdb3e71ec76143633a0325538-image.jpeg?14497476195420319951.0925;7.00606Am Oberfeld 3 a86707 Westendorfhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/785/3bb4b254fb42669d2d4f635c3b2b175f-IMG_5214.JPG?1449751961https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/786/0da52f1d06c8b80748edcc2f6dee6b7b-IMG_5215.JPG?1449751961https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/787/4edb9b916f374dcf478e0998388ef5af-IMG_5219.JPG?1449751961https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/788/6106c5ca50a6321624f91c1680096f6e-IMG_5218.JPG?1449751961https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/789/7c78c909542186da2242986c606eeb64-IMG_5220.JPG?144975196154203213 48.53;10.8564 Krmerstr. 1a17034Neubrandenburg https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/790/31435ae8d19c2981cae01acfae9cafa7-image.jpg?1449753223https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/791/31435ae8d19c2981cae01acfae9cafa7-image.jpg?1449753223https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/792/fa290cd0e2a3024006ce127388cbe726-image.jpg?1449753223https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/793/e2c85ce2fe90a8354e1204f3139b1c6f-image.jpg?1449753223https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/794/5d375a74f368beec01e475b888c47187-image.jpg?14497532235420304453.5566;13.2523Grindelallee 12620146https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/795/2ffd6441a3b9f37b6d50fa44e36ef050-DSC_0096_1_.jpg?1449753449https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/796/b5bcc412fa133b3d45d9e0b0a13dde99-DSC_0098_1_.jpg?1449753449https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/797/2bf3a1252d91ef2e2ca6c61d4ee6b28a-DSC_0100_1_.jpg?1449753449https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/798/405b789e072c29ebe12a4227b687d3a0-DSC_0099_1_.jpg?1449753449https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/799/78331ce623b40c2481830fe30c3c4af4-DSC_0101_1_.jpg?14497534495420315953.5688;9.98198Ernst-Reuter-Allee 1139104 Magdeburghttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/842/5b626aa585d75349c8e199891bc55a49-1449755773919.jpg?1449756220https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/843/d387567a479bd05303ad27f4189717b3-1449755792721.jpg?1449756220https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/844/9bcec69fd064684b3b435ccbf22e611e-1449755814528.jpg?1449756220https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/845/6d1c5bf033fd51641646f209a86e9341-1449755835100.jpg?1449756220https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/846/a67c1797345bcaf3ea3031df02cf1945-1449755853222.jpg?14497562205420313452.1299;11.637 eugenstr. 3972072Tbingenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/891/2d2179fb1ade6b56dd9fa2dcc06a95ea-WP_20151210_17_53_01_Pro.jpg?1449768437https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/892/4902e0bb48f91131f6caf7144aeae775-WP_20151210_17_52_37_Pro.jpg?1449768437https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/893/1b0f227234e0402cfc19b85dae39d311-WP_20151210_17_54_24_Pro.jpg?1449768437https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/894/1be76939191928ad83b309d74b50b818-WP_20151210_17_53_20_Pro.jpg?1449768437https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/895/db90b4f0a1be50e11978ae5bec83fd5e-WP_20151210_17_53_56_Pro.jpg?14497684375420320548.5086;9.07434Aschaffenburger Strae 1863825 Schllkrippenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/907/64eb221d5e9016e136a6ac2aa1a66973-WP_20151210_002.jpg?1449773406https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/908/f7d15c108affe8fcfc56cb449c8597b0-WP_20151210_001.jpg?1449773406https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/909/185dcd98cc34e0e1377c3bb8420abe24-WP_20151210_003.jpg?1449773406https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/910/f4fc8930aa99de5379cc4048f4e6ef69-WP_20151210_004.jpg?1449773406https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/911/5c9a3174ea5345c716c29364b5e22441-WP_20151210_005.jpg?1449773406https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/912/246adc162b498f666a1d7a9f613499cc-WP_20151210_006.jpg?14497734065422341650.0841;9.24578Bonhoefferstrae 273760 Ostfildernhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/936/34e14c4020462f25d83b65f37b2b08f7-image.jpeg?1449790996https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/937/835aef1addc4458a3c62bba2ede33455-image.jpeg?1449790996https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/938/8e87bede2049291cb69b08b3305f18b8-image.jpeg?1449790996https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/939/b6120c8da37b29ee0aa10d49564922f2-image.jpeg?1449790996https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/940/f47b2cf15b4af08a3ad1cd61340db476-image.jpeg?1449790996https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/941/0bbc0db62c7bc0d96e7c28f44d682b4e-image.jpeg?14497909965420318148.7283;9.14193StatusacceptedKirchstrasse 79412 Emmendingen.~https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/66/d3215f1325928ff1ba83f13743f2< 70e6-image.jpg?1448552031~https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/67/9861b80693552752838e35cad56c42f2-image.jpg?1448552031~https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/68/d2cbd6c3c8690f24c4dd682be9763cc1-image.jpg?1448552031~https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/69/70e099de52cc2c1515f12c5e8e599402-image.jpg?1448552031~https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/70/eff217daf3d0bcc4a5770d12c4a861f6-image.jpg?1448552031~https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/71/eb69198f75a746c6d79e6a9dbdfc36f2-image.jpg?144855203153934519Lange Strae 563741 Aschaffenburghttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/72/b8d583398c62a2bcad0f230a0416a9fa-WP_20151126_002_1_.jpg?1448552051https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/73/5b67645a311b51307af3d8a72a243a8c-WP_20151126_008_1_.jpg?1448552051https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/74/1a2b522a252d30b0b6ac1954f7caae59-WP_20151126_004_1_.jpg?1448552051https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/75/e761bb81d9d3f90ee7de57992ab18990-WP_20151126_007_1_.jpg?1448552051https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/76/87cf8af719b439693702b4f48adc8357-WP_20151126_010_2_.jpg?144855205153934496https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/77/b8d583398c62a2bcad0f230a0416a9fa-WP_20151126_002_1_.jpg?1448552649https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/78/baf594154d9053289d57bd0195ef9936-WP_20151126_005_1_.jpg?1448552649https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/79/1a2b522a252d30b0b6ac1954f7caae59-WP_20151126_004_2_.jpg?1448552649https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/80/e761bb81d9d3f90ee7de57992ab18990-WP_20151126_007_2_.jpg?1448552649https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/81/87cf8af719b439693702b4f48adc8357-WP_20151126_010_1_.jpg?144855264953934537https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/82/b8d583398c62a2bcad0f230a0416a9fa-WP_20151126_002_1_.jpg?1448553280https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/83/87cf8af719b439693702b4f48adc8357-WP_20151126_010_1_.jpg?1448553280https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/84/1a2b522a252d30b0b6ac1954f7caae59-WP_20151126_004_1_.jpg?1448553280https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/85/5b67645a311b51307af3d8a72a243a8c-WP_20151126_008_1_.jpg?1448553280https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/86/e761bb81d9d3f90ee7de57992ab18990-WP_20151126_007_1_.jpg?1448553280https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/87/baf594154d9053289d57bd0195ef9936-WP_20151126_005_1_.jpg?144855328053934535https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/88/baf594154d9053289d57bd0195ef9936-WP_20151126_005_1_.jpg?1448553604https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/89/b8d583398c62a2bcad0f230a0416a9fa-WP_20151126_002_2_.jpg?1448553604https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/90/1a2b522a252d30b0b6ac1954f7caae59-WP_20151126_004_1_.jpg?1448553604https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/91/5b67645a311b51307af3d8a72a243a8c-WP_20151126_008_1_.jpg?1448553604https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/92/e761bb81d9d3f90ee7de57992ab18990-WP_20151126_007_1_.jpg?1448553604https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/93/87cf8af719b439693702b4f48adc8357-WP_20151126_010_1_.jpg?144855360453934527 Mhlstein 306682Teuchernhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/110/e190ca8e5395181ca1763e3467bf004c-20151126_163358.jpg?1448554313https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/111/ef6a9b2d1c62b002e05625eeea278ffc-20151126_163845.jpg?1448554313https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/112/385df0bf7cf48fb95c7b2736bc89b02f-20151126_163658.jpg?1448554313https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/113/ef6a9b2d1c62b002e05625eeea278ffc-20151126_163845.jpg?1448554313https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/114/9ea67e302734e96d9eb81821f8a1eb91-20151126_163703.jpg?1448554313https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/115/2b873a1c5fcc1c41ed0ef6e63662d235-20151126_163737.jpg?144855431353934525 Mhlstraehttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/116/e190ca8e5395181ca1763e3467bf004c-20151126_163358.jpg?1448554893https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/117/2b873a1c5fcc1c41ed0ef6e63662d235-20151126_163737.jpg?1448554893https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/118/385df0bf7cf48fb95c7b2736bc89b02f-20151126_163658.jpg?1448554893https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/119/ef6a9b2d1c62b002e05625eeea278ffc-20151126_163845.jpg?1448554893https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/120/9ea67e302734e96d9eb81821f8a1eb91-20151126_163703.jpg?1448554893https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/121/a34bdb1b1ae801ac2482d1d6246d529f-20151126_163649.jpg?144855489353934524 Irisstrae 6909110Frthhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/122/dd9d2231fb75db3df5760c710bb2eafa-mueller_3.jpg?1448554971https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/123/dd9d2231fb75db3df5760c710bb2eafa-mueller_3.jpg?1448554971https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/124/dd9d2231fb75db3df5760c710bb2eafa-mueller_3.jpg?1448554971https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/125/dd9d2231fb75db3df5760c710bb2eafa-mueller_3.jpg?1448554971https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/126/dd9d2231fb75db3df5760c710bb2eafa-mueller_3.jpg?1448554971https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/127/dd9d2231fb75db3df5760c710bb2eafa-mueller_3.jpg?144855497153934442https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/128/dd9d2231fb75db3df5760c710bb2eafa-mueller_3.jpg?1448555199https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/129/0c566e60372bf9ac0a548bc6ee9517f3-image.jpg?1448555199https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/130/93ab1306d9d18e01725e145c13506511-head-and-shoulders-shampoo-9-size-3.jpg?1448555199https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/131/0c566e60372bf9ac0a548bc6ee9517f3-image.jpg?1448555199https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/132/0c566e60372bf9ac0a548bc6ee9517f3-image.jpg?1448555199Reutlingenstr.672555 Metzingenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/133/6e571ba17d8ba16048708ccb29b675d1-1448555638356-814986118.jpg?1448556095https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/134/93d45408e3fa785ddfabea2ba6047bf6-14485557269642128504313.jpg?1448556095https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/135/4b9adc96f5f426f9f3580f7084977ae5-14485559034361372979143.jpg?1448556095https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/136/5b8d3330debc6d9fc649b993e730cbe1-1448555859819376031377.jpg?1448556095https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/137/232045cb38787c4f8441988ee3c55082-1448555932928-657408679.jpg?144855609553934547 Nuthestr 1-1014480Potsdam https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/138/ed99f2fdf8835baab9e61ba27322ebbb-image.jpeg?1448557562https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/139/140be3229b1dfd6d8dcbf67< 637ef288e-image.jpeg?1448557562https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/140/f81c548930546a8fdfc88eae2b2464ca-image.jpeg?1448557562https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/141/fba435b8f72116cd027aa710c1b6ceac-image.jpeg?1448557562https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/144/2b97e29879278f6eb69fd415efaf534d-image.jpeg?144855756253934554https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/143/cdce17c650fb16732a1bddb925b6952d-image.jpeg?1448557562 Tschirchdamm Brandenburghttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/145/fce71a7a70ee856068bb0bd6a2032f67-DSC_0114.JPG?1448558361https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/146/fce71a7a70ee856068bb0bd6a2032f67-DSC_0114.JPG?1448558361https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/147/b7d1e057cbb09c66ca62aa9667d2a3e8-DSC_0115.JPG?1448558361https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/148/4af75386936a33d55c903e9f69cc8a50-DSC_0116.JPG?1448558361https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/149/cee242356c189b725b1f574330db5354-DSC_0117.JPG?1448558361Reutlinger Str. 6, 72555 https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/150/93d45408e3fa785ddfabea2ba6047bf6-14485557269642128504313.jpg?1448558378https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/151/6e571ba17d8ba16048708ccb29b675d1-1448555638356-814986118.jpg?1448558378https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/152/4b9adc96f5f426f9f3580f7084977ae5-14485559034361372979143.jpg?1448558378https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/153/5b8d3330debc6d9fc649b993e730cbe1-1448555859819376031377.jpg?1448558378https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/154/4b9adc96f5f426f9f3580f7084977ae5-14485559034361372979143.jpg?1448558378https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/155/6e571ba17d8ba16048708ccb29b675d1-1448555638356-814986118.jpg?1448558378Schneeberger Strae 108107 Kirchberghttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/156/f3a44d843184cedbb42f64aaf46c5813-20151126_191130.jpg?1448563202https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/157/9a8a2862abdb387b22123be68c969580-20151126_191006.jpg?1448563202https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/158/8952fa2691df900fbd619f33ada7779e-20151126_191023.jpg?1448563202https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/159/7ae69da848343196100b50628259681e-20151126_190952.jpg?1448563202https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/160/f18e17ad4292589b5ba4ea5ccf26c696-20151126_190911.jpg?1448563202https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/161/cf01d13a1e36c1f88fd8b61e34ad61bc-20151126_190931.jpg?144856320253934461https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/162/8f46cc9c581025e9293bc4ff01d241e1-20151126_191006.jpg?1448563571https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/163/5049947b42195d08a3ed360f387dd283-20151126_191130.jpg?1448563571https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/164/c1a0a1335820703bba8fb86ecc42ed1f-20151126_190952.jpg?1448563571https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/165/d60be578ffa0aebefcc4bd87ebbeb146-20151126_191023.jpg?1448563571https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/166/360126d11e51270ade4e53ac0701f19f-20151126_190911.jpg?1448563571https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/167/685cd1764a1d79f720998f0949c77b48-20151126_190931.jpg?1448563571https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/168/ed99f2fdf8835baab9e61ba27322ebbb-image.jpeg?1448568991https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/169/f81c548930546a8fdfc88eae2b2464ca-image.jpeg?1448568991https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/170/fba435b8f72116cd027aa710c1b6ceac-image.jpeg?1448568991https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/171/312865afce950d8da0662314fea1ba78-image.jpeg?1448568991https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/173/2b97e29879278f6eb69fd415efaf534d-image.jpeg?1448568991https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/172/cdce17c650fb16732a1bddb925b6952d-image.jpeg?1448568991https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/180/c4a33d1c8b56a45c77737a91ceb8d6e9-IMAG0108.jpg?1448612469https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/181/6bcf4a31f9e468488edf30447b3bcc3b-IMAG0109.jpg?1448612469https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/182/3d710fd040ff2423db05fffa7adf35e9-IMAG0102.jpg?1448612469https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/183/6bcf4a31f9e468488edf30447b3bcc3b-IMAG0109.jpg?1448612469https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/186/e4d83db73e24dac5c3fa1db70f8a37e1-IMAG0106.jpg?1448612469https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/187/54dcd60f8333fab10acdcdcc44d08675-IMAG0107.jpg?1448612469https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/185/dd1cf5f966ea82636b51bc028e5fae6e-IMAG0105.jpg?144861246952.6367;9.84508https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/196/963bcc858844afd9c30c012461464fc0-IMAG0100.jpg?1448625876https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/197/622e10a692c5ab69a5ca2fd8ce1935de-IMAG0101.jpg?1448625876https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/198/3d710fd040ff2423db05fffa7adf35e9-IMAG0102.jpg?1448625876https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/199/963bcc858844afd9c30c012461464fc0-IMAG0100.jpg?1448625876https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/203/6bcf4a31f9e468488edf30447b3bcc3b-IMAG0109.jpg?1448625877https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/204/54dcd60f8333fab10acdcdcc44d08675-IMAG0107.jpg?1448625877https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/202/dd1cf5f966ea82636b51bc028e5fae6e-IMAG0105.jpg?144862587752.3686;9.72882Bahnhofstrae 194032Passau}https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/215/dc0ea553fb848b7fa97f655baaee4fb4-dm2.jpg?1448626975}https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/216/83452069fc05197053665e2972645ad9-dm3.jpg?1448626975}https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/217/d2367d223682d1d190eded6104e4b351-dm5.jpg?1448626975}https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/218/badbbeafb08519d7d79235191bf60eb7-dm4.jpg?1448626975}https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/219/d3bfee610cf5295dc28d8760dec02330-dm6.jpg?14486269755393531048.577;13.4627Forchheimer Str. 6191083 Baiersdorfhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/236/e0a66d2269acb1011df0a8615cd5002b-2015-11-27_10.11.44.jpg?1448650055https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/237/b3d29a83054cffa7043ea1e306e4325b-2015-11-27_10.12.10.jpg?1448650055https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/238/8672f0e41984f6153c9128f9eff48950-2015-11-27_10.13.23.jpg?1448650055https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/239/83bbcf1f23e5c26595f011e627dfd4e1-2015-11-27_10.12.48.jpg?1448650055https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/240/ecbd098afdccc61211d2e43acbf657fd-2015-11-27_10.12.56.jpg?1448650055https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/241/b6b5bb9ce80bc4e86f404e7530c432f9-2015-11-27_10.13.05.jpg?144865005553934552< 49.6342;11.0348Bahnhofstrae. 26https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/249/9e9354a857c2703a70819dc8381cffe2-IMG_8742.JPG?1448726855https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/250/7fac97ee3ed254de127e974e5d04819d-IMG_8741.JPG?1448726855https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/251/073c06b2681ae63f65b9d22b08a68fde-IMG_8749.JPG?1448726855https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/252/fb3457201cf66fdfd269e985cea0b4a8-IMG_8748.JPG?1448726855https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/253/bc9ddcb3cdb27be5b89af1f25ae7780d-IMG_8752.JPG?14487268555393445152.1099;9.36326https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/274/c2051bbb692508b26c2f9da2187422d7-tmp_11157-20151130_1742131035619873.jpg?1448904761https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/275/5cd529aa1359ca4a1f2eaa6b023d6ab0-tmp_11157-20151130_174013837273324.jpg?1448904761https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/276/547bacc9083a54344f519330a89575ad-tmp_11157-20151130_175010312995445.jpg?1448904761https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/277/03faf472710b29ba45ea85e52b6d86f5-tmp_11157-20151130_175004-2099788783.jpg?1448904761https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/278/ce11345bd80d66b78986dbf22576f4bf-tmp_11157-20151130_175019-2104533753.jpg?14489047615393458150.5384;7.26145https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/279/c4a33d1c8b56a45c77737a91ceb8d6e9-IMAG0108.jpg?1448961474https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/280/27c22a2248f2c083dc996dd014342f16-IMAG0103.jpg?1448961474https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/281/3d710fd040ff2423db05fffa7adf35e9-IMAG0102.jpg?1448961474https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/282/963bcc858844afd9c30c012461464fc0-IMAG0100.jpg?1448961474https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/283/e4d83db73e24dac5c3fa1db70f8a37e1-IMAG0106.jpg?1448961474https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/284/54dcd60f8333fab10acdcdcc44d08675-IMAG0107.jpg?14489614745399328152.3678;9.73195Mannheimer Landstrae 5b68782Brhl https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/285/e90792ee7f11ed3e9dd8dd9ece5e4e91-14489637212411299527379.jpg?1448965867https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/286/6910c8355e5469f0510f84a7d7adbe92-14489636661792109361262.jpg?1448965867https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/287/883ab725c4ae22364fb25407cfacd695-14489631446161800880723.jpg?1448965867https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/288/880b8052291ebc892211d19dd6fb095d-1448963270255-65662248.jpg?1448965867https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/289/3e77578d5954bebd68b4a1c211672fc3-1448963286750-556672382.jpg?14489658675399325549.3991;8.53525Schleissheimerstrasse 131 80798Mnchenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/296/ceb231e583354ee875fc5b09a725eaa4-1448965152869.jpg?1448986244https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/297/bc1d937bebe2884185ad71dde9465eeb-1448965845823.jpg?1448986244https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/298/2a402e9aa1e97b3e20860a8d2ed1468a-1448965950995.jpg?1448986244https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/299/650d820957c9d19f203423a5aeb3bcc5-1448966151432.jpg?1448986244https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/300/ec1965487d8f2c6376d8848c6a4362b5-1448966335726.jpg?1448986244https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/301/b4cb6b3b0f7d1e0557feea9d25cf4335-1448966260697.jpg?144898624453993264 48.15;11.5833 L15, 10-1168165https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/302/5bfac901e520378e4353ccd277149710-20151201_181446.jpg?1448991912https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/303/ffcb9cb08e172bd77d37eefc00d10a5a-20151201_181500.jpg?1448991912https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/304/6c18bfbb1b00e48bddef19daa8db74ca-20151201_181520.jpg?1448991912https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/305/1ea0fef5d45c46ca3e87406b776e78c7-20151201_181423.jpg?1448991912https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/306/d838cee301a82ff9fa36a0756e958620-20151201_181342.jpg?14489919125399327949.4859;8.47014Matthias-Hoeren-Platz 1-741352Korschenbroichhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/312/492caf9aaaf292d21cf324e94dcf6eeb-20151202_123044.jpg?1449056325https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/313/c89b89d620a3d11c041cc655fbbf78ab-20151202_122642.jpg?1449056325https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/314/180f3961485993727e8dfca7033e240f-20151202_122620.jpg?1449056325https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/315/3f4abd8825e6144e4eaaf5f8259dd76f-20151202_122730.jpg?1449056325https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/316/652d299ef11995284e109ddc90e06ac2-20151202_122710.jpg?1449056325https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/317/d4d5ce1c7189af96637a4036e91eea6a-20151202_122847.jpg?14490563255399338151.192;6.52441leonberger 98-10871229leonberghttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/318/65dbfdd08ea26195d1a7e3ac604141de-WP_20151202_18_42_55_Pro.jpg?1449079065https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/319/702e30a80dafeedc2d1049639678e76e-WP_20151202_18_38_33_Pro.jpg?1449079066https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/320/6a56cc27e20c6ebf3763721e7ea2cffe-WP_20151202_18_39_44_Pro.jpg?1449079066https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/321/08722464f3bd6efd38541e9796f3ab60-WP_20151202_18_40_03_Pro.jpg?1449079066https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/322/528a7a54be4d03bf426cfdf157dc609d-WP_20151202_18_40_59_Pro.jpg?1449079066https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/323/6a71be2c6bfdad9ce713bb1d4c3ef13c-WP_20151202_18_40_30_Pro.jpg?14490790665399328748.7947;9.01098Im Gewerbegebiet 4777839 Lichtenauhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/324/d9a3fb8076466a9161670e1cb50ef230-1449144342131.jpg?1449144571https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/325/605a2e0dcbd51eda90e152f0819573ef-1449144360142.jpg?1449144571https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/326/a2a9cbf82b9e953648a91692b76ae692-1449144312756.jpg?1449144571https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/327/453ca716a4b3f371465b7addb7d4ecaa-1449144295314.jpg?1449144571https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/328/3f9e10b5ecf72715b6154daf114d1367-1449144376352.jpg?1449144571https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/329/33de50d8a40d69496ee22fa68960df78-1449144409431.jpg?144914457148.7118;7.99919Heidering Passage 5https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/330/b8903f240f0ddad60282a6f6e9277878-IMG_20151203_125706.jpg?1449145737https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/331/1d6a91df01aa9584172f80d33891acdb-IMG_20151203_125824.jpg?1449145737https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/332/c675f0f02a7613b57a484f9dc42e56fe-IMG_20151203_125854.jpg?1449145737https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/333/825e1c6b67875f3a6ffe1150e180b1c7-IMG_20151203_125921.jpg?1449145737< https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/334/ce3e02c5d30c278ef73ead24bcb38a7d-IMG_20151203_130145.jpg?144914573754087186https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/335/b8903f240f0ddad60282a6f6e9277878-IMG_20151203_125706.jpg?1449145769https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/336/1d6a91df01aa9584172f80d33891acdb-IMG_20151203_125824.jpg?1449145769https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/337/c675f0f02a7613b57a484f9dc42e56fe-IMG_20151203_125854.jpg?1449145769https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/338/825e1c6b67875f3a6ffe1150e180b1c7-IMG_20151203_125921.jpg?1449145769https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/339/ce3e02c5d30c278ef73ead24bcb38a7d-IMG_20151203_130145.jpg?1449145769https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/340/ce3e02c5d30c278ef73ead24bcb38a7d-IMG_20151203_130145.jpg?1449145769https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/341/b8903f240f0ddad60282a6f6e9277878-IMG_20151203_125706.jpg?1449145828https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/342/1d6a91df01aa9584172f80d33891acdb-IMG_20151203_125824.jpg?1449145828https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/343/c675f0f02a7613b57a484f9dc42e56fe-IMG_20151203_125854.jpg?1449145828https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/344/825e1c6b67875f3a6ffe1150e180b1c7-IMG_20151203_125921.jpg?1449145828https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/345/ce3e02c5d30c278ef73ead24bcb38a7d-IMG_20151203_130145.jpg?1449145828https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/346/ce3e02c5d30c278ef73ead24bcb38a7d-IMG_20151203_130145.jpg?1449145828https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/352/b8903f240f0ddad60282a6f6e9277878-IMG_20151203_125706.jpg?1449146039https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/353/1d6a91df01aa9584172f80d33891acdb-IMG_20151203_125824.jpg?1449146039https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/354/c675f0f02a7613b57a484f9dc42e56fe-IMG_20151203_125854.jpg?1449146039https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/355/825e1c6b67875f3a6ffe1150e180b1c7-IMG_20151203_125921.jpg?1449146039https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/356/f53db9403e53e6f39eda9d46dd43611e-IMG_20151203_130053.jpg?1449146039https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/357/ce3e02c5d30c278ef73ead24bcb38a7d-IMG_20151203_130145.jpg?144914603954087210 Alter Markt 144866Bochumhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/364/8540b193707f0fef5388e7bad6297428-IMG_2170.JPG?1449149422https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/365/4b1ec277372c85042e8528cabcc9a551-IMG_2166.JPG?1449149422https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/366/33fa43ab5205218ce3ed72c567d8e7e6-IMG_2168.JPG?1449149422https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/367/4b1ec277372c85042e8528cabcc9a551-IMG_2166.JPG?1449149422https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/368/be294a29dc60614019b747adcf9aa0a9-IMG_2174.JPG?1449149422https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/369/2d8dca7b51bf149aed02a36ca0405842-IMG_2173.JPG?14491494225408717451.4522;7.14457 Bauhofstrae 3,04103Leipzighttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/370/5357b228bc24143b290618ea364bf0b4-image.jpg?1449151486https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/371/e74f5cfcf0922de5f9206277ddcdf500-image.jpg?1449151487https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/372/7e73a06d4966c7c825df46fed517d542-image.jpg?1449151487https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/373/0ca23cf40d886ce9846f528841d165d6-image.jpg?1449151487https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/374/a435612f7ebc87103219645e540378b4-image.jpg?14491514875408719051.331;12.3805https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/375/5357b228bc24143b290618ea364bf0b4-image.jpg?1449151710https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/376/e74f5cfcf0922de5f9206277ddcdf500-image.jpg?1449151710https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/377/7e73a06d4966c7c825df46fed517d542-image.jpg?1449151710https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/378/0ca23cf40d886ce9846f528841d165d6-image.jpg?1449151710https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/379/a435612f7ebc87103219645e540378b4-image.jpg?1449151710 heidering 10https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/380/b8903f240f0ddad60282a6f6e9277878-IMG_20151203_125706.jpg?1449152096https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/381/1d6a91df01aa9584172f80d33891acdb-IMG_20151203_125824.jpg?1449152096https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/382/c675f0f02a7613b57a484f9dc42e56fe-IMG_20151203_125854.jpg?1449152096https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/383/825e1c6b67875f3a6ffe1150e180b1c7-IMG_20151203_125921.jpg?1449152096https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/384/76d9649d44991b8f23f8ac306a99ef6a-IMG_20151203_125957.jpg?1449152096https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/385/ce3e02c5d30c278ef73ead24bcb38a7d-IMG_20151203_130145.jpg?144915209654087243Bauhofstrae 3https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/386/5357b228bc24143b290618ea364bf0b4-image.jpg?1449152676https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/387/e74f5cfcf0922de5f9206277ddcdf500-image.jpg?1449152676https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/388/7e73a06d4966c7c825df46fed517d542-image.jpg?1449152676https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/389/0ca23cf40d886ce9846f528841d165d6-image.jpg?1449152676https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/390/a435612f7ebc87103219645e540378b4-image.jpg?144915267651.3312;12.3802Leher Heerstrae 1/328359Bremenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/391/1f73c473eb8df32dcafbc403f621fc62-1449156444969-1817686526.jpg?1449156911https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/392/defa66dd6845d6a3613454edaa3ae814-14491565248631220343981.jpg?1449156911https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/393/bb263b2b9f03b56b3f805e0ccf8147fd-1449156311399466846672.jpg?1449156911https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/394/8ee77a79f6a61cab3868772db1f2cde9-1449156369903-1616203692.jpg?1449156911https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/395/cd26092dde8df501cc1b9ef8b38587af-1449156421722-1780267595.jpg?14491569115408723953.0979;8.87052Anderterstrae 1-330629 Hannover https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/396/5482bfa6634fd151baa5c4ff6bc66348-image.jpeg?1449158188https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/397/ee90463c3e1f10f66412c80351836acd-image.jpeg?1449158188https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/398/1242299d7191dc1aa5089ab5ec1fc44b-image.jpeg?1449158188https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/399/48c1e627b3fb10748ac4a27fc1a5b2c9-image.jpeg?1449158188https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/400/b244d763d1c7271bc223b0fa96282ae9-image.jpeg?14491581885408725752.3905;9.8540869161https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/401/6e11c012bf922ab6a06671ea0b88670d-144915893986< 6-1459473272.jpg?1449159431https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/402/e7a7f109ad805b2613d3b289c8dcb251-1449158998675-1429901999.jpg?1449159431https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/403/220f4aeac80464ae8d1bef77b48d07f3-14491590817391459522424.jpg?1449159431https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/404/da66924995502ccfdc5051e164566dcd-1449159122818-1891341219.jpg?1449159431https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/405/b286dd5f44c7f3752e2321e30b669b20-1449159243726390079732.jpg?1449159431https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/406/2159c930ad42053827edafd992525530-14491593644191189744429.jpg?14491594315408725449.4813;8.47097https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/407/6e11c012bf922ab6a06671ea0b88670d-1449158939866-1459473272.jpg?1449159676https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/408/e7a7f109ad805b2613d3b289c8dcb251-1449158998675-1429901999.jpg?1449159676https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/409/220f4aeac80464ae8d1bef77b48d07f3-14491590817391459522424.jpg?1449159676https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/410/da66924995502ccfdc5051e164566dcd-1449159122818-1891341219.jpg?1449159676https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/411/b286dd5f44c7f3752e2321e30b669b20-1449159243726390079732.jpg?1449159676https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/412/2159c930ad42053827edafd992525530-14491593644191189744429.jpg?144915967649.4814;8.4708991083 Baiersdorfhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/419/e0a66d2269acb1011df0a8615cd5002b-2015-11-27_10.11.44.jpg?1449165455https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/420/b3d29a83054cffa7043ea1e306e4325b-2015-11-27_10.12.10.jpg?1449165460https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/421/8672f0e41984f6153c9128f9eff48950-2015-11-27_10.13.23.jpg?1449165464https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/422/83bbcf1f23e5c26595f011e627dfd4e1-2015-11-27_10.12.48.jpg?1449165465https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/423/b6b5bb9ce80bc4e86f404e7530c432f9-2015-11-27_10.13.05.jpg?1449165467https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/424/ecbd098afdccc61211d2e43acbf657fd-2015-11-27_10.12.56.jpg?14491654675408728949.6342;11.034991083 https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/425/e0a66d2269acb1011df0a8615cd5002b-2015-11-27_10.11.44.jpg?1449165514https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/426/b3d29a83054cffa7043ea1e306e4325b-2015-11-27_10.12.10.jpg?1449165514https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/427/8672f0e41984f6153c9128f9eff48950-2015-11-27_10.13.23.jpg?1449165514https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/428/83bbcf1f23e5c26595f011e627dfd4e1-2015-11-27_10.12.48.jpg?1449165514https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/429/b6b5bb9ce80bc4e86f404e7530c432f9-2015-11-27_10.13.05.jpg?1449165514https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/430/ecbd098afdccc61211d2e43acbf657fd-2015-11-27_10.12.56.jpg?1449165514 Talstr. 1076337Waldbronn-Reichenbachhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/431/fe67224a29185d6287496fd52db45a0c-20151203_191043.jpg?1449166849https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/432/e4753fec68fe5f6f3f08236e8896ba0e-20151203_191103.jpg?1449166849https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/433/576870fb9eb3457b7d5eca0419bca38b-20151203_191959.jpg?1449166849https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/434/b5440f061c376fe0406738b13db408f9-20151203_191058.jpg?1449166849https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/435/e4753fec68fe5f6f3f08236e8896ba0e-20151203_191103.jpg?1449166849https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/436/fe67224a29185d6287496fd52db45a0c-20151203_191043.jpg?144916684948.9301;8.47358Hauptstrae. 219-22156564Neuwiedhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/442/4c74639ca465d67b51e9c40944a7a1ef-image.jpeg?1449171381https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/443/248b215f76ea9089fdbeffa7e1cbc733-image.jpeg?1449171381https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/444/6cb2a95927bd96d86ec4d3c3ae5b4083-image.jpeg?1449171381https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/445/d1230c75ce181cc5786c1107484e0a3a-image.jpeg?1449171381https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/446/ae79ddffefb30947a8f662d62033e8ca-image.jpeg?14491713815408725550.4722;7.50038Friedrichstrae 141https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/447/ba18e12b83140dbbf55f8ca45514efca-image.jpeg?1449176835https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/448/19c16433d0ddfadb80be0610a95f521a-image.jpeg?1449176841https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/449/94d32a71d3856d7be3c80b43bf9876c4-image.jpeg?1449176847https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/450/08ebbb8ce49fd7450f29ba134191f04d-image.jpeg?1449176857https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/451/0a057c07c354566a5ef5e1fb3daadef6-image.jpeg?1449176859Porscheplatz 245127Essenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/462/78f8ba90d4090a79604de1a1a3b7260b-12316404_133799396987028_5344044433584226998_n.jpg?1449218634https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/463/17dd7c8fc3a52578834b07e8246c9280-12308811_133799300320371_9201492491847059217_n.jpg?1449218634https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/464/55e1bbec3a2ff225fa1add390e41d2dc-12308628_133799293653705_6532462147005399207_n.jpg?1449218634https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/465/c53c40fa663ba1ca69413b947c7bcc90-12313534_133799646987003_2033600295046536862_n.jpg?1449218634https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/466/9c708189bac532982bc2aa5485adc923-12316286_133799616987006_2442876006338517193_n.jpg?1449218634https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/467/56b1e9ed6e509d698bb25e4affb99bed-12108022_133799523653682_6113112073681811658_n.jpg?14492186345408721551.4556;7.01045https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/468/78f8ba90d4090a79604de1a1a3b7260b-12316404_133799396987028_5344044433584226998_n.jpg?1449219481https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/469/69faad3de111ce54674dcf62de17b85c-12316488_133799560320345_1957748870102272370_n.jpg?1449219481https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/470/c866b29d60986a2b92d0005871ca9034-11209589_133799496987018_6980584554681753715_n.jpg?1449219481https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/471/c53c40fa663ba1ca69413b947c7bcc90-12313534_133799646987003_2033600295046536862_n.jpg?1449219481https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/472/9c708189bac532982bc2aa5485adc923-12316286_133799616987006_2442876006338517193_n.jpg?1449219481https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/473/56b1e9ed6e509d698bb25e4affb99bed-12108022_133799523653682_6113112073681811658_n.jpg?144921948154087370 Goethestr. 753424Remagenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/474/4d273b99344d189dfb1813097a8ebb08-tmp_24076-20151204_112905-1480100011.jpg?1449225346https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/475/d030ae3373f5520a502801497171f6e4< -tmp_24076-20151204_112933935369251.jpg?1449225346https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/476/d65a1a742820415f13381ac777eb03eb-tmp_24076-20151204_095330-1100255635.jpg?1449225346https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/477/490a3c22817ade47faa0ba3ef610de73-tmp_24076-20151204_1128491049268410.jpg?1449225346https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/478/125b3d617ef53b3e8743b2cacd20bf42-tmp_24076-20151204_1128381475954675.jpg?14492253465408736850.5385;7.26127https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/479/ef514a6c941095e14ec15d1d6650389b-tmp_24076-20151204_11375532455333.jpg?1449226214https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/480/9e87e31c5f1bc5c9f15432f3ac3d0c41-tmp_24076-20151204_114143-707002258.jpg?1449226214https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/481/564cf7c546118b46308763661f28c374-tmp_24076-20151204_113737-2137390969.jpg?1449226214https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/482/3a64efe60a2ce1d604575ec4783557a4-tmp_24076-20151204_113729-23158926.jpg?1449226214https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/483/cf414102cceda113f3c1c86fa31644e8-tmp_24076-20151204_1137331168411876.jpg?144922621454087369Hauptstrasse 133 Oberwinterhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/490/7e3ded2856d7cbbc34150065c01f3422-tmp_24076-20151204_1202222036845483.jpg?1449227048https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/491/8ef43e231fea1b4cc501d588e762d783-tmp_24076-20151204_120129-72793354.jpg?1449227048https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/492/05d92402b341d89cfd7e9b09a82279be-tmp_24076-20151204_120151-1963005965.jpg?1449227048https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/493/eddb5cf27da2aee31bb96c1623343ee1-tmp_24076-20151204_120156-590472496.jpg?1449227048https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/494/3e273913abd8be258450c6deb53885cf-tmp_24076-20151204_120204-281720662.jpg?144922704854087285https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/495/320adeebb3e4909ad74c61945f211e7c-image.jpg?1449232334https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/496/092a448b67a9a4501dfde4fad8500bde-image.jpg?1449232334https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/497/0af577d1e34a326ce4090c91f15d2f89-image.jpg?1449232334https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/498/be4c2fdbc52674f99b954cbcac7f5e8e-image.jpg?1449232334https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/499/1597a78b7f43d0efb6b2d9813a236852-image.jpg?144923233454087276 48.57;13.4562august bebel str 3002736Oppach https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/500/b5d1ae6fcc0af513d9456ea32fd8ccb0-WP_20151204_13_28_44_Pro.jpg?1449232584https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/501/92f84dc78a108bc7d5ee21c8e22a3731-WP_20151204_13_30_13_Pro.jpg?1449232584https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/502/983476c73a0a8c2fcd08ebb0028012a0-WP_20151204_13_29_01_Pro.jpg?1449232584https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/503/b5d1ae6fcc0af513d9456ea32fd8ccb0-WP_20151204_13_28_44_Pro.jpg?1449232584https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/504/b3ab45fced18585b7582432837965bec-WP_20151204_13_29_58_Pro.jpg?1449232584https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/505/467f0c324a961213194b24de7a3d7cd4-WP_20151204_13_29_19_Pro.jpg?14492325845408728151.058;14.5034Markt 950321BRHL https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/506/7a212978150447264186856591d66811-image.jpg?1449239873https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/507/a7cf8b4e9b8f49b5607eaa5930eb34b2-image.jpg?1449239873https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/508/3077fe92df0d6a83ce7aebef5fc79c12-image.jpg?1449239873https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/509/2c776e591ded4d9120f236b44d45bdf9-image.jpg?1449239873https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/510/1631694777fc309f308aee105ae6bc41-image.jpg?1449239873https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/511/adf2756e0378a419f3e437d80938115a-image.jpg?14492398735408732250.8284;6.90474Wrzburger Strae 2474653 Kuenzelsauhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/512/e58bfbb8c525dba2603a03bd6af3a38f-vikas_handy_neu_190.JPG?1449242866https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/513/9cb0556799e214ebb317f80e8039e946-vikas_handy_neu_186.JPG?1449242866https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/514/1aabb30a0e0a3fcfcce5a1bb3e91003e-vikas_handy_neu_189.JPG?1449242866https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/515/bf98ceed6c1bb72060733e8d3a225fbb-vikas_handy_neu_187.JPG?1449242866https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/516/500b74d402a1f29efe2b54597a2902a9-vikas_handy_neu_192.JPG?144924286649.2769;9.67195Wilhelmstrasse 1889547 Gerstettenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/517/b3b6e104c5d534023f6d9e54695a50b5-DSC_0070.JPG?1449249140https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/518/d335d6f0ce5a010eddd8e28d65db4b72-DSC_0063.JPG?1449249140https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/519/f255967db9d49d10383ea0584e87aed5-DSC_0066.JPG?1449249140https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/520/cf975538be112ecd69d978560531d1d1-DSC_0064.JPG?1449249140https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/521/f308b8595172ed6d12c62e678feb8134-DSC_0067.JPG?1449249140https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/522/e980a442f7479f3f60998d15d521283c-DSC_0062.JPG?14492491405408723648.6326;10.0508 Fuggerstr. 32https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/523/c55e65435e18573179bc3ce428f372b4-IMAG0206.jpg?1449251928https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/524/61f4ff5c470765ecbdffcdd70972d4fe-IMAG0205.jpg?1449251928https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/525/79aea3efa8951c41a0d355046366c219-IMAG0203.jpg?1449251928https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/526/9adeed5b2f87a05dcdc5e0074a3be4c6-IMAG0204.jpg?1449251928https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/527/f005093a428e298ec514c027e5c7984b-IMAG0207.jpg?14492519285408738648.1768;10.7606 Karl-str. 2188250 Weingartenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/528/4b5b3f54f8221c3a4b0bbade70befaab-IMG_20151204_180857768_1_.jpg?1449252001https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/529/11d4b4cb73788c438f33e69053f24dba-IMG_20151204_180725717_1_.jpg?1449252001https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/530/01afd9df4233294c6e1a10ead718d0b3-IMG_20151204_181042166_1_.jpg?1449252001https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/531/90d13fdd80969a5f21fb4e5023fc948a-IMG_20151204_181150704_1_.jpg?1449252001https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/532/097a112c829671104481052e56fcacf9-IMG_20151204_181344582_1_.jpg?1449252001https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/533/4222e46382b410ce4f06095396b839ad-IMG_20151204_181404182_1_.jpg?14492520015408721447.8189;9.6449486830 Schwabmnchenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/534/61f4ff5c470765ecbdffcdd70972d4fe-IMAG0205.jpg?1449252165https://s3-eu-west-1.amazonaws.com/fbassets.product< ion/production/250/535/c55e65435e18573179bc3ce428f372b4-IMAG0206.jpg?1449252165https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/536/79aea3efa8951c41a0d355046366c219-IMAG0203.jpg?1449252165https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/537/9adeed5b2f87a05dcdc5e0074a3be4c6-IMAG0204.jpg?1449252165https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/538/f005093a428e298ec514c027e5c7984b-IMAG0207.jpg?144925216554106082https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/556/b5dfe0739d254ff68de56821c031b80a-IMG_20151204_191810.jpg?1449262589https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/557/522b98432dbf449570593901c3011e6e-IMG_20151204_191755.jpg?1449262589https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/558/8a100d3981e364a2c6bc901d850190d8-IMG_20151204_191306.jpg?1449262589https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/559/4a38b31a0e7537fc43635914632c34b5-IMG_20151204_191445.jpg?1449262589https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/560/2e825b7e491baac3ac0d03d844125fec-IMG_20151204_191451.jpg?14492625895393451052.1099;9.36321Walburgistr.1259457Werlhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/561/2d0af25742797448e4e21327c6573046-2015-12-04_13.49.07.jpg?1449293954https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/562/f95da929da3b7eefa3d3b6e0307bb0c2-2015-12-04_13.48.56.jpg?1449293954https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/563/b7066af4dfb62ec450147df25b2930f8-2015-12-04_13.47.30.jpg?1449293954https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/564/f5a412c52ba3d8279a6957b0b1abf2cb-2015-12-04_13.48.02.jpg?1449293954https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/565/daf63d44e747ed88d3bdca8ff31df5e7-2015-12-04_13.48.17.jpg?1449293954https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/566/3399abcad538513f77838d77c2cc98ff-2015-12-04_13.48.37.jpg?14492939545408739251.5568;7.92239Am Markt 12 1323966Wismarhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/567/1c46988b0bf260ada00f5e038d80589d-20151205_102123.jpg?1449308193https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/568/cb073dcea79404de9f8d8dda4c60c9ae-20151205_101501.jpg?1449308193https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/569/dbb5b691fac2d7ccfc2ba06acb17b814-20151205_102155.jpg?1449308193https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/570/2fba06c91b2b922f1419b4a1f6afb19a-20151205_102450.jpg?1449308193https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/571/40a0cca956e52ef69e83563a8e7d8307-20151205_101747.jpg?14493081935408716353.8912;11.465https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/572/8bf055ac9336800264e4db2fff5b9315-20151201_174943.jpg?1449309333https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/573/8bf055ac9336800264e4db2fff5b9315-20151201_174943.jpg?1449309333https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/574/b6f3e6a80fffa1900d1a77e034dbfc03-20151201_175038.jpg?1449309333https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/575/eddf8f31d1a936816da19c44cc653db4-20151201_175105.jpg?1449309333https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/576/491fe51ce88780e18c7e68ffa443c59c-20151201_175124.jpg?14493093335408725051.4271;6.79033Kronsforder Allee 72-7423560https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/582/7ff81d3299b9ca95af68b0f386fad9d5-image.jpg?1449315661https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/583/7ff81d3299b9ca95af68b0f386fad9d5-image.jpg?1449315661https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/584/9fdfe258ce05c0377675540520246076-image.jpg?1449315661https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/585/ab00375120a2d2ee17b1352d99eb014e-image.jpg?1449315661https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/586/119d8d680152e864b8362a0da754b22f-image.jpg?14493156615409397753.8625;10.727Mnkhofer Weg 123564https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/587/11040769e86dcafa91e906eab96e38a1-image.jpg?1449315831https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/588/11040769e86dcafa91e906eab96e38a1-image.jpg?1449315831https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/589/ae5e8c7cd30d390c4c7622a90921ce5d-image.jpg?1449315831https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/590/5b037f4f123c39875a0fe07d078f9e02-image.jpg?1449315831h      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijnttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/591/c32172e292acf2e0a1767edb4e006640-image.jpg?14493158315408737353.8624;10.7269Heimburgstr. 9-15 81243Mnchen https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/592/177686d6a91d2747a90de41599299f87-20151205_140319.jpg?1449321025https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/593/38f79be8346a26c60f7e26285f5bcbd3-20151205_140829.jpg?1449321025https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/594/72097d6ef77873e2927c511cb1359439-20151205_140434.jpg?1449321025https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/595/11a3bcc76ae4eea90108e2b21eeb68cd-20151205_140512.jpg?1449321025https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/596/02965a820674058f30a7160082922601-20151205_140604.jpg?14493210255408726048.1478;11.4441https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/597/177686d6a91d2747a90de41599299f87-20151205_140319.jpg?1449321084https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/598/38f79be8346a26c60f7e26285f5bcbd3-20151205_140829.jpg?1449321084https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/599/72097d6ef77873e2927c511cb1359439-20151205_140434.jpg?1449321084https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/600/11a3bcc76ae4eea90108e2b21eeb68cd-20151205_140512.jpg?1449321084https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/601/02965a820674058f30a7160082922601-20151205_140604.jpg?1449321084Leipziger Str. 4510117https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/602/02fe8d30af6b0854600d2eb05dbc9a9d-1449321649380.jpg?1449321910https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/603/5880138e66f55e8d6e134d6958b97c88-1449321666864.jpg?1449321910https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/604/eeb896c19dc75ebf2850e28eb93d9a87-1449321680866.jpg?1449321910https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/605/ec999ff6ed0b3ca7807679646b2e77a5-1449321750150.jpg?1449321910https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/606/5b0477989647b7ad3d20c97cc6036346-1449321779346.jpg?14493219105408734052.5111;13.397https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/607/02fe8d30af6b0854600d2eb05dbc9a9d-1449321649380.jpg?1449322291https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/608/5880138e66f55e8d6e134d6958b97c88-1449321666864.jpg?1449322291https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/609/eeb896c19dc75ebf2850e28eb93d9a87-1449321680866.jpg?1449322291https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/610/ec999ff6ed0b3ca7807679646b2e77a5-1449321750150.jpg?1449322291https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/611/5b0477989647b7ad3d20c97cc6036346-1449321779346.jpg?1449322291Kirchheimer Strae 63 67269 Grnstadt https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/612/157b70d7c31d92ddd2b4776d6a5b7d93-20151204_171208.jpg?1449328845https://s3-eu-west-1.amazonaws.com/fbassets.< production/production/250/613/708000bcea6ee341a5f98b8d06c1e652-20151204_153939.jpg?1449328845https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/614/0a3201d633aad641c2e644be232d4aff-20151204_154915.jpg?1449328845https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/615/a6210ba625ca8e3a12620e23726d2995-20151204_154622.jpg?1449328845https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/616/ffd908b34887bcc2fa373fce0db8f038-20151204_155133.jpg?14493288455408731849.3805;8.21725Mauerstettenerstr 387600 Kaufbeurenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/617/240503f3a76bca2b40b89a13a62c1f4e-image.jpg?1449331533https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/618/240503f3a76bca2b40b89a13a62c1f4e-image.jpg?1449331533https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/619/240503f3a76bca2b40b89a13a62c1f4e-image.jpg?1449331533https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/620/f944757aeb3869e4d98e24504426e517-image.jpg?1449331533https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/621/1613193f665f5dcb7bf528c520ae2dcd-image.jpg?144933153347.8842;10.6362 ZeppelinstrBad wrishofen https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/622/fe29ce3b177e7f0695ecf8b6248505f7-image.jpg?1449335030https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/623/3c227e8049c7a04333336cfd1642c359-image.jpg?1449335030https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/624/6a80c4aa8cdd26ad60a048f5ca3c2cbf-image.jpg?1449335030https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/625/46a439eb5148a7009c4dd1ad6edb4e53-image.jpg?1449335030https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/626/d65c49af50a74fbf22674f0a6ab5385a-image.jpg?14493350305408728448.0333;10.639Irkutsker strasse 109119Chemnitzhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/637/e36b5eb124e8ef350f14f8228c2d1cd0-DSC_0206.JPG?1449352010https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/638/5a599b7ab4958a835c8f20a5a4a1b90b-DSC_0207.JPG?1449352010https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/639/9cbf3cd5e85c20054bd8d3b467bbbd8c-DSC_0210.JPG?1449352010https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/640/99f069b0e123407f9ad30b849583868e-DSC_0209.JPG?1449352010https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/641/b5c5fb5f45b421d0373012c35f1e138e-DSC_0211.JPG?1449352010https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/642/8d3645a7a26316d02802d83bce0af871-DSC_0215.JPG?14493520105408732750.8172;12.9356Josef-Roth-Strae 163808Haibachhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/643/36587e14ebe92aa8310eb6c90ca85810-WP_20151205_16_57_28_Pro.jpg?1449494266https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/644/de2c642cf9d703b3f835c6e2779c279f-WP_20151205_16_57_04_Pro.jpg?1449494266https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/645/42f7e72b6f1c41d25cabdc77c7fa8d11-WP_20151205_16_54_35_Pro.jpg?1449494266https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/646/19ec57558a514d542eac83e915693bc8-WP_20151205_16_55_41_Pro.jpg?1449494266https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/647/74645529dd6df6c213c7c4a988d441fa-WP_20151205_16_54_53_Pro.jpg?14494942665408734349.9644;9.19727Brnicker Chaussee 116321Bernauhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/648/032c712704228bc9a353ddbb118e0354-IMG_20151127_112528.jpg?1449568060https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/649/ed3394b231be0e00e0168cdfd87224ea-IMG_20151127_112440.jpg?1449568060https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/650/a4f43b03a7ae3b42379306503f87a005-IMG_20151127_112455.jpg?1449568060https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/651/a4f43b03a7ae3b42379306503f87a005-IMG_20151127_112455.jpg?1449568060https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/652/032c712704228bc9a353ddbb118e0354-IMG_20151127_112528.jpg?1449568060https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/653/ed3394b231be0e00e0168cdfd87224ea-IMG_20151127_112440.jpg?14495680605408715552.671;13.5768https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/666/032c712704228bc9a353ddbb118e0354-IMG_20151127_112528.jpg?1449678376https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/667/ed3394b231be0e00e0168cdfd87224ea-IMG_20151127_112440.jpg?1449678376https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/668/a4f43b03a7ae3b42379306503f87a005-IMG_20151127_112455.jpg?1449678376https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/669/032c712704228bc9a353ddbb118e0354-IMG_20151127_112528.jpg?1449678376https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/670/ed3394b231be0e00e0168cdfd87224ea-IMG_20151127_112440.jpg?14496783765420303752.691;13.5537Wolfgangstr. 1406844Dessauhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/677/0db83a1e6d57af0bc026480c3feea63b-WP_20151209_17_35_15_Rich_LI.jpg?1449679410https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/678/2227f0612cc49349f533fc891ab2e8fc-WP_20151209_17_34_20_Rich_LI.jpg?1449679410https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/679/57b5688b088aa78a06df0900453b673f-WP_20151209_17_35_03_Rich_LI.jpg?1449679410https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/680/f2c86260d1c560430d9ce1ab343e035b-WP_20151209_17_34_39_Rich_LI.jpg?1449679410https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/681/952ba2d4dd016ad0897bd9d645eddf27-WP_20151209_17_35_33_Rich_LI.jpg?14496794105420303051.8404;12.2397Stuttgarter Strae 67 Stuttgart https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/688/b308e81697c941ec3cc87cf5ea4b2065-image.jpg?1449680337https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/689/289d3e5382da0d3039fb4f30f7992e99-image.jpg?1449680337https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/690/af20787a475db0877ca4c8195ff55f47-image.jpg?1449680337https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/691/e42dd82860959dad4b2a631126aac51a-image.jpg?1449680337https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/692/d4ccf27154170c017af284e48ac6c28d-image.jpg?1449680337https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/693/b405a03f1702cc73e440f84d6fc5c069-image.jpg?14496803375420302548.8093;9.1593Grapengieer Str. 18-20https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/694/924a6ef6f1ee62fbe552be2aaefff9e3-image.jpg?1449680934https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/695/aaf7ad06db4acd3d3ce4faeb6dd8da6c-image.jpg?1449680934https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/696/26291001bc229e03944320c9b1f56399-image.jpg?1449680934https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/697/1d2922e448e36697b500abe275296215-image.jpg?1449680934https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/698/031fbd41d4809b20378de0179b44e5f3-image.jpg?14496809345420304553.2475;10.406 Irkutsker Str. 1chemnitzhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/713/e36b5eb124e8ef350f14f8228c2d1cd0-DSC_0206.JPG?1449690689https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/714/5a599b7ab4958a835c8f20a5a4a1b90b-DSC_0207.JPG?1449690689https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/715/9cbf3cd5e85c20054bd8d3b467bbbd8c-DSC_0210.JPG?1449690689https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/716/99f069b0e123< 407f9ad30b849583868e-DSC_0209.JPG?1449690689https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/717/9791992dab7486e705a6f24cb9c96fb4-DSC_0216.JPG?1449690689https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/718/8d3645a7a26316d02802d83bce0af871-DSC_0215.JPG?14496906895420307550.8191;12.9377 Karlstrae 21https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/719/b91e28528f391b6417dc798fa51b8ae3-IMG-20151204-WA0035.jpg?1449698019https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/720/17dd1d98eaadda54f315b9b2c3359166-IMG-20151204-WA0036.jpg?1449698019https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/721/5220ad3bb7b4ef2d8fb4c0b120323dff-IMG-20151204-WA0034.jpg?1449698019https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/722/e6fdeca7a93dbd9f5d4f7bb7f10442c2-IMG-20151204-WA0037.jpg?1449698019https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/723/4be62ebf9adb61703fa2d545845f250f-IMG-20151204-WA0031.jpg?1449698019https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/724/e4dd6ef3a77bfe278bf0bc17c0b447cc-IMG-20151204-WA0032.jpg?14496980195420311747.8437;9.66199Lange Strae 5 https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/725/02336ddc645f7903afa1edd6d2b5596b-WP_20151126_008.jpg?1449698867https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/726/fb7a61d41d9f6738480f1277a725d3d9-WP_20151126_006.jpg?1449698867https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/727/6ca804cfa0e61042d0fb793dd5edce57-WP_20151126_004.jpg?1449698867https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/728/529b64ada0d77d07b9cb6c39135937a4-WP_20151126_005.jpg?1449698867https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/729/f32444c652648daeee8d1916e8926a2c-WP_20151126_007.jpg?1449698867https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/730/144741b5dd54e84c11ca8df7dffcb7b4-WP_20151126_001.jpg?14496988675420307649.4678;11.0693https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/734/d335d6f0ce5a010eddd8e28d65db4b72-DSC_0063.JPG?1449701108https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/735/b3b6e104c5d534023f6d9e54695a50b5-DSC_0070.JPG?1449701108https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/736/24ea0cbe32de396f8ce3775e0b031d12-DSC_0065.JPG?1449701108https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/737/cf975538be112ecd69d978560531d1d1-DSC_0064.JPG?1449701108https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/738/f308b8595172ed6d12c62e678feb8134-DSC_0067.JPG?1449701108https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/739/e980a442f7479f3f60998d15d521283c-DSC_0062.JPG?14497011085420311548.5895;10.0984Soester Str.26https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/740/cd537e3b7124a1e48e7715a1b8585593-20151210_092116.jpg?1449739264https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/741/cd537e3b7124a1e48e7715a1b8585593-20151210_092116.jpg?1449739264https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/742/7d24b35d8bc05a8bfd5ae15603cd406b-20151210_092130.jpg?1449739264https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/743/c82d71e65618b42715bafbdf8573b434-20151210_092222.jpg?1449739264https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/744/52d77e291f36c2e2db82779fca9a2ccd-20151210_092212.jpg?1449739264https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/745/5d06f553a196d299e4f35fcbe6e0222a-20151210_092155.jpg?14497392645420305851.5568;7.92228 Poststrae 125469 Halstenbekhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/746/bd5365beb9e07081eb5a0f2b048a06a1-DSC_0075_1_.jpg?1449744935https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/747/8cf7b39e332cf9233d874d6199f9a99b-DSC_0094_1_.jpg?1449744935https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/748/1bb6cc24069f857def31f721d517b2f4-DSC_0079_1_.jpg?1449744935https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/749/ecab02671a74168c529170a7d58c9c66-DSC_0077_1_.jpg?1449744935https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/750/fe34e1f25436a7cb3c7668fb64c7947d-DSC_0092_1_.jpg?14497449355420316353.6274;9.83539Uelzener Str. 105https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/753/59928a0782c2b9a8e7598fa638c30cd9-image.jpg?1449745286https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/754/2aea8d10d5954e11ea67dd9492a58abc-image.jpg?1449745286https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/755/b5d1e97ae2dddbdc535e54d548b17ed6-image.jpg?1449745286https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/756/c61d2e0a7153e8d062e9b7c5e60bf8de-image.jpg?1449745286https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/757/11dcf03bf83ab613173feca62c2405b4-image.jpg?14497452865420315053.2237;10.4042 Hauptsrae 37https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/758/18ad0338a56ade527c7c4fa259626efa-DSC_0073_1_.jpg?1449745450https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/759/042c04bfb759cae77c0d444914cca7fc-DSC_0068_1_.jpg?1449745450https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/760/a0838103574cd27df9c6c4fbe7a807fa-DSC_0062_1_.jpg?1449745450https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/761/da0d11620871326f149f1530e0745e28-DSC_0061_1_.jpg?1449745450https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/762/40d39ed35aa307c65d9a12370f916a7c-DSC_0064_1_.jpg?14497454505420316453.6274;9.8354Hcklinger Weg 66chttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/763/072688f8002dfb1d0fd742f0edf42d36-image.jpg?1449746504https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/764/8539a6293010ecace524023f4c52731e-image.jpg?1449746504https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/765/3e2d6d0aa2fe7d567144509fcb66590f-image.jpg?1449746504https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/766/4ac118e7d714cfc8eef6957dcc07ed7d-image.jpg?1449746504https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/767/111433ba355b79e2a548e1b4aa6291bb-image.jpg?14497465045420314653.2214;10.3846Knesebeckerstr.229378 Wittingen https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/779/3d929168ff982e00deb30378b072a1ca-IMAG2028.jpg?1449751942https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/780/b77943884e7f85b819685a4a598cac65-IMAG2029.jpg?1449751942https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/781/5cf2c041402271580abddd1fde8d8af7-IMAG2030.jpg?1449751942https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/782/3d929168ff982e00deb30378b072a1ca-IMAG2028.jpg?1449751942https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/783/334755c32a67d0fcaa212da1569adfde-IMAG2031.jpg?1449751942https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/784/0c777290c93f29a7369e8baa7b92e4fd-IMAG2032.jpg?14497519425420321952.7235;10.7487Grapengieerstr. 7https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/800/a27edc3f2140d8ad23e68477e1340db9-image.jpg?1449753503https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/801/a8fcb9693fdfa288f0178b8c109c26c9-image.jpg?1449753503https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/802/940debd58935d2acae350b12608ac6f1-image.jpg?1449753503https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/803/7563e9cbfb1c9068134b0ba874cf739e-image.jpg?1449753503https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/804/acdfdd0df30604397c61fc< ca336b4431-image.jpg?1449753503https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/805/eb52684a45cc23ac0e43e8e6560b49d7-image.jpg?14497535035420313953.2474;10.4078Grapengieerstr. 2https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/806/d4b5be82c070eaf319ef34eb3a17a223-image.jpg?1449753968https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/807/df92b1d4ea4157d6ebbcee218f7cf9f0-image.jpg?1449753968https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/808/d15baf132c358a1f6015da81e6753d31-image.jpg?1449753968https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/809/aab2d185d2f5de1a8f58a54fc4622b19-image.jpg?1449753968https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/810/da38dae82e14239ecd3245d898398ced-image.jpg?14497539685420313853.2475;10.4082Wilhelminenstrae 764283 Darmstadt https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/811/ba7682c507699c47958461bbf7a9ad0a-20151210_142230.jpg?1449754380https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/812/6d3697bd55fcef379a22d1611afa5273-20151210_142007.jpg?1449754380https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/813/60e29f33ebb339c1b40db3c093f0a299-20151210_141557.jpg?1449754380https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/814/28295ec73313b0af44f91c5f9bcf4db7-20151210_141732.jpg?1449754380https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/815/ad39dee07e9f6c3131f2de79160e5d86-20151210_141739.jpg?1449754380https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/816/165f8da7759f369e48f5b24e83e43157-20151210_142241.jpg?14497543805420304349.8722;8.65071Groe Bckerstrae 23https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/817/b0ede79a58c803e6b85fcb040faf01fe-image.jpg?1449754545https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/818/72b8d0136757935a9846f4f647f4e8ce-image.jpg?1449754545https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/819/e910be7d0b2b299a038434a1315a16b1-image.jpg?1449754545https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/820/1478148f71dbc4dd88cd08c306812e23-image.jpg?1449754545https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/821/46e5e6684b5432443286818fec82b9bd-image.jpg?14497545455420312353.2491;10.4089 Hornstr.693053 Regensburghttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/822/00425b269442802090b69306aa449531-WP_20151210_14_45_48_Pro.jpg?1449755507https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/823/67fd1b83b168be0512acbe716eb700b4-WP_20151210_14_47_38_Pro.jpg?1449755507https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/824/51e2b1604ef9cb6ce5cff67bd48300fb-WP_20151210_14_46_23_Pro.jpg?1449755507https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/825/4ebba4a1bf6f483af325a9db686b7d80-WP_20151210_14_47_56_Pro.jpg?1449755507https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/826/5659b55a29e6c749c96b0d635510dce5-WP_20151210_14_48_07_Pro.jpg?14497555075420321549.0055;12.1151Mnchener Strae 4347249https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/827/28a4f0c09b759323d04adda8b4bd67e6-20151210_142242.jpg?1449755538https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/828/28a4f0c09b759323d04adda8b4bd67e6-20151210_142242.jpg?1449755538https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/829/69b5d8b1560c4c1055a4227611ea9694-20151210_142308.jpg?1449755538https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/830/5eaf55a34ab9f667412ee30f6e7d3ae5-20151210_142254.jpg?1449755538https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/831/73a575f6a150ebbaa56f1bc7392e2e56-20151210_142330.jpg?14497555385420319151.3841;6.75868Slztorstr. 18https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/832/62000480a0f26d3d6ec674655c1cfeae-image.jpg?1449755777https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/833/d3f5aac2f01070eb7485ce2a7dbc613f-image.jpg?1449755777https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/834/deb6b738f333a7d7b5e21544beee2c82-image.jpg?1449755777https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/835/d669ddeef4a25fc1401912117e9d3d13-image.jpg?1449755777https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/836/c7c1c6ac8560441ab22ce4a1eccf32bd-image.jpg?14497557775420306053.245;10.4026Tauentzienstrasse 9-1210789https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/837/70fee9d53db83792bb8d83372d15a3a5-image.jpg?1449756150https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/838/7942839ef57ad361b0a5b016a343ad59-image.jpg?1449756150https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/839/1f1822bdfa869abafdbf8cba1c954b42-image.jpg?1449756150https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/840/ebcbf590c582357965ddcf250e1e6661-image.jpg?1449756150https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/841/ebcbf590c582357965ddcf250e1e6661-image.jpg?14497561505420319752.4813;13.359Eberhardstrasse 3570173https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/847/f500aaec9b34802d353d22c2f38316f4-image.jpg?1449756760https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/848/5ff32e51779c542dccb0efcf354c6ab9-image.jpg?1449756760https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/849/d9928558c662593989284a4050b5149c-image.jpg?1449756760https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/850/dffd0fa3888e816d33007bc596e1fdb1-image.jpg?1449756760https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/851/a89d4dbedca5e1d08afbfe95d868f7e7-image.jpg?1449756760https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/852/5cd661e51bdbe8f62d675f61fb13a659-image.jpg?14497567605420302648.774;9.17896Theodor-Heuss-Strae 9 70174https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/853/0160c9830d77c8b84c29f7d71ac727c3-image.jpg?1449757784https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/854/dfb01229175023be2ad8cf7079c1994e-image.jpg?1449757784https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/855/518ab4923b016f9eaa68c46d4872a05b-image.jpg?1449757784https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/856/92cc7a38bfdc56820eb2e32793b29156-image.jpg?1449757784https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/857/b4675fd1e090cfc0891bfbdc941207a9-image.jpg?1449757784https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/858/ae6ed664ce1fa281b4f4f39772717ade-image.jpg?14497577845420302748.7771;9.17476 Hornstr. 6 Regensburg https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/859/4400bc95570d2f01405bf3f8814c88a1-WP_20151210_14_56_06_Pro.jpg?1449757895https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/860/bac5f92d554486c479b67e097e697759-WP_20151210_14_56_34_Pro.jpg?1449757895https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/861/6933dfb2ff5553059df09aa6984b200b-WP_20151210_14_56_47_Pro.jpg?1449757895https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/862/e7bf29cde35d242c78bef5e8ac614f4c-WP_20151210_14_57_18_Pro.jpg?1449757895https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/863/cb9a0aa76bb79b1776eda9990eccdc24-WP_20151210_14_57_33_Pro.jpg?1449757895https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/864/dcba2ed234d8bf673e7bafa2f98b2939-WP_20151210_14_57_45_Pro.jpg?144975789549.0163;12.1214Suhler Strae 32-4098553 Schleusingenhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/865/0b758< 72960271f35fa30860fceb5787a-image.jpeg?1449762672https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/866/2e9ffbdcf4f2396b3e230a57baea4f2f-image.jpeg?1449762672https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/867/b42e8583652d02d9f7a1e7f3dacceb32-image.jpeg?1449762672https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/868/d549f966a26e18d9c3597424b7e15e02-image.jpeg?1449762672https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/869/5c88be107fb0dadfa1432cd27d466da0-image.jpeg?14497626725420307050.5437;10.7528 An der Zehnt36466 Dermbach https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/870/e8dd9286a2c7a0a2cd293d3798c3a56c-20151210_153911.jpg?1449762732https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/871/e8dd9286a2c7a0a2cd293d3798c3a56c-20151210_153911.jpg?1449762732https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/872/645c3e8b54e2be0221471705c1bd008b-20151210_153925.jpg?1449762732https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/873/7019d3a2a13a7c4f91ff2b1c9b4c62a5-20151210_154052.jpg?1449762732https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/874/601bf3d8d6e8488d5772fbf3fca73f4f-20151210_154102.jpg?14497627325420318050.6983;10.1699 Markt 5, 09111 https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/875/a6eeeb22a959ef5779007533d744bb1b-DSC_0222.JPG?1449763126https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/876/57bd52028d64c46b3909160fc55fa35c-DSC_0223.JPG?1449763126https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/877/88734fc19ebb4fb079c254f567157bbf-DSC_0228.JPG?1449763126https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/878/2cf44050e362220a3646f290890789a2-DSC_0224.JPG?1449763126https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/879/38ddb014081eca7046ed9b76539d4807-DSC_0229.JPG?1449763126https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/880/62e806b0182d064445c46c9847569772-DSC_0231.JPG?144976312650.8172;12.9355Mnchenerstr.43https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/881/28a4f0c09b759323d04adda8b4bd67e6-20151210_142242.jpg?1449765041https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/882/28a4f0c09b759323d04adda8b4bd67e6-20151210_142242.jpg?1449765041https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/883/f6f6c78de4684d5e59ddea4b77a814c5-20151210_142202.jpg?1449765041https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/884/5eaf55a34ab9f667412ee30f6e7d3ae5-20151210_142254.jpg?1449765041https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/885/73a575f6a150ebbaa56f1bc7392e2e56-20151210_142330.jpg?14497650415422341351.3841;6.75859https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/886/28a4f0c09b759323d04adda8b4bd67e6-20151210_142242.jpg?1449765645https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/887/807e949b9486215994e9a3bfd7ba42c1-20151210_142231.jpg?1449765645https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/888/69b5d8b1560c4c1055a4227611ea9694-20151210_142308.jpg?1449765645https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/889/5eaf55a34ab9f667412ee30f6e7d3ae5-20151210_142254.jpg?1449765645https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/890/73a575f6a150ebbaa56f1bc7392e2e56-20151210_142330.jpg?14497656455422341451.3841;6.75854 Osttangente 634613 Schwalmstadthttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/896/6e93f79894a2bc641e75a60a6827d7df-image.jpeg?1449771366https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/897/b1676f7d14b2e59f6fc8d5f18e9dbba5-image.jpeg?1449771366https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/898/4982479273e35fc8aa72db20e920057a-image.jpeg?1449771366https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/899/4e9926c42caaa44e06d713f34d37cff7-image.jpeg?1449771366https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/900/57ddae16f7fab2aa1327f387fab354f0-image.jpeg?1449771366https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/901/46c643d50212586da58c4fedd0a0121c-image.jpeg?144977136654203049 50.85;8.8https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/902/032c712704228bc9a353ddbb118e0354-IMG_20151127_112528.jpg?1449773210https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/903/a4f43b03a7ae3b42379306503f87a005-IMG_20151127_112455.jpg?1449773210https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/904/a4f43b03a7ae3b42379306503f87a005-IMG_20151127_112455.jpg?1449773210https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/905/ed3394b231be0e00e0168cdfd87224ea-IMG_20151127_112440.jpg?1449773210https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/906/032c712704228bc9a353ddbb118e0354-IMG_20151127_112528.jpg?14497732105422342252.6918;13.5558Emil-Adolff-Strae 2172760 Reutlingen https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/913/8c1c6edaf1b9e094010fd217d1e9e1e0-DSC_0262.JPG?1449777790https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/914/8c1c6edaf1b9e094010fd217d1e9e1e0-DSC_0262.JPG?1449777790https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/915/1dbed9d48eef7b42216fcd81b3523892-DSC_0260.JPG?1449777790https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/916/0f0aa12805c1e012ed35baefcc155576-DSC_0259.JPG?1449777790https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/917/fd54247ebfd5a700e035ff7e19025a91-DSC_0261.JPG?1449777790https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/918/b67a0017f133462633fb4e10b0c51bdf-DSC_0263.JPG?14497777905420306548.5621;9.2641Graflinger Strasse 14594469 Deggendorfhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/919/31797b340c1e196efc1770fdffba7066-DSC_0118.jpg?1449778269https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/920/46b9e7ce21b800d3d677999cba31848f-DSC_0117.jpg?1449778269https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/921/f481624006f92f28a24b55f7e80b3b2a-DSC_0106.jpg?1449778269https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/922/ddc74da02fcfed31ad31581825109532-DSC_0107.jpg?1449778269https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/923/3dc69e0e2b389389191661166958ea20-DSC_0110.jpg?14497782695420310748.1446;11.5895https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/924/8c1c6edaf1b9e094010fd217d1e9e1e0-DSC_0262.JPG?1449779180https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/925/8c1c6edaf1b9e094010fd217d1e9e1e0-DSC_0262.JPG?1449779180https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/926/1dbed9d48eef7b42216fcd81b3523892-DSC_0260.JPG?1449779180https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/927/0f0aa12805c1e012ed35baefcc155576-DSC_0259.JPG?1449779180https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/928/fd54247ebfd5a700e035ff7e19025a91-DSC_0261.JPG?1449779180https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/929/b67a0017f133462633fb4e10b0c51bdf-DSC_0263.JPG?14497791805422343848.5621;9.26409" Niki-de-Saint-Phalle-Promenade 7530159 hannover https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/930/973dc82001b6065ab2e2e94c75b3089b-20151210_211617.jpg?1449781541https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/931/a1320178cb0ccebfe62404e2977751b3-20151210_211725.jpg?1449781541https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/932/dc1780dcd4a97a15fb974df2fce699ef-20151210_211901.jpg?1449781541https://s3-eu-west-1.amazon<saws.com/fbassets.production/production/250/933/b1ae4b729a2c7d06479ded6cb48a8a61-20151210_211918.jpg?1449781541https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/934/372db27b571946ccface923a31e4047c-20151210_211948.jpg?1449781541https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/935/6acc05f0caf68d9bfadf300221e2f82d-20151210_212042.jpg?14497815415420312152.3753;9.9572https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/942/601bf3d8d6e8488d5772fbf3fca73f4f-20151210_154102.jpg?1449820065https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/943/e8dd9286a2c7a0a2cd293d3798c3a56c-20151210_153911.jpg?1449820065https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/944/645c3e8b54e2be0221471705c1bd008b-20151210_153925.jpg?1449820065https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/945/6798e397a84986e9174a861a0b1b2208-20151210_153931.jpg?1449820065https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/946/7019d3a2a13a7c4f91ff2b1c9b4c62a5-20151210_154052.jpg?1449820065Gablonzer Str. 3a24610 Trappenkamp https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/947/b32d3c0696d203f2c062633559857259-1449821386015-1133102344.jpg?1449822183https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/948/7169b0a1508dfe86659a1d455d0ba610-1449821405256-1345068566.jpg?1449822183https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/949/ee161519e6fea0770737617217c844a3-14498214260421707119430.jpg?1449822183https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/950/bce37bfecea34e6c5f16dd36f31b231b-1449821439072565522726.jpg?1449822183https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/951/e8cc8bbe3276cf3e6ed370fe06452f4c-1449821455819969921202.jpg?14498221835408723854.042;10.2076Deutsche Str. 4 44339Dortmundhttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/952/bbf0ac1db7ec592818e3cc559e079929-image.jpg?1449824075https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/953/a47faa48a6bad1a533d9b8b3e78d10d4-image.jpg?1449824075https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/954/1ff47ed68e796761f82c006a28067e24-image.jpg?1449824075https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/955/6e1e84a4f7f96f0db2a567e17651c36a-image.jpg?1449824075https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/956/c5cf8bccb3d064789643bd7707acadfe-image.jpg?1449824075https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/957/2a88d2b20ceac1600fe2af378a115411-image.jpg?14498240755420307351.5812;7.46316Groe Ulrichstrae 206108Hallehttps://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/964/4800b4194ac12ff160adb803b7ab86e6-IMG_20151210_175903.jpg?1449825103https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/965/70c10207074ad9c3d01fcdcf6b639be2-IMG_20151210_175545.jpg?1449825103https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/966/c4bf62451327a195f2c318dd72bd3757-IMG_20151210_175517.jpg?1449825103https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/967/99b0d2df83883e8f29c3057593425107-IMG_20151210_175920.jpg?1449825103https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/968/a5bd384cf54cb8f44f5fea193778c552-IMG_20151210_180001.jpg?14498251035420318540.7143;-74.006Wrzburger Strasse 18863743https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/969/42d0ae8c5a82379c7b660f7e58d5e1e2-WP_20151211_001.jpg?1449825824https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/970/8710acbe5408f02a4b9926e9c3f97591-WP_20151211_003.jpg?1449825824https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/971/b079525e655275eefb6af45abeb4c8cf-WP_20151211_004.jpg?1449825824https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/972/a3e2395acfdad6fcaeea754750b59c72-WP_20151211_005.jpg?1449825824https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/973/3a84e7ac5c22832634b5bcfcf291947d-WP_20151211_006.jpg?14498258245422342649.962;9.17514Kulmbacher Strasse 2https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/974/328729cc87fce03a956535f8de1d995b-WP_20151211_008.jpg?1449826772https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/975/7c98477467eb8a390f23be4e6393af8f-WP_20151211_007.jpg?1449826772https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/976/a5ab02e04aac26afbff6235a070e3b51-WP_20151211_009.jpg?1449826772https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/977/0da030702763a8af25edef5f7280ebd5-WP_20151211_010.jpg?1449826772https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/978/fd8de6ee9912939d83ddc71ea7b24428-WP_20151211_011.jpg?14498267725422342749.962;9.17256https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/979/4400bc95570d2f01405bf3f8814c88a1-WP_20151210_14_56_06_Pro.jpg?1449827293https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/980/bac5f92d554486c479b67e097e697759-WP_20151210_14_56_34_Pro.jpg?1449827293https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/981/6933dfb2ff5553059df09aa6984b200b-WP_20151210_14_56_47_Pro.jpg?1449827293https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/982/e7bf29cde35d242c78bef5e8ac614f4c-WP_20151210_14_57_18_Pro.jpg?1449827293https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/983/cb9a0aa76bb79b1776eda9990eccdc24-WP_20151210_14_57_33_Pro.jpg?1449827293https://s3-eu-west-1.amazonaws.com/fbassets.production/production/250/984/dcba2ed234d8bf673e7bafa2f98b2939-WP_20151210_14_57_45_Pro.jpg?144982729354223494declinedweitere e+ 3,/-4X8 ;m?1tBErJNNSW0 ZJ]/Vae7ixnr,v 8{~^ EOET /65q٧D:Rܸ p*+ wk@4V*,{ i= b6 o|/ asB#@'O+/"r5 %: =WB%YGL7RqW3 [^T bfe3k7o~dsw 4{{zY"i/R| >asϤ:]#F98]\ML RQ  &sN: T  L> L $W PK!pO[Content_Types].xmlj0Eжr(΢]yl#!MB;.n̨̽\A1&ҫ QWKvUbOX#&1`RT9<l#$>r `С-;c=1g KߣHHxk_ٺ6s"*V/ ƒU-GRhh])k^K~,5Ty6N2?ywkZWt:VaAgcQ[olZx 2v-ްS̡U@{ 3z F-P ev)cEIzr1 (qQY ܒ剀L3%1?^=rN=:ye@iT%|'ӯ^>̍U}ӏPA3^|gO^|K6[s<ƈX(=[SD]wCpOXb>!fX]hqn*Y'i'U>B.]ZM2ERZRsTXz;a}B,3һO"N J @\.!ԖovyF]VC C!D%[H.%STq=!!XV~Ar}N%9p"wA7FIHWHQ12B3 }`+g7$T%z3X^]ZթmS}Hx۶6lbqY/ [{b~zۡvh?ߡY+.3k;Y8x 9г (âӧN\IJ~J.Hx1`nI$r֑2&༨fiNMu1C BFFk3m!hM1XVڕ)&Jյj)ZJ\^7a`/ù_8T~71*¢ׄ(YouOgr|,N;[ gI' fNDjm;jj1tgAЄ(d홵tfq˝UuXP0Vg\$bC*M$jb p4XۀǴPۡ1d5ؕ;wB6 G~'$.,tA]S֯ޚwꝖƙuDRgຟ:觊z`SwmMQQTfa-Tv#OUJg\ Pa׽nh58M (Iqۉ9{Жto=%錪+2b:T=p]`ߋ!u7ۀƝ?9QfjY:M ݃)f0,bv?Cɋ=jz1EUX_zCـ+{Xs6 b9dp`#{$<.'L /$ňъL%ZIf/lHnYM\RhвKb}bƭv7MXM˖P2gYk.ǯwY)p|N59t[PK! ѐ'theme/theme/_rels/themeManager.xml.relsM 0wooӺ&݈Э5 6?$Q ,.aic21h:qm@RN;d`o7gK(M&$R(.1r'JЊT8V"AȻHu}|$b{P8g/]QAsم(#L[PK-!pO[Content_Types].xmlPK-!֧6 -_rels/.relsPK-!kytheme/theme/themeManager.xmlPK-!ptheme/theme/theme1.xmlPK-! ѐ' theme/theme/_rels/themeManager.xml.relsPK] 7g $Sk#?  dMbP?_*+%M com.apple.print.PageFormat.FormattingPrinter com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.FormattingPrinter com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMHorizontalRes com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMHorizontalRes 300 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMOrientation com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMOrientation 1 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMScaling com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMScaling 1 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalRes com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalRes 300 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalScaling com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalScaling 1 com.apple.print.ticket.stateFlag 0 com.apple.print.subTicket.paper_info_ticket PMPPDPaperCodeName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray PMPPDPaperCodeName A4 com.apple.print.ticket.stateFlag 0 PMPPDTranslationStringPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray PMPPDTranslationStringPaperName A4 com.apple.print.ticket.stateFlag 0 PMTiogaPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray PMTiogaPaperName iso-a4 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMAdjustedPageRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPageRect 0 0 3262.5000000000005 2329.166666666667 com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMAdjustedPaperRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPaperRect -75 -75 3433.3333333333335 2404.166666666667 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMCustomPaper com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMCustomPaper com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMPaperName iso-a4 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMUnadjustedPageRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPageRect 0 0 783 559 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMUnadjustedPaperRect com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPaperRect -18 -18 824 577 com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.ppd.PMPaperName com.apple.print.ticket.creator com.apple.jobticket com.apple.print.ticket.itemArray com.apple.print.PaperInfo.ppd.PMPaperName A4 com.apple.print.ticket.stateFlag 0 com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.type com.apple.print.PaperInfoTicket com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.type com.apple.print.PageFormatTicket Mz,,  j eg{HH(dh " d,,??U } }  }                                                  ! " # $  %  &   ' ( ) * + , -  .  /  0  1 2 3 4 5 6 7 8  9  :  ;  =  <   > ? @ A B C D  E  F  G   H I J K L M N  O  P  Q   R S T U V W X  Y  Z  [  \  ] ^ _ ` a b c d e f g  ] h   i j k l m n o   p q ` r s t u v w x   y q ` z { | } ~     y q `       x                   1                              ]                                                                1                                 )              1                 1   `                                                                 )    ! "  #  $  %  &   ' ( ) * + , -  .  /  0  1  ] 2 3 ) 4 5 6 7  8  9  :  ;Dl !"#$%&'()*+,-./0123456789:;<=>?  ] < = > ? @ A B C D E ! ! !F !G !H !I !J !K !L ! M ! N ! O ! P " " "Q "R "S "T "U "V "W " X " Y " Z # # #[ #\ #] #^ #_ #` #a # b # c # d $ $] $e $f $T $g $h $i $j $ k $ l $ m % % %n %o %p %q %r %s %t % u % v % w & &] &x &y &z &{ &| &} &~ &  &  &  ' '] ' ' ' ' ' ' ' '  '  '  '  ( ( ( ( ( ( ( ( ( (  (  (  (  ) ) ) ) ) ) ) ) ) )  )  )  * * * * * * * * * *  *  + + + + + + + + + +  +  , , , , , , , , , ,  ,  ,  - - - - - - - - - -  -  -  . .] . . . . . . . .  .  .  / /] / / / / / / / /  /  /  0 0 0 0 0 0 0 0 0 0  0  0  1 1 1 1 1 1 1 1 1 1  2 2 2 2 2 2 2 2 2 2  2  3 3 3 3 3 3 3 3 3 3  3  3  4 4 4 4 4 4 4 4 4  5 5 5 5 5 5 5 5 5 5  5  5  6 6 6 6 6 6 6 6  6  6   6   6   7 7 7 7 7 7 7 7 7 7  7  7   8 8 8 8 8 8 8 8 8 8  8  8  9 91 92 93 94 9 9 9 9 9  9  9 ; 9   9 ! : :1 :2 :3 :4 :" :# :$ :% : & : ' : ; : ( : ) ; ; ;* ;+ ;, ;- ;. ;/ ;0 ; 1 ; 2 ; 3 < < <4 <5 <6 <7 <8 <9 <: < ; < < < = < > = = =? =q =` =@ =A =B =C = D = E = F > > > > > >G >H >I >J > K > L > M ? ?1 ?2 ?3 ?4 ?N ?O ?P ?Q ? R ? S ? T ? UDl~@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ @ @ @V @W @X @Y @Z @[ @\ @ ] @ ^ @ _ A A A` Aa Ab Ac Ad Ae Af A g A h A i A j B B Bk Bl B Bm Bn Bo Bp B q B r B s C C Ct Cu Cv Cw Cx Cy Cz C { C | C } C ~ D D D D D D D D D D  D  D  D  E E] E E E E E E E E  E  E  F F] F F F F F F F F  F  F  G G] G G G G G G G G  G  G  G  H H] H H H H H H H H  H  H  H  I I] I I I I I I I I  I  I  I  J J J J J J J J J J  J  J  J  K K K K K K K K K K  K  K  L L L L L L L L L L  L  L  M M] M M M M M M M M  M  M  M  N N] N N N N N N N N  N  N  O O O O O O O O O O  O  O  P P P P P P P P P P  P  P  Q Q Q Q Q Q Q Q Q Q  Q  Q  Q  R R R R R R R R R R  R  R  R  S S S4 S S S S S S  S  S  S  T T T4 T T6 T T T T T   T   T  T  U U U  U  U  U U U U U  U  U  V V V V V V V V V V  V  V  W W W W  W! W" W# W $ W  X X X% X& X' X( X) X* X+ X , X - X . X / Y Y Y% Y& Y' Y0 Y1 Y2 Y3 Y 4 Y 5 Y 6 Y / Z Z Z7 Z8 Z9 Z: Z; Z< Z= Z > Z ? Z @ [ [] [7 [8 [9 [A [B [C [D [ E [ F [ @ \ \] \G \8 \H \I \J \K \L \ M \ N \ @ ] ] ]* ]+ ], ]O ]P ]Q ]R ] S ] T ] U ^ ^] ^V ^W ^X ^Y ^Z ^[ ^\ ^ ] ^ ^ ^ _ ^ ` _ _ _a _b _c _d _e _f _g _ h _ i _ j _ kD(l~`abcdefghijklmnopqrstuvwxyz{|}~ ` `] `l `m `n `o `p `q `r ` s ` t a a au av aw ax ay az a{ a | a } a ~ a  b b b b b b b b  b  b  c c1 c c c c c c c c  c  c  c  d d d d d d d d d d  d  d  e e e e e` e e e e e  e  e  f f f f f f f f f f  f  f  f  g g g g g g g g g g  g  g  h h1 h h h h h h h h  h  h  i i i i i i i i i i  i  i  j j] j j j j j j j j  j  j  k k k k k k k k k k  k  k  l l l l l l l l l l  l  l  m m m m m) m m m m m  m  m  n n n n n) n n n n n  n  n  o o o o o o o o o o  o  o  p p p p p p p p p p  p  q q q q q q q q q  q  q  r r] r  r  r  r  r  r r r  r  r  r  s s] s s s s s s s s  s  s  t t] t t t  t! t" t# t$ t % t & t ' t ( u u u u u  u) u* u+ u, u - u . u / v v1 v0 v1 v2 v3 v4 v5 v6 v 7 v 8 v 9 w w w: w3 w; w< w= w> w? w @ w A w B w C x x xD x= x> xE xF xG xH x I x J x K y y] yL y  yM yN yO yP yQ y R y S y T y U z z1 zV z z zW zX zY zZ z [ z \ z ] z ^ { { {_ { { {` {a {b {c { d { e { f { g | | |u |v |w |h |i |j |k | l | m | n | o } }1 }p } } }q }r }s }t } u } v } w } x ~ ~ ~y ~z ~{ ~| ~} ~~ ~ ~  ~  ~   ]  = >          Dl  ]  z {           = >                        = >            = >                        = >                                 ]  = >            )            )             )          ]                                                ! " # $ % & T '   (   ) * + , - . /   (   0 1 2 3 4 5 6  1 7 8 9 : ; < = > ? @ A  ]     B C D E F G H  ] I J K L M N O P Q R S   T U V W X Y Z [ \ ]  ] I J K ^ _ ` a b c d e   f g h i j k l m n o p  ]    q r s t u     v w x y z { | } ~                            ]                        ]            D6l>@@  ggD   Oh+'08@T x ' Julia VogelMicrosoft Macintosh Excel@4@0B4 ՜.+,0HP X`hp x '  Worksheet1 ArbeitsbltterCompObje F'Microsoft Excel 97 - 2004-Arbeitsmappe8FIBExcel.Sheet.8rubyzip-2.3.0/test/data/testDirectory.bin000066400000000000000000000004571363314356200204620ustar00rootroot00000000000000PK$,y longAscii.txtUT6v/;{sYDuUsTg3_|e[׌.}uѲ{/wkvc{Ƹc]uWg ^}LC ]p'KAgƚ쬁hmRaUh`BB< o%y 3׶٪Dml]<Z/1s۝xmPbI tA~Qw1[Քރ~ Oְ:/4j+,:d)GxLq 6`.p޾w`!2o<4OhWTṱĩM("Oh4S~sn )h*-=?\lf6m0gv^[-xΥm3uE!8 .% Al @ $#^{@99.RTNVƵm%0tkCϹ7؞KP5O˯w"-ڥ"ڷj([wNylljZmѽΈ`=a|ݸWE6Q,5[*7W9ؽqĸaی C7;|D zij}cqb7fR[K@tLOfmX%06/oxg=)_' eZʬIw""V"`[9zS(vv>ɔD զxs@p`)#REDSqfG^ߺ/h$5?'o;>kdRu`X{s8d~[EC֕;.v~ݨwc&&Q|u&u: tfw&g(ģYFv1a[uA8M qM%2'oˢ?tDf sVOI0lj*^'\ "ʟfԄSATă$3I- bD#"*w1~yݤ+% y lmhR(#O#C 5Dn隊wyIw SgevC_(b/&mjqA(h#DqE#]4S,DR,@pz$ X'HA&}iLW,,ަedfT)ˉL5WӃ#}2Ӊmb7Dl.Yfb 0EU$huE9a!oV#~ȅZK@o8^<_ rp+Y#S)`d=i|V⽘=ŕX~BNܖVܫџƌx=%߂1+i'Qݒ7Qjұaޏns!Z$hc8J`“ŪN=A/ޜiGpEX;zshçH%3ؼs3d6?㓳O ePDٝ'%aJlQ:lˑ}* }Ws gIt%ɾ/oC4a$z:'o 'y5 PŠR Ar`0N $}c0fG|1; ־rY[mF6ٓ鑿tScȲ lZIu[5YYlu>:KeYG~3g !:J{rhPnrCQoan}wM3ZX=̳sP64Q'w(tC$WW4TeZ%p렧aG+6>r&֗˨/r/3];R\x$X?D#M+&9I%d.r\d*F4-I*nO1f! a!i,7}UŸdbA;clBo:Φ=k] &O8X(alY$"I dD8SS1QOX|KO}vv vSHjoQ`-J` $ύoC =8<5_U? 0ŗHOXj*o|Bai(xMhnq'9_9?R CMD> F!$ 8f|T(Z0ǯUHqw4-׭VXfՍX=*Q\9*E*t˹[vUte]wDv2Kqtl.-54 ,C | H/܇~8Jm) ?' w2҂=dgj庹ى}yp05@UѠq??w6_} @200Hg'S+mm4vU Bf%_-`lE[Io ̱%D=ԏ~Dq؉7$`i hۢF *Iirȧ^|v=Z0G$ ܼ~l_ise5`~ {~w (K@ޚCs/v?9<(Bm$^0Gis1og8ĥ ^\EXI0:.+E@\0ng!I^*e0F+Ck&ߌ{qn(Q&$2ɘ3/0ҪD1+{Q2g( 4i z}pc+cZ Z@I<]h!VrFt.&zf Ν ٻAw,OK XHk< j0a' ˱|``$M8E9cBR"|KV;vܢ|ꓑ@RBS8ʖ }LTvC)vS,dGН#fD^bCbLF]Qb͂hBs`9ggyO؀e`@9@O \ٚ?sp79CMm) h{ >;‰* b)f fZ&)(f"Z$X!kk3=z K@֫Q.Xm `mTǯ7ՋQ{9z, ]ᣱ{A"cNRfm"v+fEfGu۝ f;i9̤PةNLO} X@Y7HL% Z!$Yddh@&,4{2)kj($AꊴZl^5 .S\B7?נ!UEuac<{Ѧ ;q<] O#cG{ x%sDr 7Z{I.*Tg萒0):wԖ3E9Y.5ףϱ0֨:fmu.YQ%W {~XD`d}98SKyaNSS],g$Y \bQqފ L [8w N$4!$w0L944ZHSyN@Nt,혭nEP-Fh%'ìDW\Û  $MR6pawѦ%+ݫk9=EN0] 9)yUcE0bڇe]ŵz/Y؁Qӊ녟 Q)P#ϣ~i C'A,7Or 1gJO-F| Ɓ2@]ZZ΄ ZCkhPLK % )VYcp@̘|@XS@߂UܧjnWi,&A&K@y67?ЎyPP1 z{/9\  ?r]>qD;RF»16~`G3q2C/'p=j׸dO@AuBѲK$!!\nLsΕn)!hD t $À3G'pwox)?>E%,iΨj3#N# ,& X&7.OUHFG,TS|wo C#iz;Ч"CD@HQ5$,DT@t;ACA@1T$ g=T~~@p?89@|}BW͠שI~WTӁ(Hf%ɿ~.poWT Ċ>oJP Df"Q! )C$;z;d#B0B02C 6U FLG3ԤC 4 0N$%0HDJv|#;q]bx ݾ_4T×ƽ &Ѣ ?/ZbGȜ[=)LTP 0s8dJ@#!Q k˺K>8!ba69Q,ǘ6uDϜ +R*5B R$zlտՐL.@DdA{# K %pХ0}9vt3f*&)[cijsg;=t]F䆍xQbn@.LydPBD}L Gf$Mq'1;l:/2 U9f8A`2"C.ә@@׆[0-Oا1HXaxM݇nb߬RJX p+JAXbZF X$"a*j%)@hejYY^9Rs]/<qՄ8gu3z¥[E5aDH:@%m:|m P7@"ab%ShpJ@R{S_hӄ!Kۛ$Bwn:}8XYdlg2H`M"&?@vgC.ݡD^9$c :N]"Qg2~`AnAm,ӽD8v/RP9b{1U.FTQ+bɱ}sQ Ku2bI($![m'%)h! Nt. E2]&56ݖ ,h R'^D6^ @?ͤnw)ndЫp_q7l$"m4@`PC0X`pzR;ð[~o7k,)gX:{^k0qQBJ1H3MU@@S$4 uJ 6M8τt0 CjJ!8L(  ")G(ilDtiO *P=Bb)C\8xG}>WGX1/DFQ@dktJCb% hܔ0(fu6urHHJkv7ERʢ+<9-} &lhV5 v9g$b07$`JebӨL-U+u䶣P(pY2LX2oy똳w![XDq$&yp啮z*ɯr^U1vՋ\IDE AK۰N]&] /C>2kƻюv HU%18ʌg$!CpAS%&^ vLrnj1b`4(HBfC@UX &rٿ;}se Zs 6Íc1ɜnr7PLQII%3x˼T ToC 񢣀 Sdʞ`ك40 %޹\x)T>(| "H&Ѷ8 ևaMmrK0PGt*%01jÊր2D7l?ĈQHX5e_Zy _,MȆ&1lJPٸD0B7{eP>Ͽ[SOED1l ,&C[%.^khyl(lBBhO'Q"h1"q! N?{TDu6*PFx1A D/U"(KR(U°:Z%q\0>3E~<8!52=QSsfzq՗p>x eBK2 ?(_*od<<$˧j8+Kj?s}~Er7ΊKVr<($-13+v:r8$K !`IQ]ZU4QDy;(,Dw$S U#PK. |O큧$/ file1.txtUTJ ]ux PK. p|Oi-  gfile2.txtUTD]ux PKw#rubyzip-2.3.0/test/data/zipWithDirs.zip000066400000000000000000000036161363314356200201300ustar00rootroot00000000000000PK ̈́,dir1/UT ^A=^A=UxPK ̈́, dir1/dir11/UT ^A=^A=UxPK̈́,x 2KUdir1/dir11/file111UT ^A=^A=Ux 0 {zd (@۫x!0bLC?*M;QΉ`Ա޼:.t55PK̈́,HN dir1/file11UT ^A=^A=Ux A 0 {_zS QtK5B^ Q?Řȧ5,%C DuTm}EWPK̈́,USHN dir1/file12UT ^A=^A=Ux A 0 {_zS>j&(A>ORq2p =P!pJiHq *_45>PK ̈́,dir2/UT ^A=^A=UxPK "}, dir2/dir21/UT oQA=^A=UxPK ̈́,dir2/dir21/dir221/UT ^A=^A=UxPK̈́,,O]dir2/dir21/dir221/file2221UT ^A=^A=UxA 0 }zMESVag1 :!--#E*18;̑rlvdzPEPK̈́,QhHN dir2/file21UT ^A=^A=Ux A 0 {_zS/|Fѕj&(A>e9j}}8:nk'_45>PK̈́,)gDHfile1UT ^A=^A=Ux A 0 {_z|%B^I?\: s Hȼn h|{PK ̈́, Adir1/UT^A=UxPK ̈́, A8dir1/dir11/UT^A=UxPK̈́,x 2KU vdir1/dir11/file111UT^A=UxPK̈́,HN dir1/file11UT^A=UxPK̈́,USHN dir1/file12UT^A=UxPK ̈́, Adir2/UT^A=UxPK "}, AJdir2/dir21/UToQA=UxPK ̈́, Adir2/dir21/dir221/UT^A=UxPK̈́,,O] dir2/dir21/dir221/file2221UT^A=UxPK̈́,QhHN idir2/file21UT^A=UxPK̈́,)gDH file1UT^A=UxPK krubyzip-2.3.0/test/data/zipWithEncryption.zip000066400000000000000000000011441363314356200213530ustar00rootroot00000000000000PK  E큧/ file1.txtI 1d{t_bfDqǓYM9APEER7#-׉g |nk+S(4RP繅Xt҄F[qpNw6,Hg|\=+\fB#Q cL8Oab7)5'eSg- \HCo !>ɀ;&,uǴ}Css{"Nt)o›Z r2LkNCZOG.QsSBmhup󘛣{z2D \,D-ZB;'vmE= size) raise Errno::EINVAL else @tell=newPos end end def reset @tell = 0 end end class ZipLocalEntryTest < RUNIT::TestCase def test_readLocalEntryHeaderOfFirstTestZipEntry File.open(TestZipFile::TEST_ZIP3.zipName) { |file| entry = ZipEntry.readLocalEntry(file) assert_equal("", entry.comment) # Differs from windows and unix because of CR LF # assert_equal(480, entry.compressedSize) # assert_equal(0x2a27930f, entry.crc) # extra field is 21 bytes long # probably contains some unix attrutes or something # disabled: assert_equal(nil, entry.extra) assert_equal(ZipEntry::DEFLATED, entry.compressionMethod) assert_equal(TestZipFile::TEST_ZIP3.entryNames[0], entry.name) assert_equal(File.size(TestZipFile::TEST_ZIP3.entryNames[0]), entry.size) assert(! entry.isDirectory) } end def test_readLocalEntryFromNonZipFile File.open("ziptest.rb") { |file| assert_equals(nil, ZipEntry.readLocalEntry(file)) } end def test_readLocalEntryFromTruncatedZipFile zipFragment="" File.open(TestZipFile::TEST_ZIP2.zipName) { |f| zipFragment = f.read(12) } # local header is at least 30 bytes zipFragment.extend(IOizeString).reset entry = ZipEntry.new entry.readLocalEntry(zipFragment) fail "ZipError expected" rescue ZipError end def test_writeEntry entry = ZipEntry.new("file.zip", "entryName", "my little comment", "thisIsSomeExtraInformation", 100, 987654, ZipEntry::DEFLATED, 400) writeToFile("localEntryHeader.bin", "centralEntryHeader.bin", entry) entryReadLocal, entryReadCentral = readFromFile("localEntryHeader.bin", "centralEntryHeader.bin") compareLocalEntryHeaders(entry, entryReadLocal) compareCDirEntryHeaders(entry, entryReadCentral) end private def compareLocalEntryHeaders(entry1, entry2) assert_equals(entry1.compressedSize , entry2.compressedSize) assert_equals(entry1.crc , entry2.crc) assert_equals(entry1.extra , entry2.extra) assert_equals(entry1.compressionMethod, entry2.compressionMethod) assert_equals(entry1.name , entry2.name) assert_equals(entry1.size , entry2.size) assert_equals(entry1.localHeaderOffset, entry2.localHeaderOffset) end def compareCDirEntryHeaders(entry1, entry2) compareLocalEntryHeaders(entry1, entry2) assert_equals(entry1.comment, entry2.comment) end def writeToFile(localFileName, centralFileName, entry) File.open(localFileName, "wb") { |f| entry.writeLocalEntry(f) } File.open(centralFileName, "wb") { |f| entry.writeCDirEntry(f) } end def readFromFile(localFileName, centralFileName) localEntry = nil cdirEntry = nil File.open(localFileName, "rb") { |f| localEntry = ZipEntry.readLocalEntry(f) } File.open(centralFileName, "rb") { |f| cdirEntry = ZipEntry.readCDirEntry(f) } return [localEntry, cdirEntry] end end module DecompressorTests # expects @refText and @decompressor def test_readEverything assert_equals(@refText, @decompressor.read) end def test_readInChunks chunkSize = 5 while (decompressedChunk = @decompressor.read(chunkSize)) assert_equals(@refText.slice!(0, chunkSize), decompressedChunk) end assert_equals(0, @refText.size) end end class InflaterTest < RUNIT::TestCase include DecompressorTests def setup @file = File.new("file1.txt.deflatedData", "rb") @refText="" File.open("file1.txt") { |f| @refText = f.read } @decompressor = Inflater.new(@file) end def teardown @file.close end end class PassThruDecompressorTest < RUNIT::TestCase include DecompressorTests TEST_FILE="file1.txt" def setup @file = File.new(TEST_FILE) @refText="" File.open(TEST_FILE) { |f| @refText = f.read } @decompressor = PassThruDecompressor.new(@file, File.size(TEST_FILE)) end def teardown @file.close end end module AssertEntry def assertNextEntry(filename, zis) assertEntry(filename, zis, zis.getNextEntry.name) end def assertEntry(filename, zis, entryName) assert_equals(filename, entryName) assertEntryContentsForStream(filename, zis, entryName) end def assertEntryContentsForStream(filename, zis, entryName) File.open(filename, "rb") { |file| expected = file.read actual = zis.read if (expected != actual) if (expected.length > 400 || actual.length > 400) zipEntryFilename=entryName+".zipEntry" File.open(zipEntryFilename, "wb") { |file| file << actual } fail("File '#{filename}' is different from '#{zipEntryFilename}'") else assert_equals(expected, actual) end end } end def AssertEntry.assertContents(filename, aString) fileContents = "" File.open(filename, "rb") { |f| fileContents = f.read } if (fileContents != aString) if (expected.length > 400 || actual.length > 400) stringFile = filename + ".other" File.open(stringFile, "wb") { |f| f << aString } fail("File '#{filename}' is different from contents of string stored in '#{stringFile}'") else assert_equals(expected, actual) end end end def assertStreamContents(zis, testZipFile) assert(zis != nil) testZipFile.entryNames.each { |entryName| assertNextEntry(entryName, zis) } assert_equals(nil, zis.getNextEntry) end def assertTestZipContents(testZipFile) ZipInputStream.open(testZipFile.zipName) { |zis| assertStreamContents(zis, testZipFile) } end def assertEntryContents(zipFile, entryName, filename = entryName.to_s) zis = zipFile.getInputStream(entryName) assertEntryContentsForStream(filename, zis, entryName) ensure zis.close if zis end end class ZipInputStreamTest < RUNIT::TestCase include AssertEntry def test_new zis = ZipInputStream.new(TestZipFile::TEST_ZIP2.zipName) assertStreamContents(zis, TestZipFile::TEST_ZIP2) zis.close end def test_openWithBlock ZipInputStream.open(TestZipFile::TEST_ZIP2.zipName) { |zis| assertStreamContents(zis, TestZipFile::TEST_ZIP2) } end def test_openWithoutBlock zis = ZipInputStream.open(TestZipFile::TEST_ZIP2.zipName) assertStreamContents(zis, TestZipFile::TEST_ZIP2) end def test_incompleteReads ZipInputStream.open(TestZipFile::TEST_ZIP2.zipName) { |zis| entry = zis.getNextEntry assert_equals(TestZipFile::TEST_ZIP2.entryNames[0], entry.name) assert zis.gets.length > 0 entry = zis.getNextEntry assert_equals(TestZipFile::TEST_ZIP2.entryNames[1], entry.name) assert_equals(0, entry.size) assert_equals(nil, zis.gets) entry = zis.getNextEntry assert_equals(TestZipFile::TEST_ZIP2.entryNames[2], entry.name) assert zis.gets.length > 0 entry = zis.getNextEntry assert_equals(TestZipFile::TEST_ZIP2.entryNames[3], entry.name) assert zis.gets.length > 0 } end end class TestFiles RANDOM_ASCII_FILE1 = "randomAscii1.txt" RANDOM_ASCII_FILE2 = "randomAscii2.txt" RANDOM_ASCII_FILE3 = "randomAscii3.txt" RANDOM_BINARY_FILE1 = "randomBinary1.bin" RANDOM_BINARY_FILE2 = "randomBinary2.bin" EMPTY_TEST_DIR = "emptytestdir" ASCII_TEST_FILES = [ RANDOM_ASCII_FILE1, RANDOM_ASCII_FILE2, RANDOM_ASCII_FILE3 ] BINARY_TEST_FILES = [ RANDOM_BINARY_FILE1, RANDOM_BINARY_FILE2 ] TEST_DIRECTORIES = [ EMPTY_TEST_DIR ] TEST_FILES = [ ASCII_TEST_FILES, BINARY_TEST_FILES, EMPTY_TEST_DIR ].flatten! def TestFiles.createTestFiles(recreate) if (recreate || ! (TEST_FILES.inject(true) { |accum, element| accum && File.exists?(element) })) ASCII_TEST_FILES.each_with_index { |filename, index| createRandomAscii(filename, 1E4 * (index+1)) } BINARY_TEST_FILES.each_with_index { |filename, index| createRandomBinary(filename, 1E4 * (index+1)) } ensureDir(EMPTY_TEST_DIR) end end private def TestFiles.createRandomAscii(filename, size) File.open(filename, "wb") { |file| while (file.tell < size) file << rand end } end def TestFiles.createRandomBinary(filename, size) File.open(filename, "wb") { |file| while (file.tell < size) file << rand.to_a.pack("V") end } end def TestFiles.ensureDir(name) if File.exists?(name) return if File.stat(name).directory? File.delete(name) end Dir.mkdir(name) end end # For representation and creation of # test data class TestZipFile attr_accessor :zipName, :entryNames, :comment def initialize(zipName, entryNames, comment = "") @zipName=zipName @entryNames=entryNames @comment = comment end def TestZipFile.createTestZips(recreate) files = Dir.entries(".") if (recreate || ! (files.index(TEST_ZIP1.zipName) && files.index(TEST_ZIP2.zipName) && files.index(TEST_ZIP3.zipName) && files.index(TEST_ZIP4.zipName) && files.index("empty.txt") && files.index("short.txt") && files.index("longAscii.txt") && files.index("longBinary.bin") )) raise "failed to create test zip '#{TEST_ZIP1.zipName}'" unless system("zip #{TEST_ZIP1.zipName} ziptest.rb") raise "failed to remove entry from '#{TEST_ZIP1.zipName}'" unless system("zip #{TEST_ZIP1.zipName} -d ziptest.rb") File.open("empty.txt", "w") {} File.open("short.txt", "w") { |file| file << "ABCDEF" } ziptestTxt="" File.open("ziptest.rb") { |file| ziptestTxt=file.read } File.open("longAscii.txt", "w") { |file| while (file.tell < 1E5) file << ziptestTxt end } testBinaryPattern="" File.open("empty.zip") { |file| testBinaryPattern=file.read } testBinaryPattern *= 4 File.open("longBinary.bin", "wb") { |file| while (file.tell < 3E5) file << testBinaryPattern << rand end } raise "failed to create test zip '#{TEST_ZIP2.zipName}'" unless system("zip #{TEST_ZIP2.zipName} #{TEST_ZIP2.entryNames.join(' ')}") # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zipName}'" unless system("bash -c \"echo #{TEST_ZIP2.comment} | zip -z #{TEST_ZIP2.zipName}\"") raise "failed to create test zip '#{TEST_ZIP3.zipName}'" unless system("zip #{TEST_ZIP3.zipName} #{TEST_ZIP3.entryNames.join(' ')}") raise "failed to create test zip '#{TEST_ZIP4.zipName}'" unless system("zip #{TEST_ZIP4.zipName} #{TEST_ZIP4.entryNames.join(' ')}") end rescue raise $!.to_s + "\n\nziptest.rb requires the Info-ZIP program 'zip' in the path\n" + "to create test data. If you don't have it you can download\n" + "the necessary test files at http://sf.net/projects/rubyzip." end TEST_ZIP1 = TestZipFile.new("empty.zip", []) TEST_ZIP2 = TestZipFile.new("4entry.zip", %w{ longAscii.txt empty.txt short.txt longBinary.bin}, "my zip comment") TEST_ZIP3 = TestZipFile.new("test1.zip", %w{ file1.txt }) TEST_ZIP4 = TestZipFile.new("zipWithDir.zip", [ "file1.txt", TestFiles::EMPTY_TEST_DIR]) end class AbstractOutputStreamTest < RUNIT::TestCase class TestOutputStream include AbstractOutputStream attr_accessor :buffer def initialize @buffer = "" end def << (data) @buffer << data self end end def setup @outputStream = TestOutputStream.new @origCommaSep = $, @origOutputSep = $\ end def teardown $, = @origCommaSep $\ = @origOutputSep end def test_write count = @outputStream.write("a little string") assert_equals("a little string", @outputStream.buffer) assert_equals("a little string".length, count) count = @outputStream.write(". a little more") assert_equals("a little string. a little more", @outputStream.buffer) assert_equals(". a little more".length, count) end def test_print $\ = nil # record separator set to nil @outputStream.print("hello") assert_equals("hello", @outputStream.buffer) @outputStream.print(" world.") assert_equals("hello world.", @outputStream.buffer) @outputStream.print(" You ok ", "out ", "there?") assert_equals("hello world. You ok out there?", @outputStream.buffer) $\ = "\n" @outputStream.print assert_equals("hello world. You ok out there?\n", @outputStream.buffer) @outputStream.print("I sure hope so!") assert_equals("hello world. You ok out there?\nI sure hope so!\n", @outputStream.buffer) $, = "X" @outputStream.buffer = "" @outputStream.print("monkey", "duck", "zebra") assert_equals("monkeyXduckXzebra\n", @outputStream.buffer) $\ = nil @outputStream.buffer = "" @outputStream.print(20) assert_equals("20", @outputStream.buffer) end def test_printf @outputStream.printf("%d %04x", 123, 123) assert_equals("123 007b", @outputStream.buffer) end def test_putc @outputStream.putc("A") assert_equals("A", @outputStream.buffer) @outputStream.putc(65) assert_equals("AA", @outputStream.buffer) end def test_puts @outputStream.puts assert_equals("\n", @outputStream.buffer) @outputStream.puts("hello", "world") assert_equals("\nhello\nworld\n", @outputStream.buffer) @outputStream.buffer = "" @outputStream.puts("hello\n", "world\n") assert_equals("hello\nworld\n", @outputStream.buffer) @outputStream.buffer = "" @outputStream.puts(["hello\n", "world\n"]) assert_equals("hello\nworld\n", @outputStream.buffer) @outputStream.buffer = "" @outputStream.puts(["hello\n", "world\n"], "bingo") assert_equals("hello\nworld\nbingo\n", @outputStream.buffer) @outputStream.buffer = "" @outputStream.puts(16, 20, 50, "hello") assert_equals("16\n20\n50\nhello\n", @outputStream.buffer) end end module CrcTest def runCrcTest(compressorClass) str = "Here's a nice little text to compute the crc for! Ho hum, it is nice nice nice nice indeed." fakeOut = AbstractOutputStreamTest::TestOutputStream.new deflater = compressorClass.new(fakeOut) deflater << str assert_equals(0x919920fc, deflater.crc) end end class PassThruCompressorTest < RUNIT::TestCase include CrcTest def test_size File.open("dummy.txt", "wb") { |file| compressor = PassThruCompressor.new(file) assert_equals(0, compressor.size) t1 = "hello world" t2 = "" t3 = "bingo" compressor << t1 assert_equals(compressor.size, t1.size) compressor << t2 assert_equals(compressor.size, t1.size + t2.size) compressor << t3 assert_equals(compressor.size, t1.size + t2.size + t3.size) } end def test_crc runCrcTest(PassThruCompressor) end end class DeflaterTest < RUNIT::TestCase include CrcTest def test_outputOperator txt = loadFile("ziptest.rb") deflate(txt, "deflatertest.bin") inflatedTxt = inflate("deflatertest.bin") assert_equals(txt, inflatedTxt) end private def loadFile(fileName) txt = nil File.open(fileName, "rb") { |f| txt = f.read } end def deflate(data, fileName) File.open(fileName, "wb") { |file| deflater = Deflater.new(file) deflater << data deflater.finish assert_equals(deflater.size, data.size) file << "trailing data for zlib with -MAX_WBITS" } end def inflate(fileName) txt = nil File.open(fileName, "rb") { |file| inflater = Inflater.new(file) txt = inflater.read } end def test_crc runCrcTest(Deflater) end end class ZipOutputStreamTest < RUNIT::TestCase include AssertEntry TEST_ZIP = TestZipFile::TEST_ZIP2.clone TEST_ZIP.zipName = "output.zip" def test_new zos = ZipOutputStream.new(TEST_ZIP.zipName) zos.comment = TEST_ZIP.comment writeTestZip(zos) zos.close assertTestZipContents(TEST_ZIP) end def test_open ZipOutputStream.open(TEST_ZIP.zipName) { |zos| zos.comment = TEST_ZIP.comment writeTestZip(zos) } assertTestZipContents(TEST_ZIP) end def test_writingToClosedStream assertIOErrorInClosedStream { |zos| zos << "hello world" } assertIOErrorInClosedStream { |zos| zos.puts "hello world" } assertIOErrorInClosedStream { |zos| zos.write "hello world" } end def test_cannotOpenFile name = TestFiles::EMPTY_TEST_DIR begin zos = ZipOutputStream.open(name) rescue Exception assert($!.kind_of?(Errno::EISDIR) || # Linux $!.kind_of?(Errno::EEXIST) || # Windows/cygwin $!.kind_of?(Errno::EACCES), # Windows "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$!.type}") end end def assertIOErrorInClosedStream assert_exception(IOError) { zos = ZipOutputStream.new("test_putOnClosedStream.zip") zos.close yield zos } end def writeTestZip(zos) TEST_ZIP.entryNames.each { |entryName| zos.putNextEntry(entryName) File.open(entryName, "rb") { |f| zos.write(f.read) } } end end module Enumerable def compareEnumerables(otherEnumerable) otherAsArray = otherEnumerable.to_a index=0 each_with_index { |element, index| return false unless yield(element, otherAsArray[index]) } return index+1 == otherAsArray.size end end class ZipCentralDirectoryEntryTest < RUNIT::TestCase def test_readFromStream File.open("testDirectory.bin", "rb") { |file| entry = ZipEntry.readCDirEntry(file) assert_equals("longAscii.txt", entry.name) assert_equals(ZipEntry::DEFLATED, entry.compressionMethod) assert_equals(106490, entry.size) assert_equals(3784, entry.compressedSize) assert_equals(0xfcd1799c, entry.crc) assert_equals("", entry.comment) entry = ZipEntry.readCDirEntry(file) assert_equals("empty.txt", entry.name) assert_equals(ZipEntry::STORED, entry.compressionMethod) assert_equals(0, entry.size) assert_equals(0, entry.compressedSize) assert_equals(0x0, entry.crc) assert_equals("", entry.comment) entry = ZipEntry.readCDirEntry(file) assert_equals("short.txt", entry.name) assert_equals(ZipEntry::STORED, entry.compressionMethod) assert_equals(6, entry.size) assert_equals(6, entry.compressedSize) assert_equals(0xbb76fe69, entry.crc) assert_equals("", entry.comment) entry = ZipEntry.readCDirEntry(file) assert_equals("longBinary.bin", entry.name) assert_equals(ZipEntry::DEFLATED, entry.compressionMethod) assert_equals(1000024, entry.size) assert_equals(70847, entry.compressedSize) assert_equals(0x10da7d59, entry.crc) assert_equals("", entry.comment) entry = ZipEntry.readCDirEntry(file) assert_equals(nil, entry) # Fields that are not check by this test: # version made by 2 bytes # version needed to extract 2 bytes # general purpose bit flag 2 bytes # last mod file time 2 bytes # last mod file date 2 bytes # compressed size 4 bytes # uncompressed size 4 bytes # disk number start 2 bytes # internal file attributes 2 bytes # external file attributes 4 bytes # relative offset of local header 4 bytes # file name (variable size) # extra field (variable size) # file comment (variable size) } end def test_ReadEntryFromTruncatedZipFile fragment="" File.open("testDirectory.bin") { |f| fragment = f.read(12) } # cdir entry header is at least 46 bytes fragment.extend(IOizeString) entry = ZipEntry.new entry.readCDirEntry(fragment) fail "ZipError expected" rescue ZipError end end class ZipCentralDirectoryTest < RUNIT::TestCase def test_readFromStream File.open(TestZipFile::TEST_ZIP2.zipName, "rb") { |zipFile| cdir = ZipCentralDirectory.readFromStream(zipFile) assert_equals(TestZipFile::TEST_ZIP2.entryNames.size, cdir.size) assert(cdir.compareEnumerables(TestZipFile::TEST_ZIP2.entryNames) { |cdirEntry, testEntryName| cdirEntry.name == testEntryName }) assert_equals(TestZipFile::TEST_ZIP2.comment, cdir.comment) } end def test_readFromInvalidStream File.open("ziptest.rb", "rb") { |zipFile| cdir = ZipCentralDirectory.new cdir.readFromStream(zipFile) } fail "ZipError expected!" rescue ZipError end def test_ReadFromTruncatedZipFile fragment="" File.open("testDirectory.bin") { |f| fragment = f.read } fragment.slice!(12) # removed part of first cdir entry. eocd structure still complete fragment.extend(IOizeString) entry = ZipCentralDirectory.new entry.readFromStream(fragment) fail "ZipError expected" rescue ZipError end def test_writeToStream entries = [ ZipEntry.new("file.zip", "flimse", "myComment", "somethingExtra"), ZipEntry.new("file.zip", "secondEntryName"), ZipEntry.new("file.zip", "lastEntry.txt", "Has a comment too") ] cdir = ZipCentralDirectory.new(entries, "my zip comment") File.open("cdirtest.bin", "wb") { |f| cdir.writeToStream(f) } cdirReadback = ZipCentralDirectory.new File.open("cdirtest.bin", "rb") { |f| cdirReadback.readFromStream(f) } assert_equals(cdir.entries, cdirReadback.entries) end def test_equality cdir1 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, "somethingExtra"), ZipEntry.new("file.zip", "secondEntryName"), ZipEntry.new("file.zip", "lastEntry.txt") ], "my zip comment") cdir2 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, "somethingExtra"), ZipEntry.new("file.zip", "secondEntryName"), ZipEntry.new("file.zip", "lastEntry.txt") ], "my zip comment") cdir3 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, "somethingExtra"), ZipEntry.new("file.zip", "secondEntryName"), ZipEntry.new("file.zip", "lastEntry.txt") ], "comment?") cdir4 = ZipCentralDirectory.new([ ZipEntry.new("file.zip", "flimse", nil, "somethingExtra"), ZipEntry.new("file.zip", "lastEntry.txt") ], "comment?") assert_equals(cdir1, cdir1) assert_equals(cdir1, cdir2) assert(cdir1 != cdir3) assert(cdir2 != cdir3) assert(cdir2 != cdir3) assert(cdir3 != cdir4) assert(cdir3 != "hello") end end class BasicZipFileTest < RUNIT::TestCase include AssertEntry def setup @zipFile = ZipFile.new(TestZipFile::TEST_ZIP2.zipName) @testEntryNameIndex=0 end def nextTestEntryName retVal=TestZipFile::TEST_ZIP2.entryNames[@testEntryNameIndex] @testEntryNameIndex+=1 return retVal end def test_entries assert_equals(TestZipFile::TEST_ZIP2.entryNames, @zipFile.entries.map {|e| e.name} ) end def test_each @zipFile.each { |entry| assert_equals(nextTestEntryName, entry.name) } assert_equals(4, @testEntryNameIndex) end def test_foreach ZipFile.foreach(TestZipFile::TEST_ZIP2.zipName) { |entry| assert_equals(nextTestEntryName, entry.name) } assert_equals(4, @testEntryNameIndex) end def test_getInputStream @zipFile.each { |entry| assertEntry(nextTestEntryName, @zipFile.getInputStream(entry), entry.name) } assert_equals(4, @testEntryNameIndex) end def test_getInputStreamBlock fileAndEntryName = @zipFile.entries.first.name @zipFile.getInputStream(fileAndEntryName) { |zis| assertEntryContentsForStream(fileAndEntryName, zis, fileAndEntryName) } end end class CommonZipFileFixture < RUNIT::TestCase include AssertEntry EMPTY_FILENAME = "emptyZipFile.zip" TEST_ZIP = TestZipFile::TEST_ZIP2.clone TEST_ZIP.zipName = "4entry_copy.zip" def setup File.delete(EMPTY_FILENAME) if File.exists?(EMPTY_FILENAME) File.copy(TestZipFile::TEST_ZIP2.zipName, TEST_ZIP.zipName) end end class ZipFileTest < CommonZipFileFixture def test_createFromScratch comment = "a short comment" zf = ZipFile.new(EMPTY_FILENAME, ZipFile::CREATE) zf.comment = comment zf.close zfRead = ZipFile.new(EMPTY_FILENAME) assert_equals(comment, zfRead.comment) assert_equals(0, zfRead.entries.length) end def test_add srcFile = "ziptest.rb" entryName = "newEntryName.rb" assert(File.exists? srcFile) zf = ZipFile.new(EMPTY_FILENAME, ZipFile::CREATE) zf.add(entryName, srcFile) zf.close zfRead = ZipFile.new(EMPTY_FILENAME) assert_equals("", zfRead.comment) assert_equals(1, zfRead.entries.length) assert_equals(entryName, zfRead.entries.first.name) AssertEntry.assertContents(srcFile, zfRead.getInputStream(entryName) { |zis| zis.read }) end def test_addExistingEntryName assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { |zf| zf.add(zf.entries.first.name, "ziptest.rb") } } end def test_addExistingEntryNameReplace gotCalled = false replacedEntry = nil ZipFile.open(TEST_ZIP.zipName) { |zf| replacedEntry = zf.entries.first.name zf.add(replacedEntry, "ziptest.rb") { gotCalled = true; true } } assert(gotCalled) ZipFile.open(TEST_ZIP.zipName) { |zf| assertContains(zf, replacedEntry, "ziptest.rb") } end def test_addDirectory ZipFile.open(TEST_ZIP.zipName) { |zf| zf.add(TestFiles::EMPTY_TEST_DIR, TestFiles::EMPTY_TEST_DIR) } ZipFile.open(TEST_ZIP.zipName) { |zf| dirEntry = zf.entries.detect { |e| e.name == TestFiles::EMPTY_TEST_DIR+"/" } assert(dirEntry.isDirectory) } end def test_remove entryToRemove, *remainingEntries = TEST_ZIP.entryNames File.copy(TestZipFile::TEST_ZIP2.zipName, TEST_ZIP.zipName) zf = ZipFile.new(TEST_ZIP.zipName) assert(zf.entries.map { |e| e.name }.include?(entryToRemove)) zf.remove(entryToRemove) assert(! zf.entries.map { |e| e.name }.include?(entryToRemove)) assert_equals(zf.entries.map {|x| x.name }.sort, remainingEntries.sort) zf.close zfRead = ZipFile.new(TEST_ZIP.zipName) assert(! zfRead.entries.map { |e| e.name }.include?(entryToRemove)) assert_equals(zfRead.entries.map {|x| x.name }.sort, remainingEntries.sort) zfRead.close end def test_rename entryToRename, *remainingEntries = TEST_ZIP.entryNames zf = ZipFile.new(TEST_ZIP.zipName) assert(zf.entries.map { |e| e.name }.include? entryToRename) newName = "changed name" assert(! zf.entries.map { |e| e.name }.include?(newName)) zf.rename(entryToRename, newName) assert(zf.entries.map { |e| e.name }.include? newName) zf.close zfRead = ZipFile.new(TEST_ZIP.zipName) assert(zfRead.entries.map { |e| e.name }.include? newName) zfRead.close end def test_renameToExistingEntry oldEntries = nil ZipFile.open(TEST_ZIP.zipName) { |zf| oldEntries = zf.entries } assert_exception(ZipEntryExistsError) { ZipFile.open(TEST_ZIP.zipName) { |zf| zf.rename(zf.entries[0], zf.entries[1].name) } } ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, zf.entries.map{ |e| e.name }) } end def test_renameToExistingEntryOverwrite oldEntries = nil ZipFile.open(TEST_ZIP.zipName) { |zf| oldEntries = zf.entries } gotCalled = false ZipFile.open(TEST_ZIP.zipName) { |zf| zf.rename(zf.entries[0], zf.entries[1].name) { gotCalled = true; true } } assert(gotCalled) oldEntries.delete_at(0) ZipFile.open(TEST_ZIP.zipName) { |zf| assert_equals(oldEntries.map{ |e| e.name }, zf.entries.map{ |e| e.name }) } end def test_renameNonEntry nonEntry = "bogusEntry" targetEntry = "targetEntryName" zf = ZipFile.new(TEST_ZIP.zipName) assert(! zf.entries.include?(nonEntry)) assert_exception(ZipNoSuchEntryError) { zf.rename(nonEntry, targetEntry) } zf.commit assert(! zf.entries.include?(targetEntry)) ensure zf.close end def test_renameEntryToExistingEntry entry1, entry2, *remaining = TEST_ZIP.entryNames zf = ZipFile.new(TEST_ZIP.zipName) assert_exception(ZipEntryExistsError) { zf.rename(entry1, entry2) } ensure zf.close end def test_replace unchangedEntries = TEST_ZIP.entryNames.dup entryToReplace = unchangedEntries.delete_at(2) newEntrySrcFilename = "ziptest.rb" zf = ZipFile.new(TEST_ZIP.zipName) zf.replace(entryToReplace, newEntrySrcFilename) zf.close zfRead = ZipFile.new(TEST_ZIP.zipName) AssertEntry::assertContents(newEntrySrcFilename, zfRead.getInputStream(entryToReplace) { |is| is.read }) zfRead.close end def test_replaceNonEntry entryToReplace = "nonExistingEntryname" ZipFile.open(TEST_ZIP.zipName) { |zf| assert_exception(ZipNoSuchEntryError) { zf.replace(entryToReplace, "ziptest.rb") } } end def test_commit newName = "renamedFirst" zf = ZipFile.new(TEST_ZIP.zipName) oldName = zf.entries.first zf.rename(oldName, newName) zf.commit zfRead = ZipFile.new(TEST_ZIP.zipName) assert(zfRead.entries.detect { |e| e.name == newName } != nil) assert(zfRead.entries.detect { |e| e.name == oldName } == nil) zfRead.close zf.close end # This test tests that after commit, you # can delete the file you used to add the entry to the zip file # with def test_commitUseZipEntry File.copy(TestFiles::RANDOM_ASCII_FILE1, "okToDelete.txt") zf = ZipFile.open(TEST_ZIP.zipName) zf.add("okToDelete.txt", "okToDelete.txt") assertContains(zf, "okToDelete.txt") zf.commit File.move("okToDelete.txt", "okToDeleteMoved.txt") assertContains(zf, "okToDelete.txt", "okToDeleteMoved.txt") end # def test_close # zf = ZipFile.new(TEST_ZIP.zipName) # zf.close # assert_exception(IOError) { # zf.extract(TEST_ZIP.entryNames.first, "hullubullu") # } # end def test_compound1 renamedName = "renamedName" originalEntries = [] begin zf = ZipFile.new(TEST_ZIP.zipName) originalEntries = zf.entries.dup assertNotContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, TestFiles::RANDOM_ASCII_FILE1) assertContains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.rename(zf.entries[0], renamedName) assertContains(zf, renamedName) TestFiles::BINARY_TEST_FILES.each { |filename| zf.add(filename, filename) assertContains(zf, filename) } assertContains(zf, originalEntries.last.to_s) zf.remove(originalEntries.last.to_s) assertNotContains(zf, originalEntries.last.to_s) ensure zf.close end begin zfRead = ZipFile.new(TEST_ZIP.zipName) assertContains(zfRead, TestFiles::RANDOM_ASCII_FILE1) assertContains(zfRead, renamedName) TestFiles::BINARY_TEST_FILES.each { |filename| assertContains(zfRead, filename) } assertNotContains(zfRead, originalEntries.last.to_s) ensure zfRead.close end end def test_compound2 begin zf = ZipFile.new(TEST_ZIP.zipName) originalEntries = zf.entries.dup originalEntries.each { |entry| zf.remove(entry) assertNotContains(zf, entry) } assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each { |filename| zf.add(filename, filename) assertContains(zf, filename) } assert_equals(zf.entries.map { |e| e.name }, TestFiles::ASCII_TEST_FILES) zf.rename(TestFiles::ASCII_TEST_FILES[0], "newName") assertNotContains(zf, TestFiles::ASCII_TEST_FILES[0]) assertContains(zf, "newName") ensure zf.close end begin zfRead = ZipFile.new(TEST_ZIP.zipName) asciiTestFiles = TestFiles::ASCII_TEST_FILES.dup asciiTestFiles.shift asciiTestFiles.each { |filename| assertContains(zf, filename) } assertContains(zf, "newName") ensure zfRead.close end end private def assertContains(zf, entryName, filename = entryName) assert(zf.entries.detect { |e| e.name == entryName} != nil, "entry #{entryName} not in #{zf.entries.join(', ')} in zip file #{zf}") assertEntryContents(zf, entryName, filename) if File.exists?(filename) end def assertNotContains(zf, entryName) assert(zf.entries.detect { |e| e.name == entryName} == nil, "entry #{entryName} in #{zf.entries.join(', ')} in zip file #{zf}") end end class ZipFileExtractTest < CommonZipFileFixture EXTRACTED_FILENAME = "extEntry" ENTRY_TO_EXTRACT, *REMAINING_ENTRIES = TEST_ZIP.entryNames.reverse def setup super File.delete(EXTRACTED_FILENAME) if File.exists?(EXTRACTED_FILENAME) end def test_extract ZipFile.open(TEST_ZIP.zipName) { |zf| zf.extract(ENTRY_TO_EXTRACT, EXTRACTED_FILENAME) assert(File.exists? EXTRACTED_FILENAME) AssertEntry::assertContents(EXTRACTED_FILENAME, zf.getInputStream(ENTRY_TO_EXTRACT) { |is| is.read }) } end def test_extractExists writtenText = "written text" File.open(EXTRACTED_FILENAME, "w") { |f| f.write(writtenText) } assert_exception(ZipDestinationFileExistsError) { ZipFile.open(TEST_ZIP.zipName) { |zf| zf.extract(zf.entries.first, EXTRACTED_FILENAME) } } File.open(EXTRACTED_FILENAME, "r") { |f| assert_equals(writtenText, f.read) } end def test_extractExistsOverwrite writtenText = "written text" File.open(EXTRACTED_FILENAME, "w") { |f| f.write(writtenText) } gotCalled = false ZipFile.open(TEST_ZIP.zipName) { |zf| zf.extract(zf.entries.first, EXTRACTED_FILENAME) { gotCalled = true; true } } assert(gotCalled) File.open(EXTRACTED_FILENAME, "r") { |f| assert(writtenText != f.read) } end def test_extractNonEntry zf = ZipFile.new(TEST_ZIP.zipName) assert_exception(ZipNoSuchEntryError) { zf.extract("nonExistingEntry", "nonExistingEntry") } ensure zf.close if zf end def test_extractNonEntry2 outFile = "outfile" assert_exception(ZipNoSuchEntryError) { zf = ZipFile.new(TEST_ZIP.zipName) nonEntry = "hotdog-diddelidoo" assert(! zf.entries.include?(nonEntry)) zf.extract(nonEntry, outFile) zf.close } assert(! File.exists?(outFile)) end end class ZipFileExtractDirectoryTest < CommonZipFileFixture TEST_OUT_NAME = "emptyOutDir" def openZip(&aProc) assert(aProc != nil) ZipFile.open(TestZipFile::TEST_ZIP4.zipName, &aProc) end def extractTestDir(&aProc) openZip { |zf| zf.extract(TestFiles::EMPTY_TEST_DIR, TEST_OUT_NAME, &aProc) } end def setup super Dir.rmdir(TEST_OUT_NAME) if File.directory? TEST_OUT_NAME File.delete(TEST_OUT_NAME) if File.exists? TEST_OUT_NAME end def test_extractDirectory extractTestDir assert(File.directory? TEST_OUT_NAME) end def test_extractDirectoryExistsAsDir Dir.mkdir TEST_OUT_NAME extractTestDir assert(File.directory? TEST_OUT_NAME) end def test_extractDirectoryExistsAsFile File.open(TEST_OUT_NAME, "w") { |f| f.puts "something" } assert_exception(ZipDestinationFileExistsError) { extractTestDir } end def test_extractDirectoryExistsAsFileOverwrite File.open(TEST_OUT_NAME, "w") { |f| f.puts "something" } gotCalled = false extractTestDir { |entry, destPath| gotCalled = true assert_equals(TEST_OUT_NAME, destPath) assert(entry.isDirectory) true } assert(gotCalled) assert(File.directory? TEST_OUT_NAME) end end TestFiles::createTestFiles(ARGV.index("recreate") != nil || ARGV.index("recreateonly") != nil) TestZipFile::createTestZips(ARGV.index("recreate") != nil || ARGV.index("recreateonly") != nil) exit if ARGV.index("recreateonly") != nil #require 'runit/cui/testrunner' #RUNIT::CUI::TestRunner.run(ZipFileTest.suite) # Copyright (C) 2002 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license. PK |O큧// file1.txtUTJ ]ux PK p|Oi-  rfile2.txtUTD]ux PKǦrubyzip-2.3.0/test/data/zipWithStoredCompressionAndEncryption.zip000066400000000000000000001236631363314356200254140ustar00rootroot00000000000000PK |O큧;/ file1.txtUT J ]]ux Opa]-\B' ;#}-| ?zPN 1gp`%swǜ}?}('(V$o ][j TTq kˢ"Cl:43Xp)3O'[D<<+oژVrсCsc_3+;BFLJt]n#0j_FvX81"Ҟ8:]v*(B3ARIVNpUS1 x? pG3I2' 3pqzx.ƨvbq >e Xr^[nP2L ׮O?:[ƍ=+VylZzWSԔ,މׂb76_<[Adc8FFykM7o*` .m#Ǜ]ѭ-7-odJ._2B7p1̵6fȐ![=\!K1as㑥^F9V2;:QvgcY}_&;WɶGK+p'YB!D@ (͆yD:Y?5JOGL:Ю3Q Bߣb&'ss*8VqD˖n%Z`_F(q=QkGNΞ]-qn)` ϟn"HSS4ɭxP0m  &LlցYֿv^FH16ܘ;yOwV H#<,/:~w5OQ W۟)XuFYȚ]ǒ:|墑d"fTnPq3t @zsgߝ5f& 4u3Dΐ6 r 0aVK?t$.-bE|Q¶|8j-4f%&R;"jؔ$D y*T ]B ^ꄧ*gg깍i q3L~/b3mMڱ\W[AaUUK5!~b+m#Ll0rH~>)D=5?kJәGcQN|鮓Kۤ2sr ";΅CJwDznal]_'bQs+ٖlqe8ӥ+J%7>8שy 94W+v=xQI\RtbcEQ&PK큧;/PK p|Oi-  file2.txtUT D]]ux .0fKrʋQlmh"j#&<躨8YN*/faa8] _dv-_a0.-S#EN.S{p6Mdp~@vu\,k,.ndSC`iaBlTxnKpZ!`"^pҩw8(o־TΙݯRU^?XSwfSKZ |cy!Hm;KA (oogE Eú/颺$*qĖ2<`.F#JT,Kg1P`VY UJ@}BWtD4hpDcJpMɷ-(Qvx=i?.,eávB<َ"rnFE/t9kEJ˩g.G6igf/eNB,BܲY]P=iRJyW8|knI ct Œ-=?7?t@ѻc`exGrካd7^j{UX{adK48aJ̤ --0 R4{{ 1FX7S"CaX/mz(ahǧ+S>}sA 0 2llոEdl*cCPԗҗʵbaB͆[bЎZȾO# CȺ5kZ7Qm~CBrgE2hwN}T.̶ _xx4`p`@%#N!`RįVӉ(;Ι`룃[ś=cmڛaߞdjL˟$7,TORנO!Xtu5R!ZmDE&38u )0FA4km@Q&̄ Z ?4z[:594.=%Uv&9Nrz+*Neze8S3[^K/~ J~aabqeW 3@BQZ|J\ .̍oCn^ƶb^/Au<Z  wF)>^Pc_cqL"HE_r8"dlI}y'O;;B`-uCz|E 0@!n0s2 '"2y -u#r1 o)v[r ~ W?s2EX]5A#)+WOYm2 ؕc{JDUe ,vh/w`"YL50(bMf = L=I'rJvG?&* {.u>Pӂz';9 >0$Y|G؁eWsY:,]ƒ/ʇJ@ 8L8mi`_EU,\)L2lu_1aáFii؄{?D.֥ѺQHo^4\0!X`j'=5RhdYՃTKi-?íV{\I{[:K6]?8)҃uOkjѦfccᰡ <V'2DVFFLu '_3ae(sŹNJrXs6Xs]3vQ:U YU*jʇ TON qGZVx WLm `8\F{+ZPVlUp߯Be5vup\& Zv7e.#”sX?8iŷ1ߍE-Ց '3&3D="U2~cP̲AS>Нr,9zZ%ĖWDβ:K\'I\>((F0V {Ŭ5uRdJyiM0w[4-Fc"7&5xr~{|԰cV#5`zD?g z`0 Fū1[;Ց9^٫Njm+?ѼHv71@e ;J4i~~g1'وe_eylr?VI>+fH .裯-721[ mY ,l ?QFР/ag]N쁽RG0Ҙ}1OHdM5 2.ԎQ[6%5Cw%g*$m Uj1=Ovm6  <2k+SqQ(Bkd=):"wUA x;ԚS߱-(TBZs7)͇u49 z ౏2fnhwB/3#TnqUP` e]A,wUhz}, %i"H'q4 %;*W,ҩ= G8xY)1W%0$Qk'f|^2h[ζ֏C'Lጆ8[8~8*%)>Z6 Pj,_:>vnAt 2)mvDLBhn3GbKG@ ^f&\7};8W5fKoթD%nY'ךщtdi^0)@%L4&yˡa*6o y"x]N܁›ZUeG\4&7~ mpֲ"o"=xlX)[lCU}o ycH>biA4Pρ{/8:x}qeD4q|g]p.6Qg3iB'@ۂ#sb6ĸP6 u9)2Nބ~R6biCrT <˷&]¯HGn;i|+W6f\I %T=/j:ͪ"5RWB΢߱4;.~=G}vhu)%sO4.X f`4  L1Lse`+4m0ZbbcAlU'%C:pHAgcr3J]|+E6lΛˢ?UMž*8R[w'¦7"y;:dX< n&KxlFzT/S(7%`cDs,2H<9c</`r {IWA ~Fh*Kݮ/ (kjҡH(d0W* 8l6Rv>svG9=ҋt$oC %#nQ KGO>7 Y/S.bat~Ofԯ{S0xb@QfWNoeCHxXMfx-gh%]R҄HDVQx $L\<k1~v [~[_ycv[䝔/"Z5yoO=~X[p=V ˤc{-4z 8=YjɮۂgNdmkojqħ$a=nYd$;&OQk)!9;Q#GTf37t\/[҇ڲ!xuP(pg3]BG ;, VAu&F34o⡯β*^'ꁴ)iI_xtw I:>ϓr&tx`k>g b|KOFζ/G@5:R&Ak;"0 1fի+t'>ܠm" |au ھ΢#0JYʢ-wN6 hMUQ.+cnWe&Ӊ=#e'"r4؏/AFў[#}p[+mu0O9{ng C'Z!m ׎sy03 h,W2^~]@}QEc;AW*kdU0săFQfUl!xJҠ'ŵ bd̰mlGIFi!I-cd<&;UdEdF+F ܗs SE) a2RwRG2|)JNxz}tkE14'tz}uԳZ1MԖT>^DYj+T)*i''nUX (6>%vyg˭fFnP *41H @d+$%t #L˸2D:_޷NC8?xMuW)?.OHUģ4q s7"wκI72>&Ӿ<sarFf_J BfV\>?yʌyP{=yv1w”>dXt 'y')ܬ:nZNи>A׌G2fsc;"xz)qt&bj]s9z 4!e. mpDܨL[$xUFF |ͧ-P|I2"GS3RsC6^t;n)g&P4kÁdbj, GΒѩ=rX_#q{ȳ{\4f[&4c47Ңt<TvtLFxl\6r쇫l*( 6?ʅ^)t:kC%>_l wKxfrMglq#,kszz&\t+ o3j,x\uABNnLۂ%[6v IG3=~4fylZ@j EmQ*l,ӈA)Yɿ2&vbf:3Yط[m],7&c>eʹv H-i_U]I9-E0(ȞTW"ZJ)P&[$̹ꔠ#Hw vx5v)FuQȯXJ&#e Mb=Az#'5'/OkC`*倜A/O$s7b:c,ʤlɶ$%Țߒiq}bCa-5"l> ]+d{ilQZ5MЊ(qL cSQxR?.4%İ{o/<F^-s _1>=Ztv=}gHݛ}Y\{L'&0ðJ`澨XzTBaóUNsMp &Vy5ʴ)dقӽe ™(ZȃytQ߹nwGLb'0aEXVO.?_7e3k:'%z  \k;=&댑T}B,>4@PETBEZ>XlCs]] -و*0L(>pl;!Se^uZ *Qt湪B_֫)YQ*3*4VcMs uè`_-Zz[wd _6y]!xN#g(_AXtIHVLh#[ sq~bMrqѐRV?-<R(Ob)bIp9֌=E&kÔ pRgh23r\}IL&6IWg޿hت CӲ,qVY!񄿽/3  ("{H1mN/, ~Z1FcLutԱU˺ic9i93=;y,Ij 6$E% &p'т~瓹A=)-:@I =Cd<fZm'3 k2 UK5Lm%ؠ|j%2쑢¢WPqF+]-KǣҸ;y[J7Rj#?@%f&;G=G_=PXDt͝$Do, L4%(7 uҩs[907!VwR*_+"r3#hq~('wd(&'sUA^ CTs4lkx;B:4K)lU+Ҙuٍ'Ӵa"L.D&'>zˬ3e3#sjʔm?K5)I% 9H{Ddt,I8v!_2bY+r]gc!(#(O^0N-aoT\m)AFYGtp̌$">cўJ`VO;mC{|;;x6l%vtzx~cp\sz~x8y9i[Hugv5=С 0Ku,L3**uz,Z?VQ)Cd7~ǤxYs\|ħۂcwkAʸD #D#sd{w7lēi"^ BډVVܛG4:iKFOUt?_79&whf^%fJR/D#ix8߃eN"KOFbFBe^=/וlP] zM3`5koxO(b,[BoS'Țuº6<4~+c Ɯ90'X;3CD6cK&jŷdۂ\nR,&sLsy-2BhL5g1Js'Vz. E7\X, Ym"@3\02#u.~P-$e$ټ8DݠMZ#Jc􎶾H{{6|8L@xs2u{3Bt'EuS@ZUc6NH:<)AMza+Qrr jT^XO\,5V9nQ44 zZ,#]b6ZwjmtQȅ3!rݺH.ERT٫\Ujf}+Mo)nFԛZ!`zC@F1H.#闹T@mϨ Πl/L{FY0 6׎婇ipR MOm:5JSFw+P6 O=m.NUMca37Qהv$H,·:B ʎ'DIt?WQ/?櫸 l?KpdC_#Nz4H+TK~Y8)4Xl~梨q i,@܋MsheEF fMB^yؒECVGZMM y ]٘f(Q7vMPg|N[I,2m4稧ׅ1j كIdؐ§`$ؔ]/.eZc =&Z6#^w@[VxS'RG3)zSY' Mo 2mMg)-6=Y}xu9ɯ,a /Fu=1qZOH[mdUԶ-8!9M0ʴ15^VǵvOKRm1t> ,{+ \ i!bH뮦TwVt.knd֭`yD%_*[ń?p0>PkÚ~;FQ3Y6H"ېu!|JPM1ɉQŞhP b<(kq#_*>_.^.J=;IIs͒ЇR VQO+݈j>2^@6N/weR5`7hf:^JG4yp518"x:p.@5@!1IO맍m%G!ܐ~\+̧u?v4Y/m6 o[r=Ic:RnOǤBMh$pܿo]pl5 zn뭚5]l2UĪzx[IG5/\X-/y?zQ..u_HsFudCM,W>UnXTRFf8nΌ3aM0^y[&Hg4Ig' wmqFet $OQ2a5#lv}K=eR6Ǿ)I'VÃȔcڼ (r]"D^ BUTgf0CO[r"sׇZ#7VH`@@BEB@]gg·D{$y0]֫ʹψ>5r;\,C%..<%ـ[_`r]مQ[PܨN)tsmÂp ՟˸{=ElĜ$EgۼhV |v18']nc`2γ%sE17jݢK-80RJb? cVg=qHU͑ ~|nz]f!UmQ-~rU1^dD{N4#-?)DF? imzD&#P* ~/PSz[CRmrˆYn;sG+ZcLWiJ^sgPlWc"x_LY/5fF%y}ȼtkN-|J] {a pFYˤPF_0SThe&/,ٜKV< ]7@Gn^p)bv$B} 0X\Ecw$A-^O :$Ծ*:VK_kQ̻Ek/&byD_ʭq{Ḿ*#4^<[3fn'Ug%VSnvFg~I0Q3]{]ۻٻϖߜMIMbb&> lmA.5,,5pǵrWئFB# <$T`6O$U7!*[Bf)YjHmN`Kg/'ΟMՔ{D+٫:}f>GkkQ |$9#sC*fDvaNZ)\V ^?| @eKG VG󸄕_abࢴ$dZz& ֎kI gIi&75n5&NfzX@L EɕU0/S+1Fj.vs3Tl3iBX_G=I'ΛL ;F[K4 W'>ZϧW,W+@]3-2 __k`)= ukf'f ֨`@IFh6*ORngJXdK>tOaN{=〼q8#5(],_g7gb8L/#yK렑/Ly`%*OV6k&#Su.`A( '^--ItămxnM@ ,ZE,]ߘN˳o1c)ή9Oxa{u lS{1 I|ҒNgTo= Y¯/6eL)5J.蒉|Ni NRf%2Ewކ אt3p:LU$aw)֫mN2جl Z`<ܩBڙP!`V:WIB-G4F0 0!ج5E+e6bd y?\LD̠Q.QRL+f?1H~6ݽ9UptQRMQ<%bAhwC8ZfTq? DMc9N4jta;1{r3۷Gp@}vNt/&4}Oғnմ('SZ\7*I~,Kq}IUt0H5v|,Ü뗸m8ڥgqs֤ <Q.?|AR~9[׫C֤MdF&WY/@hѠڪz b*lb!O#>з~p->,{wgu}F iV~#gWUГ'人f>c: e4jK붡Q!@|U 9dؼ zHrE9S JO;/22nրBJxj;rbQM1 s!Whg7ŋ|Q#ش`Rڜ8]cSŇbH ?\d3 [mtR`k,qBaFlWVa긣\Xvry!J>|Ptp&bL߇NWKk?Tp{8(HƏdC픵M>v*Skdus72krٰȋҞ HH*%7ܾS(&IY֚ƚNm(5FvK b6F҆l`r!yP{5_{R;wi8~nɈ4){1özɼ4EIQ8..wW.mӟ_QIW)Vzw|Lç1&ǮF){,je`(x4S/Y) :ͭ W~Pe!xVJ`>1|J?k[ <|,,Kz3W-wkpsҫCKDv[2KLVc#4IHx>҅#zG!$Ѹ1;d-zbmlU=PE6A=$aȥ\{jHmnZ8D{Dzs1ThvQ;p%̅OG2|N4;H5&;ʢL"v=^r0} {d _#?`'CƇH4JXoNHF(0 1n5c)"{(Q҄TqJ!5. :v-(X+\3Uxz12>-!-e/\"-[I+n/_4pmNoJtSF_۞2_+]_KkD܇9]|N7UJ:tQ8ԚMӵgJࢱWsڅZ[m^V!͵{Rڮ<3RI''K w:YChZaJ#D#Fw1ՏI E7GhCjR1MJam FbC6D@;*W*iJ`t #> j LYLjo7Kzyߺ.tQUࠂq+s6DTW _TL"H0q"'OqVbnj~3u;WN&!]3̹ ӲRsƃ/Y3D2p {K;'FԦlmm˛f4F&/^%XAU-?W?ڻOȐ~}/~Nbm\Pn{L0mn=w˹؎BĬy=={`kPK`jyQy8 <4Q~`d_lOXqoKܼ;6+b9nG +MO[#7Z TS@&+ݴ7 ~>`o$[:%d4EZNډ)$Lɡ&O xHtWn4h'I?}yZG7]+XWAG"0̐Bu#׻.RyYTC`{;b_]xb7vYe܌XQ°k*{Hi5tPYx`#}{ wBNoGu+)ᘎ& &obyTKS0rX 5)qN-L$`~DiBd?1T# +ְv#K%PfWwic(<@yh,˻^rF-Aa0|i]HfRn %WTabdD : /V@}3Y. 5kWcy ~sO64־di$s)$K O=FСp_=u'2 ҮL#K*PfYz"u\^>az>%u]-6mƭc7\мɚāP\r锎 e| -do4v[FCܮygޱLP R h!g!=Dq5ܪ4fMv{m ϮXYb^ F ( 'ChۤkSԵƧ&aUzv7qi0"hZ{/d^T]S^"h S XW9M:p\gm >PklXEem}NJ%FyNe$lOatkb񅋟?RyɌ;3!!s=v8/B{ JVNsr..LL3#A % '^ aX^ռkOFo6с|-gi\,[22qBb{n[ li@pk\&up]d ޟM -.v:Q6,q}C8oK6u 6sFq=űG GteTnicpJ&nƃ.[.IS1ЯSx=I6=Ay,Dpx5d or,0 u]T#5~"b:4Gp<.K|:r3ʰݵC]E3리y;"a?`(o0!˵c 4^0PńlMGPïw:Ou' Pm"֡4IH|Pr%%X [ ͩ]u)ZQ $aFE;n#w9[@=x8r~U8di)+H,:JP] oM6.=9_#D^ȒZ1R¡锆0)ҕpAΥC\>̖v'1ƤKYRdpgvMãq5HJj;c߬}[]z'MQ1C+xV>GW&4:٭sPBDǿOB~"y\/d_K~!_v\DP~v*n[FRl=H̞pJ[s)eV/Y.3? {ݢH}e |to33m尿%j* D}._^M{O`pE\## >h#v1=ɂ{?& Mض8u7Ο(̋c2.&_3:#(h5E_cl69u9O 8Ľ=Ivρ"3dU]}0׍p=̭9qB㕹U0sӸ,x-T|UCX㚍e7(WKg/r@zӦ_y12 S 6G{sSF9gQ./*AkLJ%׮( .5۾8l^ SLȾ5e }K-p~#C1UkH1MpD'*).>̻KLSS>)VMa$-.ͯE.kri$zMQztˡJDͪLwG]k4̸u~ eCIr!zk|A‰3h#ҾΪ.Lӊt"x_N^H^dEiINKVb]D?_հS]2DXuNZFOހ 4[hGhX0ޒUeHC=-csN֯jΡZX\:2A*uʹ\B#ɒ kDKwfC mhVEND'6 <.2֌k|`7$s/|bMP9 ^M/HJ$]Kv٭pY6 w> ɂ>xPIӳ {$@s0KJ-IC }4VGG#l]7 17fj"`!\E!2$~% P6*Eh)V*NJ$5]K3Xvm$1&Њe>M E ZTfdWOQ2;^#a<(OnDX1{S x:-Y%J) 4uO܃ȵs8o3OKt!\7WS$x)#lq?jĻd#@b7Q$㛏'57u!J*B:/才Y p?v|lO)Ѵ#sLa/WGu*("X6Y2vM$08cSNm2^Yco }*L,#cI2=de"Y֤ 8~Ċy1}~ǒ.uT-sQjǧ*ԎNܞ{a&?`YN{qK[{&؎`%+#%kK"6B* <Wo'^L z*4td "Fww)^0?̀JJ=$k0VEC4)jYʟXHr$o&;#ၓF2=6 HSe$;f8-%2yh  &.\JpL{ZX FOMi;O^@9vO(rN[u &Թ#6u\ش~,S{q4pP~T0[ Ihcd9lgϯ&ϰ60\p;;ڡi~ ; (@:`&7>=i]#dJ~ ф830{]̬*c=9 ւ\+ 3`qkzғW0+\qM?\K8ϼ rڜ:H"N&[D/ /f\8H^o04#s{,=hs-CuW2ؚc'5XNzJܾ)p&Bewy{=V]ڒʫ6&e616tiNm'^^ۿუv\Xu  3VCq4KuM&> 1Ou<0u `XΗ5ᗽYmQۺ ( n"'{ ۹"/W5P:EYz]6TE3̎; uf}r-t{3pY)QͰu*1>JF$ٹQ !Db%* S%޴PW͚<遙$)gsD09"p QI]+rasBjS@Xxq5: ܢCt1+Vv%|(%8#uU9\g4KݛIY躀h5 gj䩕^[ݬ3{c0,ګ\:Ňaz\Wjiz \zn/@CpzTZ;$Z4 Q&>2q6g -uJF*4# (ؐ-+]"έ|?1Z$K{Rh&6  D~kIcۺS1j>f[50ҵG׏s-iٕ|Qi1JsX (ɝLwHb-H*E/,z{`$!IkH ]"]ens}z [L6Nм3p` < bDg[EP1njPws&dBxWZv:8>HEDxRd:(NJpo:LQgz'EWO(__:!a 5L;|&(06#+V⯩H!zI^I )=x/)%[힣YI/P w5o|T&7^G<%Y؄JHϠOovi3 _]yC{5s5܊FkM*(}eA-4QLaF>؀T(8c9~EG&ݮTњn0 C"l!AU1x;Ub l A5T,IbV,>n΂U.K U\e-! 5U)epc6 X$0A#WEPW5নϹ9tSUw+geU`idڎp~I"}t뱾WiὭKΘry}CKB*\S[O$IUDDp4$86~'NF653!_D,G&k`[^~[`qO!9jqHTPۙn!&rx/i<8)d <( +RCuhw`\$Q78ެ} P5#4!Wi:wN 6L+`rN@f~/C,たKbqmcē:COFPi6 wx[ESSFQ(7n)hEMLpRB7h (0Y̦!],s=`CSt툋$3PetΠT;*!^~?;C);J QE{$hvR U`/S(^љu&wS#' Wctߟϫ5Eb>fvh#mPס0u~z.Rɓv:qW(Ut*jaJKR19GhouhM\ǟ4D"/4`z^qƅJ4r `D,5ѹeGCQ:e$wSc}~;)_a+Yc 8w7`>Aފ:a n27snZb`:)JP94po.lu$Mjp޷1WY6 Ѧv02yt#ܴͮSb~G0ΓLj 浻 [6eM!Oؒ 6&Ͼ~kS51rV:!|UwuMx^ kE O}Ex{Tg.!usd[C bcuqT9"e\թPCSўcB7d֖-a"{ K9h\Eg\Tt?(Wc9pSwm0S InnFYd(ȳ状J*Nˣy+hyve & ۼCyF^# [~;?Oh|J{Ƿͦ=y3;=UZW${ ZcmW ~V;If_7=}Aw'rg=$pg ,`24ݘ/_ %UE蓟D\B#C.u䕽;'n&\auxi2xW4dҙ-h^)A!&ˊӄJ}Ssn̙mx>4th0= |/6O}bEwב*1E2rf;~2bɛUJ~AI66U|(ܒXDڡwL8Ik 8=zc#hR8k;ʝ޻ZXk =AHa7Bzv'Pۯ*$GLbmXd\ !bpXQ)u -05<'HW8,:˝v&^䨙`i2}z#O |ok-dXhVw. rעr% |/J:5AZ:tQ0 Nq q{O8r,fTswj=!mh󑨧/b1d[f4<?a^} M HYӮSekJQwUkgS1ӏ (k4V:KI&ymuY=]36T"VC3 + yua|CN_؍r3] a`d{O[PC_M%v6˄*Kc/RV2C8بGެ7UPq"[-J8zHj}Tu6EN]Cx8 Ʃ,W=6{:\>+Gb&[T'hoz+Y]]h GuP-hU1&R&gRŏM/ \.z9n 3BJ!]γGPʋ>Fƭ#E&0<zLEo!ߘ)DG0S&GZ?gzvS*'y"!boca5yVX r46h>&b6n|}wpgz@Y%QW:Pth4Ճ}?4b%Zj}{ "f Eu =^2-G~M}r3W!7T#ڇVxٖ!1w>󿳺[%lQBDe|%` ȿXmm4_l ZB *5}P^xMAl5/,>a5x[snjޅBO&Ɩ*Tg^l.[1_g ~m$`A9&v -b RtI E_=3Ľ`,jd3O,L;U=W.^ V*W-s*Y pM·K |K0kVYUݯq_M14DX(l"`dM>6S(51SZLPz:'b>qzL#';Ko6xݻ`DKA V QV0;AV5s6|QVmysF1J!oM}+B9pOXhĦ)k`}h{z_64JA҉k^1l6Rmk5e-v0#/hoo;.$hTdjh,.+lC'SjeӢj)IW,9vO$ ;86N^Q6^]ڟ0Oޟ\5gm(eDM;dp= ,i6k<:=(&6/LUpëf:kexm*2^:Ymwu,HgCav0,cݩ ZgOR2[xzJ5w lxy흷6ܨ~TքY@Yqlg{9[ZfhKOZ>HY'KF=!1dSmE7Dʤz-[JKR Л3>ͧB qq o;slqT n߷]6͙&V}L9ȣ/ϟsYjcF-^ܗ6! lMsen'N V%LmA) !%-14S v+mc]cv'63TON6c>Bߐ<揙,N{|9LQh,(cjһp]`*Źbt{z}:[arP>i(]K^qnN}9F#9-E\$={nTdZأlKiiC`yp|Ǻ;yhZaArg==VΨzxu€ͥ m6 cS a.z/ů"M.<7>S޾4pU.PLt,q2vvNR-u&O,Cmtz*'}D|cDc4`Xu҇JOnʼm r !ӧJt ¸p3үh Im#;P9}YYhҒ%o"uer.V\3[]r2C[%NSRdȒ1S&L %X'=OZ*f-bߊꞞM:NYAȬt1&v0ј ou>=m fȬ$3vZMsjM+Dhp >$s`+8#n^XU,+a87BdTD'Ej @ n*ީL )F9^v+,Ys((u( ZVB!d}ލ VE:J’.ګ`s@4~-R'D d4Ē4Rv-#xTAe+Ǒ}2Bݣ=ADwtqf6^&}D 8>͐릐J'zI>%l;ھ-7}(+OۼDA]!6rQS]O@t<2V5}9w-Bw6 0j׷z(qygƨ+^o.?t{O~T[)3:z>M}|ݰwEx}?cKkrߛW]J@L59$6h1Bӽ KqWJfOp,'_Je~C`RrH lN o֠{u{IׯGbw0mOIk!"lGon(e i7`1mϽ; qD$nyO'3OP"O뵓w?!s[ny(j|X`r~^~&W4xGNb?E%V=2.z2^غ gj(mUxrǃ}&۫T&,Qcv*W앎!@ס[h띑Ht }l~5W͏Ղ8L5 w+b0523goKK;ܑՎި\Or+%c!<KیEATru8M<[ nӉjrqu(-Mtnq|+L(tk[(#O @v!$Q[xfh2̬," Ki u5Y=UOzc/DIc^2P2'-F0£29%3C"rA[n_PVR~"-Ҹ>|_@N`PE~V}ݨq*kAX~fڲA9qe_̳gq}.s _@55сV&Q"Q{M gzAA1Aei%D dhh̲96D}b+WcoXXr~5pȹLE I4,;9>4KU*B>pv@@oK?!h,+PP'5I[ ǐ2 qID *%?/ ]'`Vթ:xQ#FftѭqwR;";T_xw)>̣ˊ_(Vn>.b\AQг!ene`\= 9<jpx!>n飼0`]3ݹ~Ov]v/XC*ds*{YIJkZpc=P ||~&zGDWn-DS,| oN$q|aU)´bN`;{ʎ#%Gh[Fm_JKUۇ4^7Dy$XӨT[zUC#ʹ>.e8[hRqZIIxp>+aB>|LlX-O0-=H=| R "Xђ.xP%*gNDoʚJ:xAt5lԐT 8)$Af_Ӫi>^ ρd.iM]Al.Wƀ T [Y~0At U>$?9 ͖zPy$~:Ͼ<''Cp8~{oZ"Z5b[M)0xA>Y@qG *_5QT?SoЁf EPWxјtʤEUU.ge 'T«\֜K>ŻFijTQ2RkbmX,Yz,Vng㇏\ k:xb{/x98nW9Mh0 4zw$OE.cߪ/<% G RSЉͱ^I]r(I<_Lq1C!)Du%H`FB[FbtM]c>EH[crjkYBg.7o:sɣ|xWO%ű5ywȒ \/CJv>ȲJS5?" TBYƞD?emlک* .Bm(~b^LDݾprFki JBMETnkk^33/S)ؑÎcXxkQ{lKH7lVcgy2;jU*7\ܛt|I`c+9ܝy*CY{O6OnmEU"G\8N#YFp+zѯ)jV>Ť_uV69,vOxYDwYu` Ϝ!Wo}\uVfܘYNhH\/ ,|rT1(BHѥ"Ε$4ے\iO0zח7C[QguPPoƹfK@ۛTT\m¡O7Y&չw#!q`Ģˍbu2zd |[rd$8VEUV[k\oO)f8u#F{flc@Yd|\3d2E^-&J!tj2,eIW`huC 3+vsHt.`" {du!9|T\pf%qQg֭ U9K><PZ!8ԎKbm!b3ny'fČHeuR!| ۏ]w]:Vezߥ]}szuζ3+:;o2o'hew+-C !b^9`@S.EWm[=KIx:wk3,O酛bheSUd/R"&bvpQai;|ô}M#h`,#V`]=5XńtCG^Uݭ~|yfND&PM]+R/uk+[DHgw?ϐyfGCmfY1G{%sJթ,Z1,\~k?e9E5XF]Rɟlv!r(HÏ0wVyl7Q`;UԺ~otnJ ÁxG6X+5Y>c?חh=#hƬA`5 lR k sySXV[~V9J35({(./ʼ )JM%ȎM2m* }to.-l)YȺUzόcc6q\A l 5V?ﲸ*ӉDg:p2>,LW-yɐ15Fĕ 뀛 ƲՋv #0n;4O:tq-i7 |6Ljt!] r/!m O@@'s >{W]~+:U-MAߔ}f[|wΩն.e`UЛL9.?ϰ;r@@s~=aD“69myf Zy7 4jy^[F)Eɀij許Ժ 6Uo,VEK9y)C-BF2vUC*8(\#URҪ(Hf͓U~`p›gxZ`-?knɄѳygͭv舟WjRbf\& p3"-Oϳ|^?ɥ B <VRIxYApBSīviaw8H+;tl7g{zdrmH֓s[M'\n[0-.dLV_o-UT36?kEz,VgCU*M !*&Q=HP,& Dyxr-G*]4ޓ` n"V3ͯ"c !S:}~.Э3Fg#b_0P37O_#|vդoE.lʔ\!&8"X2苬,~-I :Vb)ӽ$tX' t5= p!dɁT3W z4MK]%a2wXFW9OX^ ɇ* ' FVj5u2w5g^%MJ8wxt =uE|2WuÒc;w $x 7p+ AY]K)sߏ|, gw 7K T @B r[l= \p5E`VVɃf^*^5(B㹇hx ˞3yӷ}f`6ʠjte6o{Qfbȣ VŠZ۝KiGY,&nC? 'l6(V]@uDuFu=wu+Yg 2Y՛8m-;@h3n/6$/BeؔCQ#~ؒZh0sH}VӛXCQz(e]V*1KUk:Q/n,VuޱV `3J)9&Fw 鷐pw()c$r9j_Bӓ ;I0@- D 0D^;-Iǫ0$T&d׈)hgjOW蘖BOdP}FZ&xav.4?m!Zzh-kr#NW(ar𭁙4C%NF22?oDC+d*[ Qo*^ pfki2DGģDJ03 ~}1}BwHE8OZ{f]"٩Z5 l;\;Ͼõx4-ʓӇXʹG/£[Mh'-jl3kpci#N9kIHM7e!ے?<O ,$Nf^^r# R|"R;gb"x{-plU"ADjKϪR8kAThl8D=s?\H` m#'V(`32Yx9S [ cg $<Xԉl91lOO+0yfЎd1,'h7bfg%ڀA'핣;Hʓ}LY؟Dj OhВC;/$&RJ[ޢvEY-4[) h fJώ%P6`/uwƃSAX=@E@W?%H7OZJ]>O{:_m2 `3vG!#@?͐2 Cœ5QfL/^R2$-x17.}˃+zXOm@HQ+?(GX$';Lq2&E=ޢ ߫h=Q27ӚII6= 0U8i°dK:+ =Seo|*6(9{iM_\1‘@PTO9KqZ?҅N8!b<,;tJ,6w ?۫cmq'{M.KoPYl,ͣF{l9V>^x'y7>Ynfez!r"H HL AʼnJ1n[(jA/P7x$9Uh9BN jJpsZ_˷KGňs5DN9~ '1 myv+ғ\?}[I)& V:F 4NM GwZ@2YAZK|䤡IUV.IЭRx}-z3Z߯,eLힰ0pf؟^ZqTTM[l5ସ< 괦..*+_PQ~ʱQ JfX)i]z'.s;)a&*dFFɏ\?:k/%k?ΑէFX0‘bZ2<_wejӬ-ţ^^dylRW.5/-lArf]!:E lpGmS~r!k 7V NL3h:ba]Jئ0KP!VQlرkoLBҶdoQEX=Ѡ}z98uk|k(39F_`Z /T؆]h٥@q1OJ7FO5CE%Cӳc7%;4pKᤁZ5?üV6 =v *'$گ6է7 e6o1jw\`_:YUcӣב:w2_wQͻizSU9Țv/4rv|cuuFL~r}&:=HtB>w3vT@tLDV9BQ]{XA` fyie.k_(|sgk9[z^=dl}vX\ϛnOs%])| k 4!Ae̷8s#d>dfAfZV-&ƺuP8WK)J[ Qfc!m^/%R. EokR 7:ק8@UNnuځfug|u0S NjExci:6/ww >GK fXEО[Ki6X/ݙ\?]N6Z} 8. >* e~юLe66Iϟ?3?l0tEtx36Y7m?+LdǍ Mw1Oa9願rC"<~IQ^\UvxRLzX݈fKO zt+ v|2".[W,bm #TX}dkE >#J򦯡VG $y-;40-O :`fT ^ $PKi- PK |O큧;/ file1.txtUTJ ]ux PK p|Oi-  file2.txtUTD]ux PKrubyzip-2.3.0/test/decompressor_test.rb000066400000000000000000000007141363314356200203000ustar00rootroot00000000000000require 'test_helper' class DecompressorTest < MiniTest::Test TEST_COMPRESSION_METHOD = 255 class TestCompressionClass end def test_decompressor_registration assert_nil(::Zip::Decompressor.find_by_compression_method(TEST_COMPRESSION_METHOD)) ::Zip::Decompressor.register(TEST_COMPRESSION_METHOD, TestCompressionClass) assert_equal(TestCompressionClass, ::Zip::Decompressor.find_by_compression_method(TEST_COMPRESSION_METHOD)) end end rubyzip-2.3.0/test/deflater_test.rb000066400000000000000000000034321363314356200173610ustar00rootroot00000000000000require 'test_helper' class DeflaterTest < MiniTest::Test include CrcTest DEFLATER_TEST_FILE = 'test/data/generated/deflatertest.bin' BEST_COMP_FILE = 'test/data/generated/compressiontest_best_compression.bin' DEFAULT_COMP_FILE = 'test/data/generated/compressiontest_default_compression.bin' NO_COMP_FILE = 'test/data/generated/compressiontest_no_compression.bin' def test_output_operator txt = load_file('test/data/file2.txt') deflate(txt, DEFLATER_TEST_FILE) inflated_txt = inflate(DEFLATER_TEST_FILE) assert_equal(txt, inflated_txt) end def test_default_compression txt = load_file('test/data/file2.txt') Zip.default_compression = ::Zlib::BEST_COMPRESSION deflate(txt, BEST_COMP_FILE) Zip.default_compression = ::Zlib::DEFAULT_COMPRESSION deflate(txt, DEFAULT_COMP_FILE) Zip.default_compression = ::Zlib::NO_COMPRESSION deflate(txt, NO_COMP_FILE) best = File.size(BEST_COMP_FILE) default = File.size(DEFAULT_COMP_FILE) no = File.size(NO_COMP_FILE) assert(best < default) assert(best < no) assert(default < no) end def test_data_error assert_raises(::Zip::DecompressionError) do inflate('test/data/file1.txt.corrupt.deflatedData') end end private def load_file(filename) File.open(filename, 'rb', &:read) end def deflate(data, filename) File.open(filename, 'wb') do |file| deflater = ::Zip::Deflater.new(file) deflater << data deflater.finish assert_equal(deflater.size, data.size) file << 'trailing data for zlib with -MAX_WBITS' end end def inflate(filename) File.open(filename, 'rb') do |file| inflater = ::Zip::Inflater.new(file) inflater.read end end def test_crc run_crc_test(::Zip::Deflater) end end rubyzip-2.3.0/test/encryption_test.rb000066400000000000000000000024431363314356200177660ustar00rootroot00000000000000require 'test_helper' class EncryptionTest < MiniTest::Test ENCRYPT_ZIP_TEST_FILE = 'test/data/zipWithEncryption.zip' INPUT_FILE1 = 'test/data/file1.txt' def setup @default_compression = Zip.default_compression Zip.default_compression = ::Zlib::DEFAULT_COMPRESSION end def teardown Zip.default_compression = @default_compression end def test_encrypt test_file = ::File.open(ENCRYPT_ZIP_TEST_FILE, 'rb').read @rand = [250, 143, 107, 13, 143, 22, 155, 75, 228, 150, 12] @output = ::Zip::DOSTime.stub(:now, ::Zip::DOSTime.new(2014, 12, 17, 15, 56, 24)) do Random.stub(:rand, ->(_range) { @rand.shift }) do Zip::OutputStream.write_buffer(::StringIO.new(''), Zip::TraditionalEncrypter.new('password')) do |zos| zos.put_next_entry('file1.txt') zos.write ::File.open(INPUT_FILE1).read end.string end end @output.unpack('C*').each_with_index do |c, i| assert_equal test_file[i].ord, c end end def test_decrypt Zip::InputStream.open(ENCRYPT_ZIP_TEST_FILE, 0, Zip::TraditionalDecrypter.new('password')) do |zis| entry = zis.get_next_entry assert_equal 'file1.txt', entry.name assert_equal 1327, entry.size assert_equal ::File.open(INPUT_FILE1, 'r').read, zis.read end end end rubyzip-2.3.0/test/entry_set_test.rb000066400000000000000000000113471363314356200176130ustar00rootroot00000000000000require 'test_helper' class ZipEntrySetTest < MiniTest::Test ZIP_ENTRIES = [ ::Zip::Entry.new('zipfile.zip', 'name1', 'comment1'), ::Zip::Entry.new('zipfile.zip', 'name3', 'comment1'), ::Zip::Entry.new('zipfile.zip', 'name2', 'comment1'), ::Zip::Entry.new('zipfile.zip', 'name4', 'comment1'), ::Zip::Entry.new('zipfile.zip', 'name5', 'comment1'), ::Zip::Entry.new('zipfile.zip', 'name6', 'comment1') ] def setup @zip_entry_set = ::Zip::EntrySet.new(ZIP_ENTRIES) end def teardown ::Zip.reset! end def test_include assert(@zip_entry_set.include?(ZIP_ENTRIES.first)) assert(!@zip_entry_set.include?(::Zip::Entry.new('different.zip', 'different', 'aComment'))) end def test_size assert_equal(ZIP_ENTRIES.size, @zip_entry_set.size) assert_equal(ZIP_ENTRIES.size, @zip_entry_set.length) @zip_entry_set << ::Zip::Entry.new('a', 'b', 'c') assert_equal(ZIP_ENTRIES.size + 1, @zip_entry_set.length) end def test_add zes = ::Zip::EntrySet.new entry1 = ::Zip::Entry.new('zf.zip', 'name1') entry2 = ::Zip::Entry.new('zf.zip', 'name2') zes << entry1 assert(zes.include?(entry1)) zes.push(entry2) assert(zes.include?(entry2)) end def test_delete assert_equal(ZIP_ENTRIES.size, @zip_entry_set.size) entry = @zip_entry_set.delete(ZIP_ENTRIES.first) assert_equal(ZIP_ENTRIES.size - 1, @zip_entry_set.size) assert_equal(ZIP_ENTRIES.first, entry) entry = @zip_entry_set.delete(ZIP_ENTRIES.first) assert_equal(ZIP_ENTRIES.size - 1, @zip_entry_set.size) assert_nil(entry) end def test_each # Used each instead each_with_index due the bug in jRuby count = 0 @zip_entry_set.each do |entry| assert(ZIP_ENTRIES.include?(entry)) count += 1 end assert_equal(ZIP_ENTRIES.size, count) end def test_entries assert_equal(ZIP_ENTRIES, @zip_entry_set.entries) end def test_find_entry entries = [::Zip::Entry.new('zipfile.zip', 'MiXeDcAsEnAmE', 'comment1')] ::Zip.case_insensitive_match = true zip_entry_set = ::Zip::EntrySet.new(entries) assert_equal(entries[0], zip_entry_set.find_entry('MiXeDcAsEnAmE')) assert_equal(entries[0], zip_entry_set.find_entry('mixedcasename')) ::Zip.case_insensitive_match = false zip_entry_set = ::Zip::EntrySet.new(entries) assert_equal(entries[0], zip_entry_set.find_entry('MiXeDcAsEnAmE')) assert_nil(zip_entry_set.find_entry('mixedcasename')) end def test_entries_with_sort ::Zip.sort_entries = true assert_equal(ZIP_ENTRIES.sort, @zip_entry_set.entries) ::Zip.sort_entries = false assert_equal(ZIP_ENTRIES, @zip_entry_set.entries) end def test_entries_sorted_in_each ::Zip.sort_entries = true arr = [] @zip_entry_set.each do |entry| arr << entry end assert_equal(ZIP_ENTRIES.sort, arr) end def test_compound new_entry = ::Zip::Entry.new('zf.zip', 'new entry', "new entry's comment") assert_equal(ZIP_ENTRIES.size, @zip_entry_set.size) @zip_entry_set << new_entry assert_equal(ZIP_ENTRIES.size + 1, @zip_entry_set.size) assert(@zip_entry_set.include?(new_entry)) @zip_entry_set.delete(new_entry) assert_equal(ZIP_ENTRIES.size, @zip_entry_set.size) end def test_dup copy = @zip_entry_set.dup assert_equal(@zip_entry_set, copy) # demonstrate that this is a deep copy copy.entries[0].name = 'a totally different name' assert(@zip_entry_set != copy) end def test_parent entries = [ ::Zip::Entry.new('zf.zip', 'a/'), ::Zip::Entry.new('zf.zip', 'a/b/'), ::Zip::Entry.new('zf.zip', 'a/b/c/') ] entry_set = ::Zip::EntrySet.new(entries) assert_nil(entry_set.parent(entries[0])) assert_equal(entries[0], entry_set.parent(entries[1])) assert_equal(entries[1], entry_set.parent(entries[2])) end def test_glob res = @zip_entry_set.glob('name[2-4]') assert_equal(3, res.size) assert_equal(ZIP_ENTRIES[1, 3].sort, res.sort) end def test_glob2 entries = [ ::Zip::Entry.new('zf.zip', 'a/'), ::Zip::Entry.new('zf.zip', 'a/b/b1'), ::Zip::Entry.new('zf.zip', 'a/b/c/'), ::Zip::Entry.new('zf.zip', 'a/b/c/c1') ] entry_set = ::Zip::EntrySet.new(entries) assert_equal(entries[0, 1], entry_set.glob('*')) # assert_equal(entries[FIXME], entry_set.glob("**")) # res = entry_set.glob('a*') # assert_equal(entries.size, res.size) # assert_equal(entry_set.map { |e| e.name }, res.map { |e| e.name }) end def test_glob3 entries = [ ::Zip::Entry.new('zf.zip', 'a/a'), ::Zip::Entry.new('zf.zip', 'a/b'), ::Zip::Entry.new('zf.zip', 'a/c') ] entry_set = ::Zip::EntrySet.new(entries) assert_equal(entries[0, 2].sort, entry_set.glob('a/{a,b}').sort) end end rubyzip-2.3.0/test/entry_test.rb000066400000000000000000000144341363314356200167400ustar00rootroot00000000000000require 'test_helper' class ZipEntryTest < MiniTest::Test include ZipEntryData def test_constructor_and_getters entry = ::Zip::Entry.new(TEST_ZIPFILE, TEST_NAME, TEST_COMMENT, TEST_EXTRA, TEST_COMPRESSED_SIZE, TEST_CRC, TEST_COMPRESSIONMETHOD, TEST_SIZE, TEST_TIME) assert_equal(TEST_COMMENT, entry.comment) assert_equal(TEST_COMPRESSED_SIZE, entry.compressed_size) assert_equal(TEST_CRC, entry.crc) assert_instance_of(::Zip::ExtraField, entry.extra) assert_equal(TEST_COMPRESSIONMETHOD, entry.compression_method) assert_equal(TEST_NAME, entry.name) assert_equal(TEST_SIZE, entry.size) assert_equal(TEST_TIME, entry.time) end def test_is_directory_and_is_file assert(::Zip::Entry.new(TEST_ZIPFILE, 'hello').file?) assert(!::Zip::Entry.new(TEST_ZIPFILE, 'hello').directory?) assert(::Zip::Entry.new(TEST_ZIPFILE, 'dir/hello').file?) assert(!::Zip::Entry.new(TEST_ZIPFILE, 'dir/hello').directory?) assert(::Zip::Entry.new(TEST_ZIPFILE, 'hello/').directory?) assert(!::Zip::Entry.new(TEST_ZIPFILE, 'hello/').file?) assert(::Zip::Entry.new(TEST_ZIPFILE, 'dir/hello/').directory?) assert(!::Zip::Entry.new(TEST_ZIPFILE, 'dir/hello/').file?) end def test_equality entry1 = ::Zip::Entry.new('file.zip', 'name', 'isNotCompared', 'something extra', 123, 1234, ::Zip::Entry::DEFLATED, 10_000) entry2 = ::Zip::Entry.new('file.zip', 'name', 'isNotComparedXXX', 'something extra', 123, 1234, ::Zip::Entry::DEFLATED, 10_000) entry3 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', 'something extra', 123, 1234, ::Zip::Entry::DEFLATED, 10_000) entry4 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', 'something extraXX', 123, 1234, ::Zip::Entry::DEFLATED, 10_000) entry5 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', 'something extraXX', 12, 1234, ::Zip::Entry::DEFLATED, 10_000) entry6 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', 'something extraXX', 12, 123, ::Zip::Entry::DEFLATED, 10_000) entry7 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', 'something extraXX', 12, 123, ::Zip::Entry::STORED, 10_000) entry8 = ::Zip::Entry.new('file.zip', 'name2', 'isNotComparedXXX', 'something extraXX', 12, 123, ::Zip::Entry::STORED, 100_000) assert_equal(entry1, entry1) assert_equal(entry1, entry2) assert(entry2 != entry3) assert(entry3 != entry4) assert(entry4 != entry5) assert(entry5 != entry6) assert(entry6 != entry7) assert(entry7 != entry8) assert(entry7 != 'hello') assert(entry7 != 12) end def test_compare assert_equal(0, (::Zip::Entry.new('zf.zip', 'a') <=> ::Zip::Entry.new('zf.zip', 'a'))) assert_equal(1, (::Zip::Entry.new('zf.zip', 'b') <=> ::Zip::Entry.new('zf.zip', 'a'))) assert_equal(-1, (::Zip::Entry.new('zf.zip', 'a') <=> ::Zip::Entry.new('zf.zip', 'b'))) entries = [ ::Zip::Entry.new('zf.zip', '5'), ::Zip::Entry.new('zf.zip', '1'), ::Zip::Entry.new('zf.zip', '3'), ::Zip::Entry.new('zf.zip', '4'), ::Zip::Entry.new('zf.zip', '0'), ::Zip::Entry.new('zf.zip', '2') ] entries.sort! assert_equal('0', entries[0].to_s) assert_equal('1', entries[1].to_s) assert_equal('2', entries[2].to_s) assert_equal('3', entries[3].to_s) assert_equal('4', entries[4].to_s) assert_equal('5', entries[5].to_s) end def test_parent_as_string entry1 = ::Zip::Entry.new('zf.zip', 'aa') entry2 = ::Zip::Entry.new('zf.zip', 'aa/') entry3 = ::Zip::Entry.new('zf.zip', 'aa/bb') entry4 = ::Zip::Entry.new('zf.zip', 'aa/bb/') entry5 = ::Zip::Entry.new('zf.zip', 'aa/bb/cc') entry6 = ::Zip::Entry.new('zf.zip', 'aa/bb/cc/') assert_nil(entry1.parent_as_string) assert_nil(entry2.parent_as_string) assert_equal('aa/', entry3.parent_as_string) assert_equal('aa/', entry4.parent_as_string) assert_equal('aa/bb/', entry5.parent_as_string) assert_equal('aa/bb/', entry6.parent_as_string) end def test_entry_name_cannot_start_with_slash assert_raises(::Zip::EntryNameError) { ::Zip::Entry.new('zf.zip', '/hej/der') } end def test_store_file_without_compression File.delete('/tmp/no_compress.zip') if File.exist?('/tmp/no_compress.zip') files = Dir[File.join('test/data/globTest', '**', '**')] Zip.setup do |z| z.write_zip64_support = false end zipfile = Zip::File.open('/tmp/no_compress.zip', Zip::File::CREATE) mimetype_entry = Zip::Entry.new(zipfile, # @zipfile 'mimetype', # @name '', # @comment '', # @extra 0, # @compressed_size 0, # @crc Zip::Entry::STORED) # @comppressed_method zipfile.add(mimetype_entry, 'test/data/mimetype') files.each do |file| zipfile.add(file.sub('test/data/globTest/', ''), file) end zipfile.close f = File.open('/tmp/no_compress.zip', 'rb') first_100_bytes = f.read(100) f.close assert_match(/mimetypeapplication\/epub\+zip/, first_100_bytes) end def test_encrypted? entry = Zip::Entry.new entry.gp_flags = 1 assert_equal(true, entry.encrypted?) entry.gp_flags = 0 assert_equal(false, entry.encrypted?) end def test_incomplete? entry = Zip::Entry.new entry.gp_flags = 8 assert_equal(true, entry.incomplete?) entry.gp_flags = 0 assert_equal(false, entry.incomplete?) end end rubyzip-2.3.0/test/errors_test.rb000066400000000000000000000014501363314356200171050ustar00rootroot00000000000000require 'test_helper' class ErrorsTest < MiniTest::Test def test_rescue_legacy_zip_error raise ::Zip::Error rescue ::Zip::ZipError end def test_rescue_legacy_zip_entry_exists_error raise ::Zip::EntryExistsError rescue ::Zip::ZipEntryExistsError end def test_rescue_legacy_zip_destination_file_exists_error raise ::Zip::DestinationFileExistsError rescue ::Zip::ZipDestinationFileExistsError end def test_rescue_legacy_zip_compression_method_error raise ::Zip::CompressionMethodError rescue ::Zip::ZipCompressionMethodError end def test_rescue_legacy_zip_entry_name_error raise ::Zip::EntryNameError rescue ::Zip::ZipEntryNameError end def test_rescue_legacy_zip_internal_error raise ::Zip::InternalError rescue ::Zip::ZipInternalError end end rubyzip-2.3.0/test/extra_field_test.rb000066400000000000000000000044161363314356200200640ustar00rootroot00000000000000require 'test_helper' class ZipExtraFieldTest < MiniTest::Test def test_new extra_pure = ::Zip::ExtraField.new('') extra_withstr = ::Zip::ExtraField.new('foo') assert_instance_of(::Zip::ExtraField, extra_pure) assert_instance_of(::Zip::ExtraField, extra_withstr) end def test_unknownfield extra = ::Zip::ExtraField.new('foo') assert_equal(extra['Unknown'], 'foo') extra.merge('a') assert_equal(extra['Unknown'], 'fooa') extra.merge('barbaz') assert_equal(extra.to_s, 'fooabarbaz') end def test_ntfs str = "\x0A\x00 \x00\x00\x00\x00\x00\x01\x00\x18\x00\xC0\x81\x17\xE8B\xCE\xCF\x01\xC0\x81\x17\xE8B\xCE\xCF\x01\xC0\x81\x17\xE8B\xCE\xCF\x01" extra = ::Zip::ExtraField.new(str) assert(extra.member?('NTFS')) t = ::Zip::DOSTime.at(1_410_496_497.405178) assert_equal(t, extra['NTFS'].mtime) assert_equal(t, extra['NTFS'].atime) assert_equal(t, extra['NTFS'].ctime) end def test_merge str = "UT\x5\0\x3\250$\r@Ux\0\0" extra1 = ::Zip::ExtraField.new('') extra2 = ::Zip::ExtraField.new(str) assert(!extra1.member?('UniversalTime')) assert(extra2.member?('UniversalTime')) extra1.merge(str) assert_equal(extra1['UniversalTime'].mtime, extra2['UniversalTime'].mtime) end def test_length str = "UT\x5\0\x3\250$\r@Ux\0\0Te\0\0testit" extra = ::Zip::ExtraField.new(str) assert_equal(extra.local_size, extra.to_local_bin.size) assert_equal(extra.c_dir_size, extra.to_c_dir_bin.size) extra.merge('foo') assert_equal(extra.local_size, extra.to_local_bin.size) assert_equal(extra.c_dir_size, extra.to_c_dir_bin.size) end def test_to_s str = "UT\x5\0\x3\250$\r@Ux\0\0Te\0\0testit" extra = ::Zip::ExtraField.new(str) assert_instance_of(String, extra.to_s) s = extra.to_s extra.merge('foo') assert_equal(s.length + 3, extra.to_s.length) end def test_equality str = "UT\x5\0\x3\250$\r@" extra1 = ::Zip::ExtraField.new(str) extra2 = ::Zip::ExtraField.new(str) extra3 = ::Zip::ExtraField.new(str) assert_equal(extra1, extra2) extra2['UniversalTime'].mtime = ::Zip::DOSTime.now assert(extra1 != extra2) extra3.create('IUnix') assert(extra1 != extra3) extra1.create('IUnix') assert_equal(extra1, extra3) end end rubyzip-2.3.0/test/extra_field_ut_test.rb000066400000000000000000000047521363314356200205770ustar00rootroot00000000000000require 'test_helper' class ZipExtraFieldUTTest < MiniTest::Test PARSE_TESTS = [ ["UT\x05\x00\x01PS>A", 0b001, true, true, false], ["UT\x05\x00\x02PS>A", 0b010, false, true, true], ["UT\x05\x00\x04PS>A", 0b100, true, false, true], ["UT\x09\x00\x03PS>APS>A", 0b011, false, true, false], ["UT\x09\x00\x05PS>APS>A", 0b101, true, false, false], ["UT\x09\x00\x06PS>APS>A", 0b110, false, false, true], ["UT\x13\x00\x07PS>APS>APS>A", 0b111, false, false, false] ] def test_parse PARSE_TESTS.each do |bin, flags, a, c, m| ut = ::Zip::ExtraField::UniversalTime.new(bin) assert_equal(flags, ut.flag) assert(ut.atime.nil? == a) assert(ut.ctime.nil? == c) assert(ut.mtime.nil? == m) end end def test_parse_size_zero ut = ::Zip::ExtraField::UniversalTime.new("UT\x00") assert_equal(0b000, ut.flag) assert_nil(ut.atime) assert_nil(ut.ctime) assert_nil(ut.mtime) end def test_parse_size_nil ut = ::Zip::ExtraField::UniversalTime.new('UT') assert_equal(0b000, ut.flag) assert_nil(ut.atime) assert_nil(ut.ctime) assert_nil(ut.mtime) end def test_parse_nil ut = ::Zip::ExtraField::UniversalTime.new assert_equal(0b000, ut.flag) assert_nil(ut.atime) assert_nil(ut.ctime) assert_nil(ut.mtime) end def test_set_clear_times time = ::Zip::DOSTime.now ut = ::Zip::ExtraField::UniversalTime.new assert_equal(0b000, ut.flag) ut.mtime = time assert_equal(0b001, ut.flag) assert_equal(time, ut.mtime) ut.ctime = time assert_equal(0b101, ut.flag) assert_equal(time, ut.ctime) ut.atime = time assert_equal(0b111, ut.flag) assert_equal(time, ut.atime) ut.ctime = nil assert_equal(0b011, ut.flag) assert_nil ut.ctime ut.mtime = nil assert_equal(0b010, ut.flag) assert_nil ut.mtime ut.atime = nil assert_equal(0b000, ut.flag) assert_nil ut.atime end def test_pack time = ::Zip::DOSTime.at('PS>A'.unpack1('l<')) ut = ::Zip::ExtraField::UniversalTime.new assert_equal("\x00", ut.pack_for_local) assert_equal("\x00", ut.pack_for_c_dir) ut.mtime = time assert_equal("\x01PS>A", ut.pack_for_local) assert_equal("\x01PS>A", ut.pack_for_c_dir) ut.atime = time assert_equal("\x03PS>APS>A", ut.pack_for_local) assert_equal("\x03PS>A", ut.pack_for_c_dir) ut.ctime = time assert_equal("\x07PS>APS>APS>A", ut.pack_for_local) assert_equal("\x07PS>A", ut.pack_for_c_dir) end end rubyzip-2.3.0/test/file_extract_directory_test.rb000066400000000000000000000025161363314356200223320ustar00rootroot00000000000000require 'test_helper' class ZipFileExtractDirectoryTest < MiniTest::Test include CommonZipFileFixture TEST_OUT_NAME = 'test/data/generated/emptyOutDir' def open_zip(&a_proc) assert(!a_proc.nil?) ::Zip::File.open(TestZipFile::TEST_ZIP4.zip_name, &a_proc) end def extract_test_dir(&a_proc) open_zip do |zf| zf.extract(TestFiles::EMPTY_TEST_DIR, TEST_OUT_NAME, &a_proc) end end def setup super Dir.rmdir(TEST_OUT_NAME) if File.directory? TEST_OUT_NAME File.delete(TEST_OUT_NAME) if File.exist? TEST_OUT_NAME end def test_extract_directory extract_test_dir assert(File.directory?(TEST_OUT_NAME)) end def test_extract_directory_exists_as_dir Dir.mkdir TEST_OUT_NAME extract_test_dir assert(File.directory?(TEST_OUT_NAME)) end def test_extract_directory_exists_as_file File.open(TEST_OUT_NAME, 'w') { |f| f.puts 'something' } assert_raises(::Zip::DestinationFileExistsError) { extract_test_dir } end def test_extract_directory_exists_as_file_overwrite File.open(TEST_OUT_NAME, 'w') { |f| f.puts 'something' } called = false extract_test_dir do |entry, dest_path| called = true assert_equal(TEST_OUT_NAME, dest_path) assert(entry.directory?) true end assert(called) assert(File.directory?(TEST_OUT_NAME)) end end rubyzip-2.3.0/test/file_extract_test.rb000066400000000000000000000104121363314356200202400ustar00rootroot00000000000000require 'test_helper' class ZipFileExtractTest < MiniTest::Test include CommonZipFileFixture EXTRACTED_FILENAME = 'test/data/generated/extEntry' ENTRY_TO_EXTRACT, *REMAINING_ENTRIES = TEST_ZIP.entry_names.reverse def setup super ::File.delete(EXTRACTED_FILENAME) if ::File.exist?(EXTRACTED_FILENAME) end def teardown ::Zip.reset! end def test_extract ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.extract(ENTRY_TO_EXTRACT, EXTRACTED_FILENAME) assert(File.exist?(EXTRACTED_FILENAME)) AssertEntry.assert_contents(EXTRACTED_FILENAME, zf.get_input_stream(ENTRY_TO_EXTRACT, &:read)) ::File.unlink(EXTRACTED_FILENAME) entry = zf.get_entry(ENTRY_TO_EXTRACT) entry.extract(EXTRACTED_FILENAME) assert(File.exist?(EXTRACTED_FILENAME)) AssertEntry.assert_contents(EXTRACTED_FILENAME, entry.get_input_stream(&:read)) end end def test_extract_exists text = 'written text' ::File.open(EXTRACTED_FILENAME, 'w') { |f| f.write(text) } assert_raises(::Zip::DestinationFileExistsError) do ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.extract(zf.entries.first, EXTRACTED_FILENAME) end end File.open(EXTRACTED_FILENAME, 'r') do |f| assert_equal(text, f.read) end end def test_extract_exists_overwrite text = 'written text' ::File.open(EXTRACTED_FILENAME, 'w') { |f| f.write(text) } called_correctly = false ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.extract(zf.entries.first, EXTRACTED_FILENAME) do |entry, extract_loc| called_correctly = zf.entries.first == entry && extract_loc == EXTRACTED_FILENAME true end end assert(called_correctly) ::File.open(EXTRACTED_FILENAME, 'r') do |f| assert(text != f.read) end end def test_extract_non_entry zf = ::Zip::File.new(TEST_ZIP.zip_name) assert_raises(Errno::ENOENT) { zf.extract('nonExistingEntry', 'nonExistingEntry') } ensure zf.close if zf end def test_extract_non_entry_2 out_file = 'outfile' assert_raises(Errno::ENOENT) do zf = ::Zip::File.new(TEST_ZIP.zip_name) non_entry = 'hotdog-diddelidoo' assert(!zf.entries.include?(non_entry)) zf.extract(non_entry, out_file) zf.close end assert(!File.exist?(out_file)) end def test_extract_incorrect_size # The uncompressed size fields in the zip file cannot be trusted. This makes # it harder for callers to validate the sizes of the files they are # extracting, which can lead to denial of service. See also # https://en.wikipedia.org/wiki/Zip_bomb Dir.mktmpdir do |tmp| real_zip = File.join(tmp, 'real.zip') fake_zip = File.join(tmp, 'fake.zip') file_name = 'a' true_size = 500_000 fake_size = 1 ::Zip::File.open(real_zip, ::Zip::File::CREATE) do |zf| zf.get_output_stream(file_name) do |os| os.write 'a' * true_size end end compressed_size = nil ::Zip::File.open(real_zip) do |zf| a_entry = zf.find_entry(file_name) compressed_size = a_entry.compressed_size assert_equal true_size, a_entry.size end true_size_bytes = [compressed_size, true_size, file_name.size].pack('LLS') fake_size_bytes = [compressed_size, fake_size, file_name.size].pack('LLS') data = File.binread(real_zip) assert data.include?(true_size_bytes) data.gsub! true_size_bytes, fake_size_bytes File.open(fake_zip, 'wb') do |file| file.write data end Dir.chdir tmp do ::Zip::File.open(fake_zip) do |zf| a_entry = zf.find_entry(file_name) assert_equal fake_size, a_entry.size ::Zip.validate_entry_sizes = false assert_output('', /.+\'a\'.+1B.+/) do a_entry.extract end assert_equal true_size, File.size(file_name) FileUtils.rm file_name ::Zip.validate_entry_sizes = true error = assert_raises ::Zip::EntrySizeError do a_entry.extract end assert_equal \ "entry 'a' should be 1B, but is larger when inflated.", error.message end end end end end rubyzip-2.3.0/test/file_options_test.rb000066400000000000000000000071261363314356200202710ustar00rootroot00000000000000require 'test_helper' class FileOptionsTest < MiniTest::Test ZIPPATH = ::File.join(Dir.tmpdir, 'options.zip').freeze TXTPATH = ::File.expand_path(::File.join('data', 'file1.txt'), __dir__).freeze TXTPATH_600 = ::File.join(Dir.tmpdir, 'file1.600.txt').freeze TXTPATH_755 = ::File.join(Dir.tmpdir, 'file1.755.txt').freeze EXTPATH_1 = ::File.join(Dir.tmpdir, 'extracted_1.txt').freeze EXTPATH_2 = ::File.join(Dir.tmpdir, 'extracted_2.txt').freeze EXTPATH_3 = ::File.join(Dir.tmpdir, 'extracted_3.txt').freeze ENTRY_1 = 'entry_1.txt'.freeze ENTRY_2 = 'entry_2.txt'.freeze ENTRY_3 = 'entry_3.txt'.freeze def teardown ::File.unlink(ZIPPATH) if ::File.exist?(ZIPPATH) ::File.unlink(EXTPATH_1) if ::File.exist?(EXTPATH_1) ::File.unlink(EXTPATH_2) if ::File.exist?(EXTPATH_2) ::File.unlink(EXTPATH_3) if ::File.exist?(EXTPATH_3) ::File.unlink(TXTPATH_600) if ::File.exist?(TXTPATH_600) ::File.unlink(TXTPATH_755) if ::File.exist?(TXTPATH_755) end def test_restore_permissions # Copy and set up files with different permissions. ::FileUtils.cp(TXTPATH, TXTPATH_600) ::File.chmod(0o600, TXTPATH_600) ::FileUtils.cp(TXTPATH, TXTPATH_755) ::File.chmod(0o755, TXTPATH_755) ::Zip::File.open(ZIPPATH, true) do |zip| zip.add(ENTRY_1, TXTPATH) zip.add(ENTRY_2, TXTPATH_600) zip.add(ENTRY_3, TXTPATH_755) end ::Zip::File.open(ZIPPATH, false, restore_permissions: true) do |zip| zip.extract(ENTRY_1, EXTPATH_1) zip.extract(ENTRY_2, EXTPATH_2) zip.extract(ENTRY_3, EXTPATH_3) end assert_equal(::File.stat(TXTPATH).mode, ::File.stat(EXTPATH_1).mode) assert_equal(::File.stat(TXTPATH_600).mode, ::File.stat(EXTPATH_2).mode) assert_equal(::File.stat(TXTPATH_755).mode, ::File.stat(EXTPATH_3).mode) end def test_restore_times_true ::Zip::File.open(ZIPPATH, true) do |zip| zip.add(ENTRY_1, TXTPATH) zip.add_stored(ENTRY_2, TXTPATH) end ::Zip::File.open(ZIPPATH, false, restore_times: true) do |zip| zip.extract(ENTRY_1, EXTPATH_1) zip.extract(ENTRY_2, EXTPATH_2) end assert_time_equal(::File.mtime(TXTPATH), ::File.mtime(EXTPATH_1)) assert_time_equal(::File.mtime(TXTPATH), ::File.mtime(EXTPATH_2)) end def test_restore_times_false ::Zip::File.open(ZIPPATH, true) do |zip| zip.add(ENTRY_1, TXTPATH) zip.add_stored(ENTRY_2, TXTPATH) end ::Zip::File.open(ZIPPATH, false, restore_times: false) do |zip| zip.extract(ENTRY_1, EXTPATH_1) zip.extract(ENTRY_2, EXTPATH_2) end assert_time_equal(::Time.now, ::File.mtime(EXTPATH_1)) assert_time_equal(::Time.now, ::File.mtime(EXTPATH_2)) end def test_get_find_consistency testzip = ::File.expand_path(::File.join('data', 'globTest.zip'), __dir__) file_f = ::File.expand_path('f_test.txt', Dir.tmpdir) file_g = ::File.expand_path('g_test.txt', Dir.tmpdir) ::Zip::File.open(testzip) do |zip| e1 = zip.find_entry('globTest/food.txt') e1.extract(file_f) e2 = zip.get_entry('globTest/food.txt') e2.extract(file_g) end assert_time_equal(::File.mtime(file_f), ::File.mtime(file_g)) ensure ::File.unlink(file_f) ::File.unlink(file_g) end private # Method to compare file times. DOS times only have 2 second accuracy. def assert_time_equal(expected, actual) assert_equal(expected.year, actual.year) assert_equal(expected.month, actual.month) assert_equal(expected.day, actual.day) assert_equal(expected.hour, actual.hour) assert_equal(expected.min, actual.min) assert_in_delta(expected.sec, actual.sec, 1) end end rubyzip-2.3.0/test/file_permissions_test.rb000066400000000000000000000025011363314356200211410ustar00rootroot00000000000000require 'test_helper' class FilePermissionsTest < MiniTest::Test ZIPNAME = File.join(File.dirname(__FILE__), 'umask.zip') FILENAME = File.join(File.dirname(__FILE__), 'umask.txt') def teardown ::File.unlink(ZIPNAME) ::File.unlink(FILENAME) end def test_current_umask create_files assert_matching_permissions FILENAME, ZIPNAME end def test_umask_000 apply_umask(0o000) do create_files end assert_matching_permissions FILENAME, ZIPNAME end def test_umask_066 apply_umask(0o066) do create_files end assert_matching_permissions FILENAME, ZIPNAME end def test_umask_027 apply_umask(0o027) do create_files end assert_matching_permissions FILENAME, ZIPNAME end def assert_matching_permissions(expected_file, actual_file) assert_equal( ::File.stat(expected_file).mode.to_s(8).rjust(4, '0'), ::File.stat(actual_file).mode.to_s(8).rjust(4, '0') ) end def create_files ::Zip::File.open(ZIPNAME, ::Zip::File::CREATE) do |zip| zip.comment = 'test' end ::File.open(FILENAME, 'w') do |file| file << 'test' end end # If anything goes wrong, make sure the umask is restored. def apply_umask(umask) saved_umask = ::File.umask(umask) yield ensure ::File.umask(saved_umask) end end rubyzip-2.3.0/test/file_split_test.rb000066400000000000000000000034361363314356200177310ustar00rootroot00000000000000require 'test_helper' class ZipFileSplitTest < MiniTest::Test TEST_ZIP = TestZipFile::TEST_ZIP2.clone TEST_ZIP.zip_name = 'large_zip_file.zip' EXTRACTED_FILENAME = 'test/data/generated/extEntrySplit' UNSPLITTED_FILENAME = 'test/data/generated/unsplitted.zip' ENTRY_TO_EXTRACT = TEST_ZIP.entry_names.first def setup FileUtils.cp(TestZipFile::TEST_ZIP2.zip_name, TEST_ZIP.zip_name) end def teardown File.delete(TEST_ZIP.zip_name) File.delete(UNSPLITTED_FILENAME) if File.exist?(UNSPLITTED_FILENAME) Dir["#{TEST_ZIP.zip_name}.*"].each do |zip_file_name| File.delete(zip_file_name) if File.exist?(zip_file_name) end end def test_split_method_respond assert_respond_to ::Zip::File, :split, 'Does not have split class method' end def test_split result = ::Zip::File.split(TEST_ZIP.zip_name, 65_536, false) return if result.nil? Dir["#{TEST_ZIP.zip_name}.*"].sort.each_with_index do |zip_file_name, index| File.open(zip_file_name, 'rb') do |zip_file| zip_file.read([::Zip::File::SPLIT_SIGNATURE].pack('V').size) if index == 0 File.open(UNSPLITTED_FILENAME, 'ab') do |file| file << zip_file.read end end end ::Zip::File.open(UNSPLITTED_FILENAME) do |zf| zf.extract(ENTRY_TO_EXTRACT, EXTRACTED_FILENAME) assert(File.exist?(EXTRACTED_FILENAME)) AssertEntry.assert_contents(EXTRACTED_FILENAME, zf.get_input_stream(ENTRY_TO_EXTRACT, &:read)) File.unlink(EXTRACTED_FILENAME) entry = zf.get_entry(ENTRY_TO_EXTRACT) entry.extract(EXTRACTED_FILENAME) assert(File.exist?(EXTRACTED_FILENAME)) AssertEntry.assert_contents(EXTRACTED_FILENAME, entry.get_input_stream(&:read)) end end end rubyzip-2.3.0/test/file_test.rb000066400000000000000000000522061363314356200165150ustar00rootroot00000000000000require 'test_helper' class ZipFileTest < MiniTest::Test include CommonZipFileFixture include ZipEntryData OK_DELETE_FILE = 'test/data/generated/okToDelete.txt' OK_DELETE_MOVED_FILE = 'test/data/generated/okToDeleteMoved.txt' def teardown ::Zip.write_zip64_support = false end def test_create_from_scratch_to_buffer comment = 'a short comment' buffer = ::Zip::File.add_buffer do |zf| zf.get_output_stream('myFile') { |os| os.write 'myFile contains just this' } zf.mkdir('dir1') zf.comment = comment end ::File.open(EMPTY_FILENAME, 'wb') { |file| file.write buffer.string } zf_read = ::Zip::File.new(EMPTY_FILENAME) assert_equal(comment, zf_read.comment) assert_equal(2, zf_read.entries.length) end def test_create_from_scratch comment = 'a short comment' zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) zf.get_output_stream('myFile') { |os| os.write 'myFile contains just this' } zf.mkdir('dir1') zf.comment = comment zf.close zf_read = ::Zip::File.new(EMPTY_FILENAME) assert_equal(comment, zf_read.comment) assert_equal(2, zf_read.entries.length) end def test_create_from_scratch_with_old_create_parameter comment = 'a short comment' zf = ::Zip::File.new(EMPTY_FILENAME, 1) zf.get_output_stream('myFile') { |os| os.write 'myFile contains just this' } zf.mkdir('dir1') zf.comment = comment zf.close zf_read = ::Zip::File.new(EMPTY_FILENAME) assert_equal(comment, zf_read.comment) assert_equal(2, zf_read.entries.length) end def test_get_input_stream_stored_with_gpflag_bit3 ::Zip::File.open('test/data/gpbit3stored.zip') do |zf| assert_equal("foo\n", zf.read('foo.txt')) end end def test_get_output_stream count = nil ::Zip::File.open(TEST_ZIP.zip_name) do |zf| count = zf.size zf.get_output_stream('new_entry.txt') do |os| os.write 'Putting stuff in new_entry.txt' end assert_equal(count + 1, zf.size) assert_equal('Putting stuff in new_entry.txt', zf.read('new_entry.txt')) zf.get_output_stream(zf.get_entry('test/data/generated/empty.txt')) do |os| os.write 'Putting stuff in data/generated/empty.txt' end assert_equal(count + 1, zf.size) assert_equal('Putting stuff in data/generated/empty.txt', zf.read('test/data/generated/empty.txt')) custom_entry_args = [TEST_COMMENT, TEST_EXTRA, TEST_COMPRESSED_SIZE, TEST_CRC, ::Zip::Entry::STORED, TEST_SIZE, TEST_TIME] zf.get_output_stream('entry_with_custom_args.txt', nil, *custom_entry_args) do |os| os.write 'Some data' end assert_equal(count + 2, zf.size) entry = zf.get_entry('entry_with_custom_args.txt') assert_equal(custom_entry_args[0], entry.comment) assert_equal(custom_entry_args[2], entry.compressed_size) assert_equal(custom_entry_args[3], entry.crc) assert_equal(custom_entry_args[4], entry.compression_method) assert_equal(custom_entry_args[5], entry.size) assert_equal(custom_entry_args[6], entry.time) zf.get_output_stream('entry.bin') do |os| os.write(::File.open('test/data/generated/5entry.zip', 'rb').read) end end ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(count + 3, zf.size) assert_equal('Putting stuff in new_entry.txt', zf.read('new_entry.txt')) assert_equal('Putting stuff in data/generated/empty.txt', zf.read('test/data/generated/empty.txt')) assert_equal(File.open('test/data/generated/5entry.zip', 'rb').read, zf.read('entry.bin')) end end def test_open_buffer_with_string string = File.read('test/data/rubycode.zip') ::Zip::File.open_buffer string do |zf| assert zf.entries.map(&:name).include?('zippedruby1.rb') end end def test_open_buffer_with_stringio string_io = StringIO.new File.read('test/data/rubycode.zip') ::Zip::File.open_buffer string_io do |zf| assert zf.entries.map(&:name).include?('zippedruby1.rb') end end def test_close_buffer_with_stringio string_io = StringIO.new File.read('test/data/rubycode.zip') zf = ::Zip::File.open_buffer string_io assert_nil zf.close end def test_open_buffer_no_op_does_not_change_file Dir.mktmpdir do |tmp| test_zip = File.join(tmp, 'test.zip') FileUtils.cp 'test/data/rubycode.zip', test_zip # Note: this may change the file if it is opened with r+b instead of rb. # The 'extra fields' in this particular zip file get reordered. File.open(test_zip, 'rb') do |file| Zip::File.open_buffer(file) do nil # do nothing end end assert_equal \ File.binread('test/data/rubycode.zip'), File.binread(test_zip) end end def test_open_buffer_close_does_not_change_file Dir.mktmpdir do |tmp| test_zip = File.join(tmp, 'test.zip') FileUtils.cp 'test/data/rubycode.zip', test_zip File.open(test_zip, 'rb') do |file| zf = Zip::File.open_buffer(file) refute zf.commit_required? assert_nil zf.close end assert_equal \ File.binread('test/data/rubycode.zip'), File.binread(test_zip) end end def test_open_buffer_with_io_and_block File.open('test/data/rubycode.zip') do |io| io.set_encoding(Encoding::BINARY) # not strictly required but can be set Zip::File.open_buffer(io) do |zip_io| # left empty on purpose end end end def test_open_buffer_without_block string_io = StringIO.new File.read('test/data/rubycode.zip') zf = ::Zip::File.open_buffer string_io assert zf.entries.map(&:name).include?('zippedruby1.rb') end def test_cleans_up_tempfiles_after_close zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) zf.get_output_stream('myFile') do |os| @tempfile_path = os.path os.write 'myFile contains just this' end assert_equal(true, File.exist?(@tempfile_path)) zf.close assert_equal(false, File.exist?(@tempfile_path)) end def test_add src_file = 'test/data/file2.txt' entry_name = 'newEntryName.rb' assert(::File.exist?(src_file)) zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) zf.add(entry_name, src_file) zf.close zf_read = ::Zip::File.new(EMPTY_FILENAME) assert_equal('', zf_read.comment) assert_equal(1, zf_read.entries.length) assert_equal(entry_name, zf_read.entries.first.name) AssertEntry.assert_contents(src_file, zf_read.get_input_stream(entry_name, &:read)) end def test_add_stored src_file = 'test/data/file2.txt' entry_name = 'newEntryName.rb' assert(::File.exist?(src_file)) zf = ::Zip::File.new(EMPTY_FILENAME, ::Zip::File::CREATE) zf.add_stored(entry_name, src_file) zf.close zf_read = ::Zip::File.new(EMPTY_FILENAME) entry = zf_read.entries.first assert_equal('', zf_read.comment) assert_equal(1, zf_read.entries.length) assert_equal(entry_name, entry.name) assert_equal(File.size(src_file), entry.size) assert_equal(entry.size, entry.compressed_size) assert_equal(::Zip::Entry::STORED, entry.compression_method) AssertEntry.assert_contents(src_file, zf_read.get_input_stream(entry_name, &:read)) end def test_recover_permissions_after_add_files_to_archive src_zip = TEST_ZIP.zip_name ::File.chmod(0o664, src_zip) src_file = 'test/data/file2.txt' entry_name = 'newEntryName.rb' assert_equal(::File.stat(src_zip).mode, 0o100664) assert(::File.exist?(src_zip)) zf = ::Zip::File.new(src_zip, ::Zip::File::CREATE) zf.add(entry_name, src_file) zf.close assert_equal(::File.stat(src_zip).mode, 0o100664) end def test_add_existing_entry_name assert_raises(::Zip::EntryExistsError) do ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.add(zf.entries.first.name, 'test/data/file2.txt') end end end def test_add_existing_entry_name_replace called = false replaced_entry = nil ::Zip::File.open(TEST_ZIP.zip_name) do |zf| replaced_entry = zf.entries.first.name zf.add(replaced_entry, 'test/data/file2.txt') do called = true true end end assert(called) ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_contains(zf, replaced_entry, 'test/data/file2.txt') end end def test_add_directory ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.add(TestFiles::EMPTY_TEST_DIR, TestFiles::EMPTY_TEST_DIR) end ::Zip::File.open(TEST_ZIP.zip_name) do |zf| dir_entry = zf.entries.detect do |e| e.name == TestFiles::EMPTY_TEST_DIR + '/' end assert(dir_entry.directory?) end end def test_remove entry, *remaining = TEST_ZIP.entry_names FileUtils.cp(TestZipFile::TEST_ZIP2.zip_name, TEST_ZIP.zip_name) zf = ::Zip::File.new(TEST_ZIP.zip_name) assert(zf.entries.map(&:name).include?(entry)) zf.remove(entry) assert(!zf.entries.map(&:name).include?(entry)) assert_equal(zf.entries.map(&:name).sort, remaining.sort) zf.close zf_read = ::Zip::File.new(TEST_ZIP.zip_name) assert(!zf_read.entries.map(&:name).include?(entry)) assert_equal(zf_read.entries.map(&:name).sort, remaining.sort) zf_read.close end def test_rename entry, * = TEST_ZIP.entry_names zf = ::Zip::File.new(TEST_ZIP.zip_name) assert(zf.entries.map(&:name).include?(entry)) contents = zf.read(entry) new_name = 'changed entry name' assert(!zf.entries.map(&:name).include?(new_name)) zf.rename(entry, new_name) assert(zf.entries.map(&:name).include?(new_name)) assert_equal(contents, zf.read(new_name)) zf.close zf_read = ::Zip::File.new(TEST_ZIP.zip_name) assert(zf_read.entries.map(&:name).include?(new_name)) assert_equal(contents, zf_read.read(new_name)) zf_read.close end def test_rename_with_each zf_name = 'test_rename_zip.zip' ::File.unlink(zf_name) if ::File.exist?(zf_name) arr = [] arr_renamed = [] ::Zip::File.open(zf_name, ::Zip::File::CREATE) do |zf| zf.mkdir('test') arr << 'test/' arr_renamed << 'Ztest/' %w[a b c d].each do |f| zf.get_output_stream("test/#{f}") { |file| file.puts 'aaaa' } arr << "test/#{f}" arr_renamed << "Ztest/#{f}" end end zf = ::Zip::File.open(zf_name) assert_equal(zf.entries.map(&:name), arr) zf.close Zip::File.open(zf_name, 'wb') do |z| z.each do |f| z.rename(f, "Z#{f.name}") end end zf = ::Zip::File.open(zf_name) assert_equal(zf.entries.map(&:name), arr_renamed) zf.close ::File.unlink(zf_name) if ::File.exist?(zf_name) end def test_rename_to_existing_entry old_entries = nil ::Zip::File.open(TEST_ZIP.zip_name) { |zf| old_entries = zf.entries } assert_raises(::Zip::EntryExistsError) do ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.rename(zf.entries[0], zf.entries[1].name) end end ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(old_entries.sort.map(&:name), zf.entries.sort.map(&:name)) end end def test_rename_to_existing_entry_overwrite old_entries = nil ::Zip::File.open(TEST_ZIP.zip_name) { |zf| old_entries = zf.entries } called = false new_entry_name = nil ::Zip::File.open(TEST_ZIP.zip_name) do |zf| new_entry_name = zf.entries[0].name zf.rename(zf.entries[0], zf.entries[1].name) do called = true true end end assert(called) old_entries.delete_if { |e| e.name == new_entry_name } ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(old_entries.sort.map(&:name), zf.entries.sort.map(&:name)) end end def test_rename_non_entry non_entry = 'bogusEntry' target_entry = 'target_entryName' zf = ::Zip::File.new(TEST_ZIP.zip_name) assert(!zf.entries.include?(non_entry)) assert_raises(Errno::ENOENT) { zf.rename(non_entry, target_entry) } zf.commit assert(!zf.entries.include?(target_entry)) ensure zf.close end def test_rename_entry_to_existing_entry entry1, entry2, * = TEST_ZIP.entry_names zf = ::Zip::File.new(TEST_ZIP.zip_name) assert_raises(::Zip::EntryExistsError) { zf.rename(entry1, entry2) } ensure zf.close end def test_replace replace_entry = TEST_ZIP.entry_names[2] replace_src = 'test/data/file2.txt' zf = ::Zip::File.new(TEST_ZIP.zip_name) zf.replace(replace_entry, replace_src) zf.close zf_read = ::Zip::File.new(TEST_ZIP.zip_name) AssertEntry.assert_contents( replace_src, zf_read.get_input_stream(replace_entry, &:read) ) AssertEntry.assert_contents( TEST_ZIP.entry_names[0], zf_read.get_input_stream(TEST_ZIP.entry_names[0], &:read) ) AssertEntry.assert_contents( TEST_ZIP.entry_names[1], zf_read.get_input_stream(TEST_ZIP.entry_names[1], &:read) ) AssertEntry.assert_contents( TEST_ZIP.entry_names[3], zf_read.get_input_stream(TEST_ZIP.entry_names[3], &:read) ) zf_read.close end def test_replace_non_entry replace_entry = 'nonExistingEntryname' ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_raises(Errno::ENOENT) do zf.replace(replace_entry, 'test/data/file2.txt') end end end def test_commit new_name = 'renamedFirst' zf = ::Zip::File.new(TEST_ZIP.zip_name) old_name = zf.entries.first zf.rename(old_name, new_name) zf.commit zf_read = ::Zip::File.new(TEST_ZIP.zip_name) refute_nil(zf_read.entries.detect { |e| e.name == new_name }) assert_nil(zf_read.entries.detect { |e| e.name == old_name }) zf_read.close zf.close res = system("unzip -tqq #{TEST_ZIP.zip_name}") assert_equal(res, true) end def test_double_commit(filename = 'test/data/generated/double_commit_test.zip') ::FileUtils.touch('test/data/generated/test_double_commit1.txt') ::FileUtils.touch('test/data/generated/test_double_commit2.txt') zf = ::Zip::File.open(filename, ::Zip::File::CREATE) zf.add('test1.txt', 'test/data/generated/test_double_commit1.txt') zf.commit zf.add('test2.txt', 'test/data/generated/test_double_commit2.txt') zf.commit zf.close zf2 = ::Zip::File.open(filename) refute_nil(zf2.entries.detect { |e| e.name == 'test1.txt' }) refute_nil(zf2.entries.detect { |e| e.name == 'test2.txt' }) res = system("unzip -tqq #{filename}") assert_equal(res, true) end def test_double_commit_zip64 ::Zip.write_zip64_support = true test_double_commit('test/data/generated/double_commit_test64.zip') end def test_write_buffer new_name = 'renamedFirst' zf = ::Zip::File.new(TEST_ZIP.zip_name) old_name = zf.entries.first zf.rename(old_name, new_name) io = ::StringIO.new('') buffer = zf.write_buffer(io) File.open(TEST_ZIP.zip_name, 'wb') { |f| f.write buffer.string } zf_read = ::Zip::File.new(TEST_ZIP.zip_name) refute_nil(zf_read.entries.detect { |e| e.name == new_name }) assert_nil(zf_read.entries.detect { |e| e.name == old_name }) zf_read.close zf.close end # This test tests that after commit, you # can delete the file you used to add the entry to the zip file # with def test_commit_use_zip_entry FileUtils.cp(TestFiles::RANDOM_ASCII_FILE1, OK_DELETE_FILE) zf = ::Zip::File.open(TEST_ZIP.zip_name) zf.add('okToDelete.txt', OK_DELETE_FILE) assert_contains(zf, 'okToDelete.txt') zf.commit File.rename(OK_DELETE_FILE, OK_DELETE_MOVED_FILE) assert_contains(zf, 'okToDelete.txt', OK_DELETE_MOVED_FILE) end # def test_close # zf = ZipFile.new(TEST_ZIP.zip_name) # zf.close # assert_raises(IOError) { # zf.extract(TEST_ZIP.entry_names.first, "hullubullu") # } # end def test_compound1 renamed_name = 'renamed_name' filename_to_remove = '' begin zf = ::Zip::File.new(TEST_ZIP.zip_name) orig_entries = zf.entries.dup assert_not_contains(zf, TestFiles::RANDOM_ASCII_FILE1) zf.add(TestFiles::RANDOM_ASCII_FILE1, TestFiles::RANDOM_ASCII_FILE1) assert_contains(zf, TestFiles::RANDOM_ASCII_FILE1) entry_to_rename = zf.entries.find do |entry| entry.name.match('longAscii') end zf.rename(entry_to_rename, renamed_name) assert_contains(zf, renamed_name) TestFiles::BINARY_TEST_FILES.each do |filename| zf.add(filename, filename) assert_contains(zf, filename) end assert_contains(zf, orig_entries.last.to_s) filename_to_remove = orig_entries.map(&:to_s).find do |name| name.match('longBinary') end zf.remove(filename_to_remove) assert_not_contains(zf, filename_to_remove) ensure zf.close end begin zf_read = ::Zip::File.new(TEST_ZIP.zip_name) assert_contains(zf_read, TestFiles::RANDOM_ASCII_FILE1) assert_contains(zf_read, renamed_name) TestFiles::BINARY_TEST_FILES.each do |filename| assert_contains(zf_read, filename) end assert_not_contains(zf_read, filename_to_remove) ensure zf_read.close end end def test_compound2 begin zf = ::Zip::File.new(TEST_ZIP.zip_name) orig_entries = zf.entries.dup orig_entries.each do |entry| zf.remove(entry) assert_not_contains(zf, entry) end assert(zf.entries.empty?) TestFiles::ASCII_TEST_FILES.each do |filename| zf.add(filename, filename) assert_contains(zf, filename) end assert_equal(zf.entries.sort.map(&:name), TestFiles::ASCII_TEST_FILES) zf.rename(TestFiles::ASCII_TEST_FILES[0], 'new_name') assert_not_contains(zf, TestFiles::ASCII_TEST_FILES[0]) assert_contains(zf, 'new_name') ensure zf.close end begin zf_read = ::Zip::File.new(TEST_ZIP.zip_name) ascii_files = TestFiles::ASCII_TEST_FILES.dup ascii_files.shift ascii_files.each do |filename| assert_contains(zf, filename) end assert_contains(zf, 'new_name') ensure zf_read.close end end def test_change_comment ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.comment = 'my changed comment' end zf_read = ::Zip::File.open(TEST_ZIP.zip_name) assert_equal('my changed comment', zf_read.comment) end def test_preserve_file_order entry_names = nil ::Zip::File.open(TEST_ZIP.zip_name) do |zf| entry_names = zf.entries.map(&:to_s) zf.get_output_stream('a.txt') { |os| os.write 'this is a.txt' } zf.get_output_stream('z.txt') { |os| os.write 'this is z.txt' } zf.get_output_stream('k.txt') { |os| os.write 'this is k.txt' } entry_names << 'a.txt' << 'z.txt' << 'k.txt' end ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(entry_names, zf.entries.map(&:to_s)) entries = zf.entries.sort_by(&:name).reverse entries.each do |e| zf.remove e zf.get_output_stream(e) { |os| os.write 'foo' } end entry_names = entries.map(&:to_s) end ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(entry_names, zf.entries.map(&:to_s)) end end def test_streaming fname = ::File.join(::File.expand_path(::File.dirname(__FILE__)), '../README.md') zname = 'test/data/generated/README.zip' Zip::File.open(zname, Zip::File::CREATE) do |zipfile| zipfile.get_output_stream(File.basename(fname)) do |f| f.puts File.read(fname) end end data = nil File.open(zname, 'rb') do |f| Zip::File.open_buffer(f) do |zipfile| zipfile.each do |entry| next unless entry.name =~ /README.md/ data = zipfile.read(entry) end end end assert data assert data =~ /Simonov/ end def test_nonexistant_zip assert_raises(::Zip::Error) do ::Zip::File.open('fake.zip') end end def test_empty_zip assert_raises(::Zip::Error) do ::Zip::File.open(TestFiles::NULL_FILE) end end def test_odd_extra_field entry_count = 0 File.open 'test/data/oddExtraField.zip', 'rb' do |zip_io| Zip::File.open_buffer zip_io.read do |zip| zip.each do |_zip_entry| entry_count += 1 end end end assert_equal 13, entry_count end def test_open_xls_does_not_raise_type_error ::Zip::File.open('test/data/test.xls') end def test_find_get_entry ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_nil zf.find_entry('not_in_here.txt') refute_nil zf.find_entry('test/data/generated/empty.txt') assert_raises(Errno::ENOENT) do zf.get_entry('not_in_here.txt') end # Should not raise anything. zf.get_entry('test/data/generated/empty.txt') end end private def assert_contains(zip_file, entry_name, filename = entry_name) refute_nil( zip_file.entries.detect { |e| e.name == entry_name }, "entry #{entry_name} not in #{zip_file.entries.join(', ')} in zip file #{zip_file}" ) assert_entry_contents(zip_file, entry_name, filename) if File.exist?(filename) end def assert_not_contains(zip_file, entry_name) assert_nil( zip_file.entries.detect { |e| e.name == entry_name }, "entry #{entry_name} in #{zip_file.entries.join(', ')} in zip file #{zip_file}" ) end end rubyzip-2.3.0/test/filesystem/000077500000000000000000000000001363314356200163715ustar00rootroot00000000000000rubyzip-2.3.0/test/filesystem/dir_iterator_test.rb000066400000000000000000000024361363314356200224510ustar00rootroot00000000000000require 'test_helper' require 'zip/filesystem' class ZipFsDirIteratorTest < MiniTest::Test FILENAME_ARRAY = %w[f1 f2 f3 f4 f5 f6] def setup @dir_iter = ::Zip::FileSystem::ZipFsDirIterator.new(FILENAME_ARRAY) end def test_close @dir_iter.close assert_raises(IOError, 'closed directory') do @dir_iter.each { |e| p e } end assert_raises(IOError, 'closed directory') do @dir_iter.read end assert_raises(IOError, 'closed directory') do @dir_iter.rewind end assert_raises(IOError, 'closed directory') do @dir_iter.seek(0) end assert_raises(IOError, 'closed directory') do @dir_iter.tell end end def test_each # Tested through Enumerable.entries assert_equal(FILENAME_ARRAY, @dir_iter.entries) end def test_read FILENAME_ARRAY.size.times do |i| assert_equal(FILENAME_ARRAY[i], @dir_iter.read) end end def test_rewind @dir_iter.read @dir_iter.read assert_equal(FILENAME_ARRAY[2], @dir_iter.read) @dir_iter.rewind assert_equal(FILENAME_ARRAY[0], @dir_iter.read) end def test_tell_seek @dir_iter.read @dir_iter.read pos = @dir_iter.tell value = @dir_iter.read @dir_iter.read @dir_iter.seek(pos) assert_equal(value, @dir_iter.read) end end rubyzip-2.3.0/test/filesystem/directory_test.rb000066400000000000000000000073131363314356200217650ustar00rootroot00000000000000require 'test_helper' require 'zip/filesystem' class ZipFsDirectoryTest < MiniTest::Test TEST_ZIP = 'test/data/generated/zipWithDirs_copy.zip' GLOB_TEST_ZIP = 'test/data/globTest.zip' def setup FileUtils.cp('test/data/zipWithDirs.zip', TEST_ZIP) end def test_delete ::Zip::File.open(TEST_ZIP) do |zf| assert_raises(Errno::ENOENT, 'No such file or directory - NoSuchFile.txt') do zf.dir.delete('NoSuchFile.txt') end assert_raises(Errno::EINVAL, 'Invalid argument - file1') do zf.dir.delete('file1') end assert(zf.file.exists?('dir1')) zf.dir.delete('dir1') assert(!zf.file.exists?('dir1')) end end def test_mkdir ::Zip::File.open(TEST_ZIP) do |zf| assert_raises(Errno::EEXIST, 'File exists - dir1') do zf.dir.mkdir('file1') end assert_raises(Errno::EEXIST, 'File exists - dir1') do zf.dir.mkdir('dir1') end assert(!zf.file.exists?('newDir')) zf.dir.mkdir('newDir') assert(zf.file.directory?('newDir')) assert(!zf.file.exists?('newDir2')) zf.dir.mkdir('newDir2', 3485) assert(zf.file.directory?('newDir2')) end end def test_pwd_chdir_entries ::Zip::File.open(TEST_ZIP) do |zf| assert_equal('/', zf.dir.pwd) assert_raises(Errno::ENOENT, 'No such file or directory - no such dir') do zf.dir.chdir 'no such dir' end assert_raises(Errno::EINVAL, 'Invalid argument - file1') do zf.dir.chdir 'file1' end assert_equal(%w[dir1 dir2 file1].sort, zf.dir.entries('.').sort) zf.dir.chdir 'dir1' assert_equal('/dir1', zf.dir.pwd) assert_equal(%w[dir11 file11 file12], zf.dir.entries('.').sort) zf.dir.chdir '../dir2/dir21' assert_equal('/dir2/dir21', zf.dir.pwd) assert_equal(['dir221'].sort, zf.dir.entries('.').sort) end end def test_foreach ::Zip::File.open(TEST_ZIP) do |zf| block_called = false assert_raises(Errno::ENOENT, 'No such file or directory - noSuchDir') do zf.dir.foreach('noSuchDir') { |_e| block_called = true } end assert(!block_called) assert_raises(Errno::ENOTDIR, 'Not a directory - file1') do zf.dir.foreach('file1') { |_e| block_called = true } end assert(!block_called) entries = [] zf.dir.foreach('.') { |e| entries << e } assert_equal(%w[dir1 dir2 file1].sort, entries.sort) entries = [] zf.dir.foreach('dir1') { |e| entries << e } assert_equal(%w[dir11 file11 file12], entries.sort) end end def test_chroot ::Zip::File.open(TEST_ZIP) do |zf| assert_raises(NotImplementedError) do zf.dir.chroot end end end def test_glob globbed_files = [ 'globTest/foo/bar/baz/foo.txt', 'globTest/foo.txt', 'globTest/food.txt' ] ::Zip::File.open(GLOB_TEST_ZIP) do |zf| zf.dir.glob('**/*.txt') do |f| assert globbed_files.include?(f.name) end zf.dir.glob('globTest/foo/**/*.txt') do |f| assert_equal globbed_files[0], f.name end zf.dir.chdir('globTest/foo') zf.dir.glob('**/*.txt') do |f| assert_equal globbed_files[0], f.name end end end def test_open_new ::Zip::File.open(TEST_ZIP) do |zf| assert_raises(Errno::ENOTDIR, 'Not a directory - file1') do zf.dir.new('file1') end assert_raises(Errno::ENOENT, 'No such file or directory - noSuchFile') do zf.dir.new('noSuchFile') end d = zf.dir.new('.') assert_equal(%w[file1 dir1 dir2].sort, d.entries.sort) d.close zf.dir.open('dir1') do |dir| assert_equal(%w[dir11 file11 file12].sort, dir.entries.sort) end end end end rubyzip-2.3.0/test/filesystem/file_mutating_test.rb000066400000000000000000000047331363314356200226130ustar00rootroot00000000000000require 'test_helper' require 'zip/filesystem' class ZipFsFileMutatingTest < MiniTest::Test TEST_ZIP = 'test/data/generated/zipWithDirs_copy.zip' def setup FileUtils.cp('test/data/zipWithDirs.zip', TEST_ZIP) end def teardown; end def test_delete do_test_delete_or_unlink(:delete) end def test_unlink do_test_delete_or_unlink(:unlink) end def test_open_write ::Zip::File.open(TEST_ZIP) do |zf| zf.file.open('test_open_write_entry', 'w') do |f| f.write "This is what I'm writing" end assert_equal("This is what I'm writing", zf.file.read('test_open_write_entry')) # Test with existing entry zf.file.open('file1', 'wb') do |f| # also check that 'b' option is ignored f.write "This is what I'm writing too" end assert_equal("This is what I'm writing too", zf.file.read('file1')) end end def test_rename ::Zip::File.open(TEST_ZIP) do |zf| assert_raises(Errno::ENOENT, '') do zf.file.rename('NoSuchFile', 'bimse') end zf.file.rename('file1', 'newNameForFile1') end ::Zip::File.open(TEST_ZIP) do |zf| assert(!zf.file.exists?('file1')) assert(zf.file.exists?('newNameForFile1')) end end def test_chmod ::Zip::File.open(TEST_ZIP) do |zf| zf.file.chmod(0o765, 'file1') end ::Zip::File.open(TEST_ZIP) do |zf| assert_equal(0o100765, zf.file.stat('file1').mode) end end def do_test_delete_or_unlink(symbol) ::Zip::File.open(TEST_ZIP) do |zf| assert(zf.file.exists?('dir2/dir21/dir221/file2221')) zf.file.send(symbol, 'dir2/dir21/dir221/file2221') assert(!zf.file.exists?('dir2/dir21/dir221/file2221')) assert(zf.file.exists?('dir1/file11')) assert(zf.file.exists?('dir1/file12')) zf.file.send(symbol, 'dir1/file11', 'dir1/file12') assert(!zf.file.exists?('dir1/file11')) assert(!zf.file.exists?('dir1/file12')) assert_raises(Errno::ENOENT) { zf.file.send(symbol, 'noSuchFile') } assert_raises(Errno::EISDIR) { zf.file.send(symbol, 'dir1/dir11') } assert_raises(Errno::EISDIR) { zf.file.send(symbol, 'dir1/dir11/') } end ::Zip::File.open(TEST_ZIP) do |zf| assert(!zf.file.exists?('dir2/dir21/dir221/file2221')) assert(!zf.file.exists?('dir1/file11')) assert(!zf.file.exists?('dir1/file12')) assert(zf.file.exists?('dir1/dir11')) assert(zf.file.exists?('dir1/dir11/')) end end end rubyzip-2.3.0/test/filesystem/file_nonmutating_test.rb000066400000000000000000000362121363314356200233230ustar00rootroot00000000000000require 'test_helper' require 'zip/filesystem' class ZipFsFileNonmutatingTest < MiniTest::Test def setup @zipsha = Digest::SHA1.file('test/data/zipWithDirs.zip') @zip_file = ::Zip::File.new('test/data/zipWithDirs.zip') end def teardown @zip_file.close if @zip_file assert_equal(@zipsha, Digest::SHA1.file('test/data/zipWithDirs.zip')) end def test_umask assert_equal(::File.umask, @zip_file.file.umask) @zip_file.file.umask(0o006) end def test_exists? assert(!@zip_file.file.exists?('notAFile')) assert(@zip_file.file.exists?('file1')) assert(@zip_file.file.exists?('dir1')) assert(@zip_file.file.exists?('dir1/')) assert(@zip_file.file.exists?('dir1/file12')) assert(@zip_file.file.exist?('dir1/file12')) # notice, tests exist? alias of exists? ! @zip_file.dir.chdir 'dir1/' assert(!@zip_file.file.exists?('file1')) assert(@zip_file.file.exists?('file12')) end def test_open_read block_called = false @zip_file.file.open('file1', 'r') do |f| block_called = true assert_equal("this is the entry 'file1' in my test archive!", f.readline.chomp) end assert(block_called) block_called = false @zip_file.file.open('file1', 'rb') do |f| # test binary flag is ignored block_called = true assert_equal("this is the entry 'file1' in my test archive!", f.readline.chomp) end assert(block_called) block_called = false @zip_file.dir.chdir 'dir2' @zip_file.file.open('file21', 'r') do |f| block_called = true assert_equal("this is the entry 'dir2/file21' in my test archive!", f.readline.chomp) end assert(block_called) @zip_file.dir.chdir '/' assert_raises(Errno::ENOENT) do @zip_file.file.open('noSuchEntry') end begin is = @zip_file.file.open('file1') assert_equal("this is the entry 'file1' in my test archive!", is.readline.chomp) ensure is.close if is end end def test_new begin is = @zip_file.file.new('file1') assert_equal("this is the entry 'file1' in my test archive!", is.readline.chomp) ensure is.close if is end begin is = @zip_file.file.new('file1') do raise 'should not call block' end ensure is.close if is end end def test_symlink assert_raises(NotImplementedError) do @zip_file.file.symlink('file1', 'aSymlink') end end def test_size assert_raises(Errno::ENOENT) { @zip_file.file.size('notAFile') } assert_equal(72, @zip_file.file.size('file1')) assert_equal(0, @zip_file.file.size('dir2/dir21')) assert_equal(72, @zip_file.file.stat('file1').size) assert_equal(0, @zip_file.file.stat('dir2/dir21').size) end def test_size? assert_nil(@zip_file.file.size?('notAFile')) assert_equal(72, @zip_file.file.size?('file1')) assert_nil(@zip_file.file.size?('dir2/dir21')) assert_equal(72, @zip_file.file.stat('file1').size?) assert_nil(@zip_file.file.stat('dir2/dir21').size?) end def test_file? assert(@zip_file.file.file?('file1')) assert(@zip_file.file.file?('dir2/file21')) assert(!@zip_file.file.file?('dir1')) assert(!@zip_file.file.file?('dir1/dir11')) assert(@zip_file.file.stat('file1').file?) assert(@zip_file.file.stat('dir2/file21').file?) assert(!@zip_file.file.stat('dir1').file?) assert(!@zip_file.file.stat('dir1/dir11').file?) end include ExtraAssertions def test_dirname assert_forwarded(File, :dirname, 'ret_val', 'a/b/c/d') do @zip_file.file.dirname('a/b/c/d') end end def test_basename assert_forwarded(File, :basename, 'ret_val', 'a/b/c/d') do @zip_file.file.basename('a/b/c/d') end end def test_split assert_forwarded(File, :split, 'ret_val', 'a/b/c/d') do @zip_file.file.split('a/b/c/d') end end def test_join assert_equal('a/b/c', @zip_file.file.join('a/b', 'c')) assert_equal('a/b/c/d', @zip_file.file.join('a/b', 'c/d')) assert_equal('/c/d', @zip_file.file.join('', 'c/d')) assert_equal('a/b/c/d', @zip_file.file.join('a', 'b', 'c', 'd')) end def test_utime t_now = ::Zip::DOSTime.now t_bak = @zip_file.file.mtime('file1') @zip_file.file.utime(t_now, 'file1') assert_equal(t_now, @zip_file.file.mtime('file1')) @zip_file.file.utime(t_bak, 'file1') assert_equal(t_bak, @zip_file.file.mtime('file1')) end def assert_always_false(operation) assert(!@zip_file.file.send(operation, 'noSuchFile')) assert(!@zip_file.file.send(operation, 'file1')) assert(!@zip_file.file.send(operation, 'dir1')) assert(!@zip_file.file.stat('file1').send(operation)) assert(!@zip_file.file.stat('dir1').send(operation)) end def assert_true_if_entry_exists(operation) assert(!@zip_file.file.send(operation, 'noSuchFile')) assert(@zip_file.file.send(operation, 'file1')) assert(@zip_file.file.send(operation, 'dir1')) assert(@zip_file.file.stat('file1').send(operation)) assert(@zip_file.file.stat('dir1').send(operation)) end def test_pipe? assert_always_false(:pipe?) end def test_blockdev? assert_always_false(:blockdev?) end def test_symlink? assert_always_false(:symlink?) end def test_socket? assert_always_false(:socket?) end def test_chardev? assert_always_false(:chardev?) end def test_truncate assert_raises(StandardError, 'truncate not supported') do @zip_file.file.truncate('file1', 100) end end def assert_e_n_o_e_n_t(operation, args = ['NoSuchFile']) assert_raises(Errno::ENOENT) do @zip_file.file.send(operation, *args) end end def test_ftype assert_e_n_o_e_n_t(:ftype) assert_equal('file', @zip_file.file.ftype('file1')) assert_equal('directory', @zip_file.file.ftype('dir1/dir11')) assert_equal('directory', @zip_file.file.ftype('dir1/dir11/')) end def test_link assert_raises(NotImplementedError) do @zip_file.file.link('file1', 'someOtherString') end end def test_directory? assert(!@zip_file.file.directory?('notAFile')) assert(!@zip_file.file.directory?('file1')) assert(!@zip_file.file.directory?('dir1/file11')) assert(@zip_file.file.directory?('dir1')) assert(@zip_file.file.directory?('dir1/')) assert(@zip_file.file.directory?('dir2/dir21')) assert(!@zip_file.file.stat('file1').directory?) assert(!@zip_file.file.stat('dir1/file11').directory?) assert(@zip_file.file.stat('dir1').directory?) assert(@zip_file.file.stat('dir1/').directory?) assert(@zip_file.file.stat('dir2/dir21').directory?) end def test_chown assert_equal(2, @zip_file.file.chown(1, 2, 'dir1', 'file1')) assert_equal(1, @zip_file.file.stat('dir1').uid) assert_equal(2, @zip_file.file.stat('dir1').gid) assert_equal(2, @zip_file.file.chown(nil, nil, 'dir1', 'file1')) end def test_zero? assert(!@zip_file.file.zero?('notAFile')) assert(!@zip_file.file.zero?('file1')) assert(@zip_file.file.zero?('dir1')) block_called = false ::Zip::File.open('test/data/generated/5entry.zip') do |zf| block_called = true assert(zf.file.zero?('test/data/generated/empty.txt')) end assert(block_called) assert(!@zip_file.file.stat('file1').zero?) assert(@zip_file.file.stat('dir1').zero?) block_called = false ::Zip::File.open('test/data/generated/5entry.zip') do |zf| block_called = true assert(zf.file.stat('test/data/generated/empty.txt').zero?) end assert(block_called) end def test_expand_path ::Zip::File.open('test/data/zipWithDirs.zip') do |zf| assert_equal('/', zf.file.expand_path('.')) zf.dir.chdir 'dir1' assert_equal('/dir1', zf.file.expand_path('.')) assert_equal('/dir1/file12', zf.file.expand_path('file12')) assert_equal('/', zf.file.expand_path('..')) assert_equal('/dir2/dir21', zf.file.expand_path('../dir2/dir21')) end end def test_mtime assert_equal(::Zip::DOSTime.at(1_027_694_306), @zip_file.file.mtime('dir2/file21')) assert_equal(::Zip::DOSTime.at(1_027_690_863), @zip_file.file.mtime('dir2/dir21')) assert_raises(Errno::ENOENT) do @zip_file.file.mtime('noSuchEntry') end assert_equal(::Zip::DOSTime.at(1_027_694_306), @zip_file.file.stat('dir2/file21').mtime) assert_equal(::Zip::DOSTime.at(1_027_690_863), @zip_file.file.stat('dir2/dir21').mtime) end def test_ctime assert_nil(@zip_file.file.ctime('file1')) assert_nil(@zip_file.file.stat('file1').ctime) end def test_atime assert_nil(@zip_file.file.atime('file1')) assert_nil(@zip_file.file.stat('file1').atime) end def test_ntfs_time ::Zip::File.open('test/data/ntfs.zip') do |zf| t = ::Zip::DOSTime.at(1_410_496_497.405178) assert_equal(zf.file.mtime('data.txt'), t) assert_equal(zf.file.atime('data.txt'), t) assert_equal(zf.file.ctime('data.txt'), t) end end def test_readable? assert(!@zip_file.file.readable?('noSuchFile')) assert(@zip_file.file.readable?('file1')) assert(@zip_file.file.readable?('dir1')) assert(@zip_file.file.stat('file1').readable?) assert(@zip_file.file.stat('dir1').readable?) end def test_readable_real? assert(!@zip_file.file.readable_real?('noSuchFile')) assert(@zip_file.file.readable_real?('file1')) assert(@zip_file.file.readable_real?('dir1')) assert(@zip_file.file.stat('file1').readable_real?) assert(@zip_file.file.stat('dir1').readable_real?) end def test_writable? assert(!@zip_file.file.writable?('noSuchFile')) assert(@zip_file.file.writable?('file1')) assert(@zip_file.file.writable?('dir1')) assert(@zip_file.file.stat('file1').writable?) assert(@zip_file.file.stat('dir1').writable?) end def test_writable_real? assert(!@zip_file.file.writable_real?('noSuchFile')) assert(@zip_file.file.writable_real?('file1')) assert(@zip_file.file.writable_real?('dir1')) assert(@zip_file.file.stat('file1').writable_real?) assert(@zip_file.file.stat('dir1').writable_real?) end def test_executable? assert(!@zip_file.file.executable?('noSuchFile')) assert(!@zip_file.file.executable?('file1')) assert(@zip_file.file.executable?('dir1')) assert(!@zip_file.file.stat('file1').executable?) assert(@zip_file.file.stat('dir1').executable?) end def test_executable_real? assert(!@zip_file.file.executable_real?('noSuchFile')) assert(!@zip_file.file.executable_real?('file1')) assert(@zip_file.file.executable_real?('dir1')) assert(!@zip_file.file.stat('file1').executable_real?) assert(@zip_file.file.stat('dir1').executable_real?) end def test_owned? assert_true_if_entry_exists(:owned?) end def test_grpowned? assert_true_if_entry_exists(:grpowned?) end def test_setgid? assert_always_false(:setgid?) end def test_setuid? assert_always_false(:setgid?) end def test_sticky? assert_always_false(:sticky?) end def test_readlink assert_raises(NotImplementedError) do @zip_file.file.readlink('someString') end end def test_stat s = @zip_file.file.stat('file1') assert(s.kind_of?(File::Stat)) # It pretends assert_raises(Errno::ENOENT, 'No such file or directory - noSuchFile') do @zip_file.file.stat('noSuchFile') end end def test_lstat assert(@zip_file.file.lstat('file1').file?) end def test_pipe assert_raises(NotImplementedError) do @zip_file.file.pipe end end def test_foreach ::Zip::File.open('test/data/generated/zipWithDir.zip') do |zf| ref = [] File.foreach('test/data/file1.txt') { |e| ref << e } index = 0 zf.file.foreach('test/data/file1.txt') do |l| # Ruby replaces \n with \r\n automatically on windows newline = Zip::RUNNING_ON_WINDOWS ? l.gsub(/\r\n/, "\n") : l assert_equal(ref[index], newline) index = index.next end assert_equal(ref.size, index) end ::Zip::File.open('test/data/generated/zipWithDir.zip') do |zf| ref = [] File.foreach('test/data/file1.txt', ' ') { |e| ref << e } index = 0 zf.file.foreach('test/data/file1.txt', ' ') do |l| # Ruby replaces \n with \r\n automatically on windows newline = Zip::RUNNING_ON_WINDOWS ? l.gsub(/\r\n/, "\n") : l assert_equal(ref[index], newline) index = index.next end assert_equal(ref.size, index) end end def test_glob ::Zip::File.open('test/data/globTest.zip') do |zf| { 'globTest/foo.txt' => ['globTest/foo.txt'], '*/foo.txt' => ['globTest/foo.txt'], '**/foo.txt' => [ 'globTest/foo.txt', 'globTest/foo/bar/baz/foo.txt' ], '*/foo/**/*.txt' => ['globTest/foo/bar/baz/foo.txt'] }.each do |spec, expected_results| results = zf.glob(spec) assert(results.all? { |entry| entry.kind_of? ::Zip::Entry }) result_strings = results.map(&:to_s) missing_matches = expected_results - result_strings extra_matches = result_strings - expected_results assert extra_matches.empty?, "spec #{spec.inspect} has extra results #{extra_matches.inspect}" assert missing_matches.empty?, "spec #{spec.inspect} missing results #{missing_matches.inspect}" end end ::Zip::File.open('test/data/globTest.zip') do |zf| results = [] zf.glob('**/foo.txt') do |match| results << "<#{match.class.name}: #{match}>" end assert(!results.empty?, 'block not run, or run out of context') assert_equal 2, results.size assert_operator results, :include?, '' assert_operator results, :include?, '' end end def test_popen if Zip::RUNNING_ON_WINDOWS # This is pretty much projectile vomit but it allows the test to be # run on windows also system_dir = ::File.popen('dir', &:read).gsub(/Dir\(s\).*$/, '') zipfile_dir = @zip_file.file.popen('dir', &:read).gsub(/Dir\(s\).*$/, '') assert_equal(system_dir, zipfile_dir) else assert_equal(::File.popen('ls', &:read), @zip_file.file.popen('ls', &:read)) end end # Can be added later # def test_select # fail "implement test" # end def test_readlines ::Zip::File.open('test/data/generated/zipWithDir.zip') do |zf| orig_file = ::File.readlines('test/data/file1.txt') zip_file = zf.file.readlines('test/data/file1.txt') # Ruby replaces \n with \r\n automatically on windows zip_file.each { |l| l.gsub!(/\r\n/, "\n") } if Zip::RUNNING_ON_WINDOWS assert_equal(orig_file, zip_file) end end def test_read ::Zip::File.open('test/data/generated/zipWithDir.zip') do |zf| orig_file = ::File.read('test/data/file1.txt') # Ruby replaces \n with \r\n automatically on windows zip_file = if Zip::RUNNING_ON_WINDOWS zf.file.read('test/data/file1.txt').gsub(/\r\n/, "\n") else zf.file.read('test/data/file1.txt') end assert_equal(orig_file, zip_file) end end end rubyzip-2.3.0/test/filesystem/file_stat_test.rb000066400000000000000000000026701363314356200217340ustar00rootroot00000000000000require 'test_helper' require 'zip/filesystem' class ZipFsFileStatTest < MiniTest::Test def setup @zip_file = ::Zip::File.new('test/data/zipWithDirs.zip') end def teardown @zip_file.close if @zip_file end def test_blocks assert_nil(@zip_file.file.stat('file1').blocks) end def test_ino assert_equal(0, @zip_file.file.stat('file1').ino) end def test_uid assert_equal(0, @zip_file.file.stat('file1').uid) end def test_gid assert_equal(0, @zip_file.file.stat('file1').gid) end def test_ftype assert_equal('file', @zip_file.file.stat('file1').ftype) assert_equal('directory', @zip_file.file.stat('dir1').ftype) end def test_mode assert_equal(0o600, @zip_file.file.stat('file1').mode & 0o777) assert_equal(0o600, @zip_file.file.stat('file1').mode & 0o777) assert_equal(0o755, @zip_file.file.stat('dir1').mode & 0o777) assert_equal(0o755, @zip_file.file.stat('dir1').mode & 0o777) end def test_dev assert_equal(0, @zip_file.file.stat('file1').dev) end def test_rdev assert_equal(0, @zip_file.file.stat('file1').rdev) end def test_rdev_major assert_equal(0, @zip_file.file.stat('file1').rdev_major) end def test_rdev_minor assert_equal(0, @zip_file.file.stat('file1').rdev_minor) end def test_nlink assert_equal(1, @zip_file.file.stat('file1').nlink) end def test_blksize assert_nil(@zip_file.file.stat('file1').blksize) end end rubyzip-2.3.0/test/gentestfiles.rb000077500000000000000000000123761363314356200172420ustar00rootroot00000000000000#!/usr/bin/env ruby $VERBOSE = true class TestFiles RANDOM_ASCII_FILE1 = 'test/data/generated/randomAscii1.txt' RANDOM_ASCII_FILE2 = 'test/data/generated/randomAscii2.txt' RANDOM_ASCII_FILE3 = 'test/data/generated/randomAscii3.txt' RANDOM_BINARY_FILE1 = 'test/data/generated/randomBinary1.bin' RANDOM_BINARY_FILE2 = 'test/data/generated/randomBinary2.bin' NULL_FILE = 'test/data/generated/null.zip' # Zero length, so not a zip file. EMPTY_TEST_DIR = 'test/data/generated/emptytestdir' ASCII_TEST_FILES = [RANDOM_ASCII_FILE1, RANDOM_ASCII_FILE2, RANDOM_ASCII_FILE3] BINARY_TEST_FILES = [RANDOM_BINARY_FILE1, RANDOM_BINARY_FILE2] TEST_DIRECTORIES = [EMPTY_TEST_DIR] TEST_FILES = [ASCII_TEST_FILES, BINARY_TEST_FILES, EMPTY_TEST_DIR].flatten! class << self def create_test_files Dir.mkdir 'test/data/generated' unless Dir.exist?('test/data/generated') ASCII_TEST_FILES.each_with_index do |filename, index| create_random_ascii(filename, 1E4 * (index + 1)) end BINARY_TEST_FILES.each_with_index do |filename, index| create_random_binary(filename, 1E4 * (index + 1)) end system("touch #{NULL_FILE}") ensure_dir(EMPTY_TEST_DIR) end private def create_random_ascii(filename, size) File.open(filename, 'wb') do |file| file << rand while file.tell < size end end def create_random_binary(filename, size) File.open(filename, 'wb') do |file| file << [rand].pack('V') while file.tell < size end end def ensure_dir(name) if File.exist?(name) return if File.stat(name).directory? File.delete(name) end Dir.mkdir(name) end end end # For representation and creation of # test data class TestZipFile attr_accessor :zip_name, :entry_names, :comment def initialize(zip_name, entry_names, comment = '') @zip_name = zip_name @entry_names = entry_names @comment = comment end def self.create_test_zips raise "failed to create test zip '#{TEST_ZIP1.zip_name}'" \ unless system("/usr/bin/zip -q #{TEST_ZIP1.zip_name} test/data/file2.txt") raise "failed to remove entry from '#{TEST_ZIP1.zip_name}'" \ unless system( "/usr/bin/zip -q #{TEST_ZIP1.zip_name} -d test/data/file2.txt" ) File.open('test/data/generated/empty.txt', 'w') {} File.open('test/data/generated/empty_chmod640.txt', 'w') {} ::File.chmod(0o640, 'test/data/generated/empty_chmod640.txt') File.open('test/data/generated/short.txt', 'w') { |file| file << 'ABCDEF' } test_text = '' File.open('test/data/file2.txt') { |file| test_text = file.read } File.open('test/data/generated/longAscii.txt', 'w') do |file| file << test_text while file.tell < 1E5 end binary_pattern = '' File.open('test/data/generated/empty.zip') do |file| binary_pattern = file.read end binary_pattern *= 4 File.open('test/data/generated/longBinary.bin', 'wb') do |file| file << binary_pattern << rand << "\0" while file.tell < 6E5 end raise "failed to create test zip '#{TEST_ZIP2.zip_name}'" \ unless system( "/usr/bin/zip -q #{TEST_ZIP2.zip_name} #{TEST_ZIP2.entry_names.join(' ')}" ) if RUBY_PLATFORM =~ /mswin|mingw|cygwin/ raise "failed to add comment to test zip '#{TEST_ZIP2.zip_name}'" \ unless system( "echo #{TEST_ZIP2.comment}| /usr/bin/zip -zq #{TEST_ZIP2.zip_name}\"" ) else # without bash system interprets everything after echo as parameters to # echo including | zip -z ... raise "failed to add comment to test zip '#{TEST_ZIP2.zip_name}'" \ unless system( "bash -c \"echo #{TEST_ZIP2.comment} | /usr/bin/zip -zq #{TEST_ZIP2.zip_name}\"" ) end raise "failed to create test zip '#{TEST_ZIP3.zip_name}'" \ unless system( "/usr/bin/zip -q #{TEST_ZIP3.zip_name} #{TEST_ZIP3.entry_names.join(' ')}" ) raise "failed to create test zip '#{TEST_ZIP4.zip_name}'" \ unless system( "/usr/bin/zip -q #{TEST_ZIP4.zip_name} #{TEST_ZIP4.entry_names.join(' ')}" ) rescue StandardError # If there are any Windows developers wanting to use a command line zip.exe # to help create the following files, there's a free one available from # http://stahlworks.com/dev/index.php?tool=zipunzip # that works with the above code raise $ERROR_INFO.to_s + "\n\nziptest.rb requires the Info-ZIP program 'zip' in the path\n" \ "to create test data. If you don't have it you can download\n" \ 'the necessary test files at http://sf.net/projects/rubyzip.' end TEST_ZIP1 = TestZipFile.new('test/data/generated/empty.zip', []) TEST_ZIP2 = TestZipFile.new( 'test/data/generated/5entry.zip', %w[ test/data/generated/longAscii.txt test/data/generated/empty.txt test/data/generated/empty_chmod640.txt test/data/generated/short.txt test/data/generated/longBinary.bin ], 'my zip comment' ) TEST_ZIP3 = TestZipFile.new('test/data/generated/test1.zip', %w[test/data/file1.txt]) TEST_ZIP4 = TestZipFile.new('test/data/generated/zipWithDir.zip', ['test/data/file1.txt', TestFiles::EMPTY_TEST_DIR]) end rubyzip-2.3.0/test/inflater_test.rb000066400000000000000000000004131363314356200173730ustar00rootroot00000000000000require 'test_helper' class InflaterTest < MiniTest::Test include DecompressorTests def setup super @file = File.new('test/data/file1.txt.deflatedData', 'rb') @decompressor = ::Zip::Inflater.new(@file) end def teardown @file.close end end rubyzip-2.3.0/test/input_stream_test.rb000066400000000000000000000133321363314356200203050ustar00rootroot00000000000000require 'test_helper' class ZipInputStreamTest < MiniTest::Test include AssertEntry class IOLike extend Forwardable def initialize(path, mode) @file = File.new(path, mode) end delegate ::Zip::File::IO_METHODS => :@file end def test_new zis = ::Zip::InputStream.new(TestZipFile::TEST_ZIP2.zip_name) assert_stream_contents(zis, TestZipFile::TEST_ZIP2) assert_equal(true, zis.eof?) zis.close end def test_open_with_block ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| assert_stream_contents(zis, TestZipFile::TEST_ZIP2) assert_equal(true, zis.eof?) end end def test_open_without_block zis = ::Zip::InputStream.open(File.new(TestZipFile::TEST_ZIP2.zip_name, 'rb')) assert_stream_contents(zis, TestZipFile::TEST_ZIP2) end def test_open_buffer_with_block ::Zip::InputStream.open(File.new(TestZipFile::TEST_ZIP2.zip_name, 'rb')) do |zis| assert_stream_contents(zis, TestZipFile::TEST_ZIP2) assert_equal(true, zis.eof?) end end def test_open_string_io_without_block string_io = ::StringIO.new(::File.read(TestZipFile::TEST_ZIP2.zip_name)) zis = ::Zip::InputStream.open(string_io) assert_stream_contents(zis, TestZipFile::TEST_ZIP2) end def test_open_string_io_with_block string_io = ::StringIO.new(::File.read(TestZipFile::TEST_ZIP2.zip_name)) ::Zip::InputStream.open(string_io) do |zis| assert_stream_contents(zis, TestZipFile::TEST_ZIP2) assert_equal(true, zis.eof?) end end def test_open_buffer_without_block zis = ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) assert_stream_contents(zis, TestZipFile::TEST_ZIP2) end def test_open_io_like_with_block ::Zip::InputStream.open(IOLike.new(TestZipFile::TEST_ZIP2.zip_name, 'rb')) do |zis| assert_stream_contents(zis, TestZipFile::TEST_ZIP2) assert_equal(true, zis.eof?) end end def test_incomplete_reads ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| entry = zis.get_next_entry # longAscii.txt assert_equal(false, zis.eof?) assert_equal(TestZipFile::TEST_ZIP2.entry_names[0], entry.name) assert !zis.gets.empty? assert_equal(false, zis.eof?) entry = zis.get_next_entry # empty.txt assert_equal(TestZipFile::TEST_ZIP2.entry_names[1], entry.name) assert_equal(0, entry.size) assert_nil(zis.gets) assert_equal(true, zis.eof?) entry = zis.get_next_entry # empty_chmod640.txt assert_equal(TestZipFile::TEST_ZIP2.entry_names[2], entry.name) assert_equal(0, entry.size) assert_nil(zis.gets) assert_equal(true, zis.eof?) entry = zis.get_next_entry # short.txt assert_equal(TestZipFile::TEST_ZIP2.entry_names[3], entry.name) assert !zis.gets.empty? entry = zis.get_next_entry # longBinary.bin assert_equal(TestZipFile::TEST_ZIP2.entry_names[4], entry.name) assert !zis.gets.empty? end end def test_incomplete_reads_from_string_io string_io = ::StringIO.new(::File.read(TestZipFile::TEST_ZIP2.zip_name)) ::Zip::InputStream.open(string_io) do |zis| entry = zis.get_next_entry # longAscii.txt assert_equal(false, zis.eof?) assert_equal(TestZipFile::TEST_ZIP2.entry_names[0], entry.name) assert !zis.gets.empty? assert_equal(false, zis.eof?) entry = zis.get_next_entry # empty.txt assert_equal(TestZipFile::TEST_ZIP2.entry_names[1], entry.name) assert_equal(0, entry.size) assert_nil(zis.gets) assert_equal(true, zis.eof?) entry = zis.get_next_entry # empty_chmod640.txt assert_equal(TestZipFile::TEST_ZIP2.entry_names[2], entry.name) assert_equal(0, entry.size) assert_nil(zis.gets) assert_equal(true, zis.eof?) entry = zis.get_next_entry # short.txt assert_equal(TestZipFile::TEST_ZIP2.entry_names[3], entry.name) assert !zis.gets.empty? entry = zis.get_next_entry # longBinary.bin assert_equal(TestZipFile::TEST_ZIP2.entry_names[4], entry.name) assert !zis.gets.empty? end end def test_read_with_number_of_bytes_returns_nil_at_eof ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| entry = zis.get_next_entry # longAscii.txt zis.read(entry.size) assert_equal(true, zis.eof?) assert_nil(zis.read(1)) assert_nil(zis.read(1)) end end def test_rewind ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| e = zis.get_next_entry assert_equal(TestZipFile::TEST_ZIP2.entry_names[0], e.name) # Do a little reading buf = '' buf << zis.read(100) assert_equal(100, zis.pos) buf << (zis.gets || '') buf << (zis.gets || '') assert_equal(false, zis.eof?) zis.rewind buf2 = '' buf2 << zis.read(100) buf2 << (zis.gets || '') buf2 << (zis.gets || '') assert_equal(buf, buf2) zis.rewind assert_equal(false, zis.eof?) assert_equal(0, zis.pos) assert_entry(e.name, zis, e.name) end end def test_mix_read_and_gets ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| zis.get_next_entry assert_equal('#!/usr/bin/env ruby', zis.gets.chomp) assert_equal(false, zis.eof?) assert_equal('', zis.gets.chomp) assert_equal(false, zis.eof?) assert_equal('$VERBOSE =', zis.read(10)) assert_equal(false, zis.eof?) end end def test_ungetc ::Zip::InputStream.open(TestZipFile::TEST_ZIP2.zip_name) do |zis| zis.get_next_entry first_line = zis.gets.chomp first_line.reverse.bytes.each { |b| zis.ungetc(b) } assert_equal('#!/usr/bin/env ruby', zis.gets.chomp) assert_equal('$VERBOSE =', zis.read(10)) end end end rubyzip-2.3.0/test/ioextras/000077500000000000000000000000001363314356200160435ustar00rootroot00000000000000rubyzip-2.3.0/test/ioextras/abstract_input_stream_test.rb000066400000000000000000000046521363314356200240330ustar00rootroot00000000000000require 'test_helper' require 'zip/ioextras' class AbstractInputStreamTest < MiniTest::Test # AbstractInputStream subclass that provides a read method TEST_LINES = ["Hello world#{$INPUT_RECORD_SEPARATOR}", "this is the second line#{$INPUT_RECORD_SEPARATOR}", 'this is the last line'] TEST_STRING = TEST_LINES.join class TestAbstractInputStream include ::Zip::IOExtras::AbstractInputStream def initialize(string) super() @contents = string @read_ptr = 0 end def sysread(chars_to_read, _buf = nil) ret_val = @contents[@read_ptr, chars_to_read] @read_ptr += chars_to_read ret_val end def produce_input sysread(100) end def input_finished? @contents[@read_ptr].nil? end end def setup @io = TestAbstractInputStream.new(TEST_STRING) end def test_gets assert_equal(TEST_LINES[0], @io.gets) assert_equal(1, @io.lineno) assert_equal(TEST_LINES[0].length, @io.pos) assert_equal(TEST_LINES[1], @io.gets) assert_equal(2, @io.lineno) assert_equal(TEST_LINES[2], @io.gets) assert_equal(3, @io.lineno) assert_nil(@io.gets) assert_equal(4, @io.lineno) end def test_gets_multi_char_seperator assert_equal('Hell', @io.gets('ll')) assert_equal("o world#{$INPUT_RECORD_SEPARATOR}this is the second l", @io.gets('d l')) end LONG_LINES = [ 'x' * 48 + "\r\n", 'y' * 49 + "\r\n", 'rest' ] def test_gets_mulit_char_seperator_split io = TestAbstractInputStream.new(LONG_LINES.join) assert_equal(LONG_LINES[0], io.gets("\r\n")) assert_equal(LONG_LINES[1], io.gets("\r\n")) assert_equal(LONG_LINES[2], io.gets("\r\n")) end def test_gets_with_sep_and_index io = TestAbstractInputStream.new(LONG_LINES.join) assert_equal('x', io.gets("\r\n", 1)) assert_equal('x' * 47 + "\r", io.gets("\r\n", 48)) assert_equal("\n", io.gets(nil, 1)) assert_equal('yy', io.gets(nil, 2)) end def test_gets_with_index assert_equal(TEST_LINES[0], @io.gets(100)) assert_equal('this', @io.gets(4)) end def test_each_line line_num = 0 @io.each_line do |line| assert_equal(TEST_LINES[line_num], line) line_num += 1 end end def test_readlines assert_equal(TEST_LINES, @io.readlines) end def test_readline test_gets begin @io.readline raise 'EOFError expected' rescue EOFError end end end rubyzip-2.3.0/test/ioextras/abstract_output_stream_test.rb000066400000000000000000000054571363314356200242400ustar00rootroot00000000000000require 'test_helper' require 'zip/ioextras' class AbstractOutputStreamTest < MiniTest::Test class TestOutputStream include ::Zip::IOExtras::AbstractOutputStream attr_accessor :buffer def initialize @buffer = '' end def <<(data) @buffer << data self end end def setup @output_stream = TestOutputStream.new @save_comma_sep = $OUTPUT_FIELD_SEPARATOR @save_output_sep = $OUTPUT_RECORD_SEPARATOR end def teardown $, = @save_comma_sep $\ = @save_output_sep end def test_write count = @output_stream.write('a little string') assert_equal('a little string', @output_stream.buffer) assert_equal('a little string'.length, count) count = @output_stream.write('. a little more') assert_equal('a little string. a little more', @output_stream.buffer) assert_equal('. a little more'.length, count) end def test_print $\ = nil # record separator set to nil @output_stream.print('hello') assert_equal('hello', @output_stream.buffer) @output_stream.print(' world.') assert_equal('hello world.', @output_stream.buffer) @output_stream.print(' You ok ', 'out ', 'there?') assert_equal('hello world. You ok out there?', @output_stream.buffer) $\ = "\n" @output_stream.print assert_equal("hello world. You ok out there?\n", @output_stream.buffer) @output_stream.print('I sure hope so!') assert_equal("hello world. You ok out there?\nI sure hope so!\n", @output_stream.buffer) $, = 'X' @output_stream.buffer = '' @output_stream.print('monkey', 'duck', 'zebra') assert_equal("monkeyXduckXzebra\n", @output_stream.buffer) $\ = nil @output_stream.buffer = '' @output_stream.print(20) assert_equal('20', @output_stream.buffer) end def test_printf @output_stream.printf('%d %04x', 123, 123) assert_equal('123 007b', @output_stream.buffer) end def test_putc @output_stream.putc('A') assert_equal('A', @output_stream.buffer) @output_stream.putc(65) assert_equal('AA', @output_stream.buffer) end def test_puts @output_stream.puts assert_equal("\n", @output_stream.buffer) @output_stream.puts('hello', 'world') assert_equal("\nhello\nworld\n", @output_stream.buffer) @output_stream.buffer = '' @output_stream.puts("hello\n", "world\n") assert_equal("hello\nworld\n", @output_stream.buffer) @output_stream.buffer = '' @output_stream.puts(%W[hello\n world\n]) assert_equal("hello\nworld\n", @output_stream.buffer) @output_stream.buffer = '' @output_stream.puts(%W[hello\n world\n], 'bingo') assert_equal("hello\nworld\nbingo\n", @output_stream.buffer) @output_stream.buffer = '' @output_stream.puts(16, 20, 50, 'hello') assert_equal("16\n20\n50\nhello\n", @output_stream.buffer) end end rubyzip-2.3.0/test/ioextras/fake_io_test.rb000066400000000000000000000006001363314356200210200ustar00rootroot00000000000000require 'test_helper' require 'zip/ioextras' class FakeIOTest < MiniTest::Test class FakeIOUsingClass include ::Zip::IOExtras::FakeIO end def test_kind_of? obj = FakeIOUsingClass.new assert(obj.kind_of?(Object)) assert(obj.kind_of?(FakeIOUsingClass)) assert(obj.kind_of?(IO)) assert(!obj.kind_of?(Integer)) assert(!obj.kind_of?(String)) end end rubyzip-2.3.0/test/local_entry_test.rb000066400000000000000000000143171363314356200201120ustar00rootroot00000000000000require 'test_helper' class ZipLocalEntryTest < MiniTest::Test CEH_FILE = 'test/data/generated/centralEntryHeader.bin' LEH_FILE = 'test/data/generated/localEntryHeader.bin' def teardown ::Zip.write_zip64_support = false end def test_read_local_entry_header_of_first_test_zip_entry ::File.open(TestZipFile::TEST_ZIP3.zip_name, 'rb') do |file| entry = ::Zip::Entry.read_local_entry(file) assert_equal('', entry.comment) # Differs from windows and unix because of CR LF # assert_equal(480, entry.compressed_size) # assert_equal(0x2a27930f, entry.crc) # extra field is 21 bytes long # probably contains some unix attrutes or something # disabled: assert_equal(nil, entry.extra) assert_equal(::Zip::Entry::DEFLATED, entry.compression_method) assert_equal(TestZipFile::TEST_ZIP3.entry_names[0], entry.name) assert_equal(::File.size(TestZipFile::TEST_ZIP3.entry_names[0]), entry.size) assert(!entry.directory?) end end def test_read_date_time ::File.open('test/data/rubycode.zip', 'rb') do |file| entry = ::Zip::Entry.read_local_entry(file) assert_equal('zippedruby1.rb', entry.name) assert_equal(::Zip::DOSTime.at(1_019_261_638), entry.time) end end def test_read_local_entry_from_non_zip_file ::File.open('test/data/file2.txt') do |file| assert_nil(::Zip::Entry.read_local_entry(file)) end end def test_read_local_entry_from_truncated_zip_file fragment = '' # local header is at least 30 bytes ::File.open(TestZipFile::TEST_ZIP2.zip_name) { |f| fragment = f.read(12) } fragment.extend(IOizeString).reset entry = ::Zip::Entry.new entry.read_local_entry(fragment) raise 'ZipError expected' rescue ::Zip::Error end def test_write_entry entry = ::Zip::Entry.new('file.zip', 'entry_name', 'my little comment', 'thisIsSomeExtraInformation', 100, 987_654, ::Zip::Entry::DEFLATED, 400) write_to_file(LEH_FILE, CEH_FILE, entry) local_entry, central_entry = read_from_file(LEH_FILE, CEH_FILE) assert( central_entry.extra['Zip64Placeholder'].nil?, 'zip64 placeholder should not be used in central directory' ) compare_local_entry_headers(entry, local_entry) compare_c_dir_entry_headers(entry, central_entry) end def test_write_entry_with_zip64 ::Zip.write_zip64_support = true entry = ::Zip::Entry.new('file.zip', 'entry_name', 'my little comment', 'thisIsSomeExtraInformation', 100, 987_654, ::Zip::Entry::DEFLATED, 400) write_to_file(LEH_FILE, CEH_FILE, entry) local_entry, central_entry = read_from_file(LEH_FILE, CEH_FILE) assert( local_entry.extra['Zip64Placeholder'], 'zip64 placeholder should be used in local file header' ) # This was removed when writing the c_dir_entry, so remove from compare. local_entry.extra.delete('Zip64Placeholder') assert( central_entry.extra['Zip64Placeholder'].nil?, 'zip64 placeholder should not be used in central directory' ) compare_local_entry_headers(entry, local_entry) compare_c_dir_entry_headers(entry, central_entry) end def test_write_64entry ::Zip.write_zip64_support = true entry = ::Zip::Entry.new('bigfile.zip', 'entry_name', 'my little equine', 'malformed extra field because why not', 0x7766554433221100, 0xDEADBEEF, ::Zip::Entry::DEFLATED, 0x9988776655443322) write_to_file(LEH_FILE, CEH_FILE, entry) local_entry, central_entry = read_from_file(LEH_FILE, CEH_FILE) compare_local_entry_headers(entry, local_entry) compare_c_dir_entry_headers(entry, central_entry) end def test_rewrite_local_header64 ::Zip.write_zip64_support = true buf1 = StringIO.new entry = ::Zip::Entry.new('file.zip', 'entry_name') entry.write_local_entry(buf1) assert(entry.extra['Zip64'].nil?, 'zip64 extra is unnecessarily present') buf2 = StringIO.new entry.size = 0x123456789ABCDEF0 entry.compressed_size = 0x0123456789ABCDEF entry.write_local_entry(buf2, true) refute_nil(entry.extra['Zip64']) refute_equal(buf1.size, 0) assert_equal(buf1.size, buf2.size) # it can't grow, or we'd clobber file data end def test_read_local_offset entry = ::Zip::Entry.new('file.zip', 'entry_name') entry.local_header_offset = 12_345 ::File.open(CEH_FILE, 'wb') { |f| entry.write_c_dir_entry(f) } read_entry = nil ::File.open(CEH_FILE, 'rb') { |f| read_entry = ::Zip::Entry.read_c_dir_entry(f) } compare_c_dir_entry_headers(entry, read_entry) end def test_read64_local_offset ::Zip.write_zip64_support = true entry = ::Zip::Entry.new('file.zip', 'entry_name') entry.local_header_offset = 0x0123456789ABCDEF ::File.open(CEH_FILE, 'wb') { |f| entry.write_c_dir_entry(f) } read_entry = nil ::File.open(CEH_FILE, 'rb') { |f| read_entry = ::Zip::Entry.read_c_dir_entry(f) } compare_c_dir_entry_headers(entry, read_entry) end private def compare_local_entry_headers(entry1, entry2) assert_equal(entry1.compressed_size, entry2.compressed_size) assert_equal(entry1.crc, entry2.crc) assert_equal(entry1.extra, entry2.extra) assert_equal(entry1.compression_method, entry2.compression_method) assert_equal(entry1.name, entry2.name) assert_equal(entry1.size, entry2.size) assert_equal(entry1.local_header_offset, entry2.local_header_offset) end def compare_c_dir_entry_headers(entry1, entry2) compare_local_entry_headers(entry1, entry2) assert_equal(entry1.comment, entry2.comment) end def write_to_file(local_filename, central_filename, entry) ::File.open(local_filename, 'wb') { |f| entry.write_local_entry(f) } ::File.open(central_filename, 'wb') { |f| entry.write_c_dir_entry(f) } end def read_from_file(local_filename, central_filename) local_entry = nil cdir_entry = nil ::File.open(local_filename, 'rb') do |f| local_entry = ::Zip::Entry.read_local_entry(f) end ::File.open(central_filename, 'rb') do |f| cdir_entry = ::Zip::Entry.read_c_dir_entry(f) end [local_entry, cdir_entry] end end rubyzip-2.3.0/test/output_stream_test.rb000066400000000000000000000102211363314356200205000ustar00rootroot00000000000000require 'test_helper' class ZipOutputStreamTest < MiniTest::Test include AssertEntry TEST_ZIP = TestZipFile::TEST_ZIP2.clone TEST_ZIP.zip_name = 'test/data/generated/output.zip' def test_new zos = ::Zip::OutputStream.new(TEST_ZIP.zip_name) zos.comment = TEST_ZIP.comment write_test_zip(zos) zos.close assert_test_zip_contents(TEST_ZIP) end def test_open ::Zip::OutputStream.open(TEST_ZIP.zip_name) do |zos| zos.comment = TEST_ZIP.comment write_test_zip(zos) end assert_test_zip_contents(TEST_ZIP) end def test_write_buffer io = ::StringIO.new('') buffer = ::Zip::OutputStream.write_buffer(io) do |zos| zos.comment = TEST_ZIP.comment write_test_zip(zos) end File.open(TEST_ZIP.zip_name, 'wb') { |f| f.write buffer.string } assert_test_zip_contents(TEST_ZIP) end def test_write_buffer_binmode io = ::StringIO.new('') buffer = ::Zip::OutputStream.write_buffer(io) do |zos| zos.comment = TEST_ZIP.comment write_test_zip(zos) end assert_equal Encoding::ASCII_8BIT, buffer.external_encoding end def test_write_buffer_with_temp_file tmp_file = Tempfile.new('') ::Zip::OutputStream.write_buffer(tmp_file) do |zos| zos.comment = TEST_ZIP.comment write_test_zip(zos) end tmp_file.rewind File.open(TEST_ZIP.zip_name, 'wb') { |f| f.write(tmp_file.read) } tmp_file.unlink assert_test_zip_contents(TEST_ZIP) end def test_writing_to_closed_stream assert_i_o_error_in_closed_stream { |zos| zos << 'hello world' } assert_i_o_error_in_closed_stream { |zos| zos.puts 'hello world' } assert_i_o_error_in_closed_stream { |zos| zos.write 'hello world' } end def test_cannot_open_file name = TestFiles::EMPTY_TEST_DIR begin ::Zip::OutputStream.open(name) rescue SystemCallError assert($ERROR_INFO.kind_of?(Errno::EISDIR) || # Linux $ERROR_INFO.kind_of?(Errno::EEXIST) || # Windows/cygwin $ERROR_INFO.kind_of?(Errno::EACCES), # Windows "Expected Errno::EISDIR (or on win/cygwin: Errno::EEXIST), but was: #{$ERROR_INFO.class}") end end def test_put_next_entry stored_text = 'hello world in stored text' entry_name = 'file1' comment = 'my comment' ::Zip::OutputStream.open(TEST_ZIP.zip_name) do |zos| zos.put_next_entry(entry_name, comment, nil, ::Zip::Entry::STORED) zos << stored_text end assert(File.read(TEST_ZIP.zip_name)[stored_text]) ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(stored_text, zf.read(entry_name)) end end def test_put_next_entry_using_zip_entry_creates_entries_with_correct_timestamps file = ::File.open('test/data/file2.txt', 'rb') ::Zip::OutputStream.open(TEST_ZIP.zip_name) do |zos| zip_entry = ::Zip::Entry.new(zos, file.path, '', '', 0, 0, ::Zip::Entry::DEFLATED, 0, ::Zip::DOSTime.at(file.mtime)) zos.put_next_entry(zip_entry) zos << file.read end ::Zip::InputStream.open(TEST_ZIP.zip_name) do |io| while (entry = io.get_next_entry) # Compare DOS Times, since they are stored with two seconds accuracy assert(::Zip::DOSTime.at(file.mtime).dos_equals(::Zip::DOSTime.at(entry.mtime))) end end end def test_chained_put_into_next_entry stored_text = 'hello world in stored text' stored_text2 = 'with chain' entry_name = 'file1' comment = 'my comment' ::Zip::OutputStream.open(TEST_ZIP.zip_name) do |zos| zos.put_next_entry(entry_name, comment, nil, ::Zip::Entry::STORED) zos << stored_text << stored_text2 end assert(File.read(TEST_ZIP.zip_name)[stored_text]) ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_equal(stored_text + stored_text2, zf.read(entry_name)) end end def assert_i_o_error_in_closed_stream assert_raises(IOError) do zos = ::Zip::OutputStream.new('test/data/generated/test_putOnClosedStream.zip') zos.close yield zos end end def write_test_zip(zos) TEST_ZIP.entry_names.each do |entry_name| zos.put_next_entry(entry_name) File.open(entry_name, 'rb') { |f| zos.write(f.read) } end end end rubyzip-2.3.0/test/pass_thru_compressor_test.rb000066400000000000000000000012021363314356200220500ustar00rootroot00000000000000require 'test_helper' class PassThruCompressorTest < MiniTest::Test include CrcTest def test_size File.open('test/data/generated/dummy.txt', 'wb') do |file| compressor = ::Zip::PassThruCompressor.new(file) assert_equal(0, compressor.size) t1 = 'hello world' t2 = '' t3 = 'bingo' compressor << t1 assert_equal(compressor.size, t1.size) compressor << t2 assert_equal(compressor.size, t1.size + t2.size) compressor << t3 assert_equal(compressor.size, t1.size + t2.size + t3.size) end end def test_crc run_crc_test(::Zip::PassThruCompressor) end end rubyzip-2.3.0/test/pass_thru_decompressor_test.rb000066400000000000000000000004321363314356200223650ustar00rootroot00000000000000require 'test_helper' class PassThruDecompressorTest < MiniTest::Test include DecompressorTests def setup super @file = File.new(TEST_FILE) @decompressor = ::Zip::PassThruDecompressor.new(@file, File.size(TEST_FILE)) end def teardown @file.close end end rubyzip-2.3.0/test/path_traversal_test.rb000066400000000000000000000113541363314356200206140ustar00rootroot00000000000000require 'test_helper' class PathTraversalTest < MiniTest::Test TEST_FILE_ROOT = File.absolute_path('test/data/path_traversal') def setup # With apologies to anyone using these files... but they are the files in # the sample zips, so we don't have much choice here. FileUtils.rm_f '/tmp/moo' FileUtils.rm_f '/tmp/file.txt' end def extract_paths(zip_path, entries) ::Zip::File.open(::File.join(TEST_FILE_ROOT, zip_path)) do |zip| entries.each do |entry, test| if test == :error assert_raises(Errno::ENOENT) do zip.find_entry(entry).extract end else assert_output('', test) do zip.find_entry(entry).extract end end end end end def in_tmpdir Dir.mktmpdir do |tmp| test_path = File.join(tmp, 'test') Dir.mkdir test_path Dir.chdir test_path do yield test_path end end end def test_leading_slash entries = { '/tmp/moo' => /WARNING: skipped \'\/tmp\/moo\'/ } in_tmpdir do extract_paths(['jwilk', 'absolute1.zip'], entries) refute File.exist?('/tmp/moo') end end def test_multiple_leading_slashes entries = { '//tmp/moo' => /WARNING: skipped \'\/\/tmp\/moo\'/ } in_tmpdir do extract_paths(['jwilk', 'absolute2.zip'], entries) refute File.exist?('/tmp/moo') end end def test_leading_dot_dot entries = { '../moo' => /WARNING: skipped \'\.\.\/moo\'/ } in_tmpdir do extract_paths(['jwilk', 'relative0.zip'], entries) refute File.exist?('../moo') end end def test_non_leading_dot_dot_with_existing_folder entries = { 'tmp/' => '', 'tmp/../../moo' => /WARNING: skipped \'tmp\/\.\.\/\.\.\/moo\'/ } in_tmpdir do extract_paths('relative1.zip', entries) assert Dir.exist?('tmp') refute File.exist?('../moo') end end def test_non_leading_dot_dot_without_existing_folder entries = { 'tmp/../../moo' => /WARNING: skipped \'tmp\/\.\.\/\.\.\/moo\'/ } in_tmpdir do extract_paths(['jwilk', 'relative2.zip'], entries) refute File.exist?('../moo') end end def test_file_symlink entries = { 'moo' => '' } in_tmpdir do extract_paths(['jwilk', 'symlink.zip'], entries) assert File.exist?('moo') refute File.exist?('/tmp/moo') end end def test_directory_symlink # Can't create tmp/moo, because the tmp symlink is skipped. entries = { 'tmp' => /WARNING: skipped symlink \'tmp\'/, 'tmp/moo' => :error } in_tmpdir do extract_paths(['jwilk', 'dirsymlink.zip'], entries) refute File.exist?('/tmp/moo') end end def test_two_directory_symlinks_a # Can't create par/moo because the symlinks are skipped. entries = { 'cur' => /WARNING: skipped symlink \'cur\'/, 'par' => /WARNING: skipped symlink \'par\'/, 'par/moo' => :error } in_tmpdir do extract_paths(['jwilk', 'dirsymlink2a.zip'], entries) refute File.exist?('cur') refute File.exist?('par') refute File.exist?('par/moo') end end def test_two_directory_symlinks_b # Can't create par/moo, because the symlinks are skipped. entries = { 'cur' => /WARNING: skipped symlink \'cur\'/, 'cur/par' => /WARNING: skipped symlink \'cur\/par\'/, 'par/moo' => :error } in_tmpdir do extract_paths(['jwilk', 'dirsymlink2b.zip'], entries) refute File.exist?('cur') refute File.exist?('../moo') end end def test_entry_name_with_absolute_path_does_not_extract entries = { '/tmp/' => /WARNING: skipped \'\/tmp\/\'/, '/tmp/file.txt' => /WARNING: skipped \'\/tmp\/file.txt\'/ } in_tmpdir do extract_paths(['tuzovakaoff', 'absolutepath.zip'], entries) refute File.exist?('/tmp/file.txt') end end def test_entry_name_with_absolute_path_extract_when_given_different_path in_tmpdir do |test_path| zip_path = File.join(TEST_FILE_ROOT, 'tuzovakaoff', 'absolutepath.zip') Zip::File.open(zip_path) do |zip_file| zip_file.each do |entry| entry.extract(File.join(test_path, entry.name)) end end refute File.exist?('/tmp/file.txt') end end def test_entry_name_with_relative_symlink # Doesn't create the symlink path, so can't create path/file.txt. entries = { 'path' => /WARNING: skipped symlink \'path\'/, 'path/file.txt' => :error } in_tmpdir do extract_paths(['tuzovakaoff', 'symlink.zip'], entries) refute File.exist?('/tmp/file.txt') end end def test_entry_name_with_tilde in_tmpdir do extract_paths('tilde.zip', '~tilde~' => '') assert File.exist?('~tilde~') end end end rubyzip-2.3.0/test/samples/000077500000000000000000000000001363314356200156515ustar00rootroot00000000000000rubyzip-2.3.0/test/samples/example_recursive_test.rb000066400000000000000000000016321363314356200227610ustar00rootroot00000000000000require 'test_helper' require 'fileutils' require_relative '../../samples/example_recursive' class ExampleRecursiveTest < MiniTest::Test DIRECTORY_TO_ZIP = 'test/data/globTest' OUTPUT_DIRECTORY = 'test/data/example_recursive.zip' TEMP_DIRECTORY = 'test/data/tmp' def setup @generator = ::ZipFileGenerator.new(DIRECTORY_TO_ZIP, OUTPUT_DIRECTORY) end def teardown FileUtils.rm_rf TEMP_DIRECTORY FileUtils.rm_f OUTPUT_DIRECTORY end def test_write @generator.write unzip assert_equal Dir.entries(DIRECTORY_TO_ZIP).sort, Dir.entries(TEMP_DIRECTORY).sort end private def unzip(file = OUTPUT_DIRECTORY) Zip::File.open(file) do |zip_file| zip_file.each do |f| file_path = File.join(TEMP_DIRECTORY, f.name) FileUtils.mkdir_p(File.dirname(file_path)) zip_file.extract(f, file_path) unless File.exist?(file_path) end end end end rubyzip-2.3.0/test/settings_test.rb000066400000000000000000000047721363314356200174430ustar00rootroot00000000000000require 'test_helper' class ZipSettingsTest < MiniTest::Test # TODO: Refactor out into common test module include CommonZipFileFixture TEST_OUT_NAME = 'test/data/generated/emptyOutDir' def setup super Dir.rmdir(TEST_OUT_NAME) if File.directory? TEST_OUT_NAME File.delete(TEST_OUT_NAME) if File.exist? TEST_OUT_NAME end def teardown ::Zip.reset! end def open_zip(&a_proc) refute_nil(a_proc) ::Zip::File.open(TestZipFile::TEST_ZIP4.zip_name, &a_proc) end def extract_test_dir(&a_proc) open_zip do |zf| zf.extract(TestFiles::EMPTY_TEST_DIR, TEST_OUT_NAME, &a_proc) end end def test_true_on_exists_proc Zip.on_exists_proc = true File.open(TEST_OUT_NAME, 'w') { |f| f.puts 'something' } extract_test_dir assert(File.directory?(TEST_OUT_NAME)) end def test_false_on_exists_proc Zip.on_exists_proc = false File.open(TEST_OUT_NAME, 'w') { |f| f.puts 'something' } assert_raises(Zip::DestinationFileExistsError) { extract_test_dir } end def test_false_continue_on_exists_proc Zip.continue_on_exists_proc = false assert_raises(::Zip::EntryExistsError) do ::Zip::File.open(TEST_ZIP.zip_name) do |zf| zf.add(zf.entries.first.name, 'test/data/file2.txt') end end end def test_true_continue_on_exists_proc Zip.continue_on_exists_proc = true replaced_entry = nil ::Zip::File.open(TEST_ZIP.zip_name) do |zf| replaced_entry = zf.entries.first.name zf.add(replaced_entry, 'test/data/file2.txt') end ::Zip::File.open(TEST_ZIP.zip_name) do |zf| assert_contains(zf, replaced_entry, 'test/data/file2.txt') end end def test_false_warn_invalid_date test_file = File.join(File.dirname(__FILE__), 'data', 'WarnInvalidDate.zip') Zip.warn_invalid_date = false assert_output('', '') do ::Zip::File.open(test_file) do |_zf| end end end def test_true_warn_invalid_date test_file = File.join(File.dirname(__FILE__), 'data', 'WarnInvalidDate.zip') Zip.warn_invalid_date = true assert_output('', /invalid date\/time in zip entry/) do ::Zip::File.open(test_file) do |_zf| end end end private def assert_contains(zip_file, entry_name, filename = entry_name) refute_nil( zip_file.entries.detect { |e| e.name == entry_name }, "entry #{entry_name} not in #{zip_file.entries.join(', ')} in zip file #{zip_file}" ) assert_entry_contents(zip_file, entry_name, filename) if File.exist?(filename) end end rubyzip-2.3.0/test/stored_support_test.rb000066400000000000000000000023431363314356200206670ustar00rootroot00000000000000require 'test_helper' class StoredSupportTest < MiniTest::Test STORED_ZIP_TEST_FILE = 'test/data/zipWithStoredCompression.zip' ENCRYPTED_STORED_ZIP_TEST_FILE = 'test/data/zipWithStoredCompressionAndEncryption.zip' INPUT_FILE1 = 'test/data/file1.txt' INPUT_FILE2 = 'test/data/file2.txt' def test_read Zip::InputStream.open(STORED_ZIP_TEST_FILE) do |zis| entry = zis.get_next_entry assert_equal 'file1.txt', entry.name assert_equal 1_327, entry.size assert_equal ::File.open(INPUT_FILE1, 'r').read, zis.read entry = zis.get_next_entry assert_equal 'file2.txt', entry.name assert_equal 41_234, entry.size assert_equal ::File.open(INPUT_FILE2, 'r').read, zis.read end end def test_encrypted_read Zip::InputStream.open(ENCRYPTED_STORED_ZIP_TEST_FILE, 0, Zip::TraditionalDecrypter.new('password')) do |zis| entry = zis.get_next_entry assert_equal 'file1.txt', entry.name assert_equal 1_327, entry.size assert_equal ::File.open(INPUT_FILE1, 'r').read, zis.read entry = zis.get_next_entry assert_equal 'file2.txt', entry.name assert_equal 41_234, entry.size assert_equal ::File.open(INPUT_FILE2, 'r').read, zis.read end end end rubyzip-2.3.0/test/test_helper.rb000066400000000000000000000124731363314356200170570ustar00rootroot00000000000000require 'simplecov' require 'minitest/autorun' require 'minitest/unit' require 'fileutils' require 'tmpdir' require 'digest/sha1' require 'zip' require 'gentestfiles' TestFiles.create_test_files TestZipFile.create_test_zips if defined? JRUBY_VERSION require 'jruby' JRuby.objectspace = true end ::MiniTest.after_run do FileUtils.rm_rf('test/data/generated') end module IOizeString attr_reader :tell def read(count = nil) @tell ||= 0 count ||= size ret_val = slice(@tell, count) @tell += count ret_val end def seek(index, offset) @tell ||= 0 case offset when IO::SEEK_END pos = size + index when IO::SEEK_SET pos = index when IO::SEEK_CUR pos = @tell + index else raise 'Error in test method IOizeString::seek' end raise Errno::EINVAL if pos < 0 || pos >= size @tell = pos end def reset @tell = 0 end end module DecompressorTests # expects @ref_text, @ref_lines and @decompressor TEST_FILE = 'test/data/file1.txt' def setup @ref_text = '' File.open(TEST_FILE) { |f| @ref_text = f.read } @ref_lines = @ref_text.split($INPUT_RECORD_SEPARATOR) end def test_read_everything assert_equal(@ref_text, @decompressor.read) end def test_read_in_chunks size = 5 while (chunk = @decompressor.read(size)) assert_equal(@ref_text.slice!(0, size), chunk) end assert_equal(0, @ref_text.size) end end module AssertEntry def assert_next_entry(filename, zis) assert_entry(filename, zis, zis.get_next_entry.name) end def assert_entry(filename, zis, entry_name) assert_equal(filename, entry_name) assert_entry_contents_for_stream(filename, zis, entry_name) end def assert_entry_contents_for_stream(filename, zis, entry_name) File.open(filename, 'rb') do |file| expected = file.read actual = zis.read if expected != actual if (expected && actual) && (expected.length > 400 || actual.length > 400) entry_filename = entry_name + '.zipEntry' File.open(entry_filename, 'wb') { |entryfile| entryfile << actual } raise("File '#{filename}' is different from '#{entry_filename}'") else assert_equal(expected, actual) end end end end def self.assert_contents(filename, string) contents = '' File.open(filename, 'rb') { |f| contents = f.read } return unless contents != string if contents.length > 400 || string.length > 400 string_file = filename + '.other' File.open(string_file, 'wb') { |f| f << string } raise("File '#{filename}' is different from contents of string stored in '#{string_file}'") else assert_equal(contents, string) end end def assert_stream_contents(zis, zip_file) assert(!zis.nil?) zip_file.entry_names.each do |entry_name| assert_next_entry(entry_name, zis) end assert_nil(zis.get_next_entry) end def assert_test_zip_contents(zip_file) ::Zip::InputStream.open(zip_file.zip_name) do |zis| assert_stream_contents(zis, zip_file) end end def assert_entry_contents(zip_file, entry_name, filename = entry_name.to_s) zis = zip_file.get_input_stream(entry_name) assert_entry_contents_for_stream(filename, zis, entry_name) ensure zis.close if zis end end module CrcTest class TestOutputStream include ::Zip::IOExtras::AbstractOutputStream attr_accessor :buffer def initialize @buffer = '' end def <<(data) @buffer << data self end end def run_crc_test(compressor_class) str = "Here's a nice little text to compute the crc for! Ho hum, it is nice nice nice nice indeed." fake_out = TestOutputStream.new deflater = compressor_class.new(fake_out) deflater << str assert_equal(0x919920fc, deflater.crc) end end module Enumerable def compare_enumerables(enumerable) array = enumerable.to_a each_with_index do |element, index| return false unless yield(element, array[index]) end size == array.size end end module CommonZipFileFixture include AssertEntry EMPTY_FILENAME = 'emptyZipFile.zip' TEST_ZIP = TestZipFile::TEST_ZIP2.clone TEST_ZIP.zip_name = 'test/data/generated/5entry_copy.zip' def setup File.delete(EMPTY_FILENAME) if File.exist?(EMPTY_FILENAME) FileUtils.cp(TestZipFile::TEST_ZIP2.zip_name, TEST_ZIP.zip_name) end end module ExtraAssertions def assert_forwarded(object, method, ret_val, *expected_args) call_args = nil call_args_proc = proc { |args| call_args = args } object.instance_eval <<-END_EVAL, __FILE__, __LINE__ + 1 alias #{method}_org #{method} def #{method}(*args) ObjectSpace._id2ref(#{call_args_proc.object_id}).call(args) ObjectSpace._id2ref(#{ret_val.object_id}) end END_EVAL assert_equal(ret_val, yield) # Invoke test assert_equal(expected_args, call_args) ensure object.instance_eval <<-END_EVAL, __FILE__, __LINE__ + 1 undef #{method} alias #{method} #{method}_org END_EVAL end end module ZipEntryData TEST_ZIPFILE = 'someZipFile.zip' TEST_COMMENT = 'a comment' TEST_COMPRESSED_SIZE = 1234 TEST_CRC = 325_324 TEST_EXTRA = 'Some data here' TEST_COMPRESSIONMETHOD = ::Zip::Entry::DEFLATED TEST_NAME = 'entry name' TEST_SIZE = 8432 TEST_ISDIRECTORY = false TEST_TIME = Time.now end rubyzip-2.3.0/test/unicode_file_names_and_comments_test.rb000066400000000000000000000034511363314356200241330ustar00rootroot00000000000000require 'test_helper' class ZipUnicodeFileNamesAndComments < MiniTest::Test FILENAME = File.join(File.dirname(__FILE__), 'test1.zip') def test_unicode_file_name file_entrys = ['текстовыйфайл.txt', 'Résumé.txt', '슬레이어스휘.txt'] directory_entrys = ['папка/текстовыйфайл.txt', 'Résumé/Résumé.txt', '슬레이어스휘/슬레이어스휘.txt'] stream = ::Zip::OutputStream.open(FILENAME) do |io| file_entrys.each do |filename| io.put_next_entry(filename) io.write(filename) end directory_entrys.each do |filepath| io.put_next_entry(filepath) io.write(filepath) end end assert(!stream.nil?) ::Zip::InputStream.open(FILENAME) do |io| file_entrys.each do |filename| entry = io.get_next_entry entry_name = entry.name entry_name = entry_name.force_encoding('UTF-8') assert(filename == entry_name) end directory_entrys.each do |filepath| entry = io.get_next_entry entry_name = entry.name entry_name = entry_name.force_encoding('UTF-8') assert(filepath == entry_name) end end ::Zip.force_entry_names_encoding = 'UTF-8' ::Zip::File.open(FILENAME) do |zip| file_entrys.each do |filename| refute_nil(zip.find_entry(filename)) end directory_entrys.each do |filepath| refute_nil(zip.find_entry(filepath)) end end ::Zip.force_entry_names_encoding = nil ::File.unlink(FILENAME) end def test_unicode_comment str = '渠道升级' ::Zip::File.open(FILENAME, Zip::File::CREATE) do |z| z.comment = str end ::Zip::File.open(FILENAME) do |z| assert(z.comment.force_encoding('UTF-8') == str) end ::File.unlink(FILENAME) end end rubyzip-2.3.0/test/zip64_full_test.rb000066400000000000000000000032641363314356200175740ustar00rootroot00000000000000if ENV['FULL_ZIP64_TEST'] require 'minitest/autorun' require 'minitest/unit' require 'fileutils' require 'zip' # test zip64 support for real, by actually exceeding the 32-bit size/offset limits # this test does not, of course, run with the normal unit tests! ;) class Zip64FullTest < MiniTest::Test def teardown ::Zip.reset! end def prepare_test_file(test_filename) ::File.delete(test_filename) if ::File.exist?(test_filename) test_filename end def test_large_zip_file ::Zip.write_zip64_support = true first_text = 'starting out small' last_text = 'this tests files starting after 4GB in the archive' test_filename = prepare_test_file('huge.zip') ::Zip::OutputStream.open(test_filename) do |io| io.put_next_entry('first_file.txt') io.write(first_text) # write just over 4GB (stored, so the zip file exceeds 4GB) buf = 'blah' * 16_384 io.put_next_entry('huge_file', nil, nil, ::Zip::Entry::STORED) 65_537.times { io.write(buf) } io.put_next_entry('last_file.txt') io.write(last_text) end ::Zip::File.open(test_filename) do |zf| assert_equal %w[first_file.txt huge_file last_file.txt], zf.entries.map(&:name) assert_equal first_text, zf.read('first_file.txt') assert_equal last_text, zf.read('last_file.txt') end # note: if this fails, be sure you have UnZip version 6.0 or newer # as this is the first version to support zip64 extensions # but some OSes (*cough* OSX) still bundle a 5.xx release assert system("unzip -tqq #{test_filename}"), 'third-party zip validation failed' end end end rubyzip-2.3.0/test/zip64_support_test.rb000066400000000000000000000006521363314356200203440ustar00rootroot00000000000000require 'test_helper' class Zip64SupportTest < MiniTest::Test TEST_FILE = File.join(File.dirname(__FILE__), 'data', 'zip64-sample.zip') def test_open_zip64_file zip_file = ::Zip::File.open(TEST_FILE) assert(!zip_file.nil?) assert(zip_file.entries.count == 2) test_rb = zip_file.entries.find { |x| x.name == 'test.rb' } assert(test_rb.size == 482) assert(test_rb.compressed_size == 229) end end