pax_global_header00006660000000000000000000000064141606670640014524gustar00rootroot0000000000000052 comment=dcf0825338889c5599932fb89a4aed8fba9b770b appstream-generator-0.8.6/000077500000000000000000000000001416066706400155175ustar00rootroot00000000000000appstream-generator-0.8.6/.editorconfig000066400000000000000000000003631416066706400201760ustar00rootroot00000000000000# See https://editorconfig.org/ root = true [*] end_of_line = lf trim_trailing_whitespace = true charset = utf-8 indent_style = space indent_size = 4 [*.yml] indent_style = space indent_size = 2 [*.xml] indent_style = space indent_size = 2 appstream-generator-0.8.6/.github/000077500000000000000000000000001416066706400170575ustar00rootroot00000000000000appstream-generator-0.8.6/.github/workflows/000077500000000000000000000000001416066706400211145ustar00rootroot00000000000000appstream-generator-0.8.6/.github/workflows/build-test.yml000066400000000000000000000025671416066706400237250ustar00rootroot00000000000000name: Build Test on: [push, pull_request] jobs: build-debian-testing: name: Debian Testing runs-on: ubuntu-latest strategy: matrix: dc: [ldc, gdc] steps: - uses: actions/checkout@v2 - name: Create Build Environment run: cd tests/ci/ && podman build -t asgen -f ./Dockerfile-debian-testing . - name: Build & Test continue-on-error: ${{ matrix.dc == 'gdc' }} run: podman run -a stdout -a stderr -e DC=${{ matrix.dc }} -v `pwd`:/build asgen ./tests/ci/build-and-test.sh build-fedora-rawhide: name: Fedora Rawhide runs-on: ubuntu-latest strategy: matrix: dc: [ldc, gdc] steps: - uses: actions/checkout@v2 - name: Create Build Environment run: cd tests/ci/ && podman build -t asgen -f ./Dockerfile-fedora-rawhide . - name: Build & Test continue-on-error: ${{ matrix.dc == 'gdc' }} run: podman run -a stdout -a stderr -e DC=${{ matrix.dc }} -v `pwd`:/build asgen ./tests/ci/build-and-test.sh build-ubuntu-lts: name: Ubuntu LTS runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Create Build Environment run: sudo ./tests/ci/install-deps-deb.sh - name: Make & Install 3rd-party run: sudo ./tests/ci/ci-install-extern.sh - name: Build & Test run: DC=ldc2 ./tests/ci/build-and-test.sh appstream-generator-0.8.6/.gitignore000066400000000000000000000001121416066706400175010ustar00rootroot00000000000000build/ .dub docs.json dub.selections.json __dummy.html *.o *.obj *.so *.a appstream-generator-0.8.6/LICENSE000066400000000000000000000167431416066706400165370ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. appstream-generator-0.8.6/MAINTAINERS000066400000000000000000000000471416066706400172150ustar00rootroot00000000000000Matthias Klumpp E-mail: mak@debian.org appstream-generator-0.8.6/NEWS000066400000000000000000000667551416066706400162410ustar00rootroot00000000000000Version 0.8.6 ~~~~~~~~~~~~~~ Released: 2021-12-22 Notes: * This is the first release to fully use libappstream-compose for almost all metadata analysis (except icon processing, which needs special care). Features: * Reimplement data extractor using the compose API * Enable screencasts in compose based on our screenshotVideos feature flag * Run external metadata through the exact same processing as all other data * Redirect glib debug messages to our own logging if in verbose mode * Adjust parallel processing work unit sizes for seeding * ci: Add Fedora to the CI environment Bugfixes: * Fix build with GLib >= 2.69 * engine: Protect iconTarFiles variable when scanning for icons in parallel * Resolve some issue tag conflicts with as-compose * Control batch processing better to enable better extractor reuse * Add duplicate-metainfo exemption for injected data * Improve explanation of the "no-metainfo" tag * Don't emit "description-from-package" if "no-metainfo" was already present Contributors: Neal Gompa (ニール・ゴンパ), Matthias Klumpp Version 0.8.5 ~~~~~~~~~~~~~~ Released: 2021-08-28 Features: * snap: Set `grade` to `stable` * Improve metainfo/desktop deduplication code * Use ascompose for adding translation status data Bugfixes: * Always mark desktop-file as handled when handling its metainfo file (#91) * Fix compilation due to broken generated GdkPixbuf code * config: Move setUseOptipng call after we determine if optipng is available * snap: Add glib{,-networking} to asgen's stage-packages * snap: Build libas from a tag * Adjust for compose API changes Contributors: Balló György, Dan Printzell, Iain Lane and Matthias Klumpp Version 0.8.4 ~~~~~~~~~~~~~~ Released: 2021-03-02 Features: * Replace our HashMap with native associative arrays * Switch back to using the curl-based downloader again * Use the desktop-entry parsing code from appstream-compose * Try finding icons in /usr/share/icons root directory as well * Update our own metainfo file and validate it Bugfixes: * snap: Set LD_LIBRARY_PATH to the stage dir for asgen's build * snap: Install curl as a normal build-package * snap: Build with -j1 * debian: Forget a package's filename when removing its temporary dir * debian: Add more synchronization * zarchive: Log the error when we can't open an archive * various: Improve builddir != srcdir builds and build the snap like this * Replace theme icon generator concurrency with a range * snap: Build AppStream in debugoptimized mode instead of release mode Contributors: Iain Lane, Matthias Klumpp Version 0.8.3 ~~~~~~~~~~~~~~ Released: 2021-02-02 Notes: * This release requires libappstream-compose from the AppStream project to build. The library is still in progress and currently has an unstable API, but is developed in sync with appstream-generator, so asgen can safely depend on it. Features: * Elevate no-metainfo presence to a warning by default (Matthias Klumpp) * Ignore all apps with only desktop-entry files using OnlyShowIn (Matthias Klumpp) * Make use of the helper classes provided by appstream-compose (Matthias Klumpp) * Add editorconfig (Matthias Klumpp) * Use ascompose result type as base for the generator result container (Matthias Klumpp) * Use the validator helper function from appstream-compose (Matthias Klumpp) * Use metainfo parsing helper from appstream-compose (Matthias Klumpp) * alpine: add capabilities to download packages via HTTP (Rasmus Thomsen) * config: allow overriding export dir via --export-dir (Rasmus Thomsen) Bugfixes: * Add explicit option to disable network-dependent tests (Matthias Klumpp) * Add missing CSS styling for permalinks to Debian template (Matthias Klumpp) * Captialize "MetaInfo" the same everywhere in hint messages (Matthias Klumpp) * Use binding generator to create the missing AppStream Utils functions (Matthias Klumpp) * Never open contents cache DB env more than once in the same thread (Matthias Klumpp) * Re-enable LMDB TLS support (Matthias Klumpp) * downloader: Check read byte count before appending to buffer (Matthias Klumpp) * Ensure export directory paths are sane, absolute paths all the time (Matthias Klumpp) Version 0.8.2 ~~~~~~~~~~~~~~ Released: 2020-05-12 Features: * Add experimental Snapcraft build definition * snap: Move to strict confinement (Iain Lane) * snap: Lots of debugging & integration work (Iain Lane) * Find external binaries in PATH instead of hardcoding absolute paths to them * Add static bindings for libsoup * Switch to new libsoup-based downloader, drop Curl * Always log the generator version for some operations * Display more verbose debug messages when downloads are retried * Better messages for network connection errors * Support Ubuntu language packs for l10n status info extraction * Make permalink anchors visible in HTML output * alpine: Add new backend for Alpine Linux (Rasmus Thomsen) Bugfixes: * Pull a few Mustache engine code improvements from upstream * Throw a better error message in unit tests when ffprobe isn't found * tests: Skip video metadata check if ffprobe can not be found * Check if our GdkPixbuf is able to handle all essential image formats * Slighty reduce default logging verbosity when finding icons * Give up icon search when component was rejected in icon storage routine * Swallow bad last-modified times from servers instead of freaking out * Make downloader a thread-local singleton (instead of thread-global) * Create XDG runtime dir in case it is missing * Make a failure to read an archive symlink target non-fatal * Print available font names in issue report if no matching font was found * Handle unexpected NULL-byte files better * Find Yarn, even when it was renamed * Strip out release artifacts for components that have a package Version 0.8.1 ~~~~~~~~~~~~~~ Released: 2020-01-20 Bugfixes: * Don't crash if a gettext locale has no strings * Reject packages which should have a long description but don't have one * debian: Search for bz2 files again, i18n still uses those * Work around some curl/D behavior changes and make HTTPS downloads work again * Check suite status before removing hints/components * Don't fail download on HTTP status code 302 * Properly fail icon search in all failure cases, add more debug logging * Ensure icon tarballs are closed immediately after writing to them * Flip around sorting of suites on HTML reports Version 0.8.0 ~~~~~~~~~~~~~~ Released: 2019-09-24 Notes: * This release needs FFmpeg installed if video screenshots should be permitted. No transcoding will be done. Check out the options to modify this feature in the documentation. Features: * Add Igor Khasilev's container implementations to the source * Replace EMSI containers with built-in implementation * Bump dependency on AppStream * Fix build with latest AppStream * Implement improved validation issue reporting * Always build registry of all hint tags, instead of adding them on-demand * Add AV1 video sample for video support tests * Add video tag support, analyze and store videos * Use generic download method for downloading screenshots as well Bugfixes: * Make dscanner happy again * Drop wrap files: We don't need them anymore * debian: Don't check for bz2 compressed indices * Only add remote icons if we have a mediaBaseUrl and are permitting image caches * Validate injected metainfo files as well * Ignore pedantic issues in HTML reports * Fix getFileContents when acting on remote data * XML-escape validator explanations Version 0.7.7 ~~~~~~~~~~~~~~ Released: 2019-02-24 Features: * Speed up locale search by moving it to its own database cache (Matthias Klumpp) Bugfixes: * Component removal requests don't need to have a package-name set (Matthias Klumpp) Version 0.7.6 ~~~~~~~~~~~~~~ Released: 2019-01-10 Features: * Limit the amount of release metadata in output (Matthias Klumpp) * Implement icon loading for injected metadata (Matthias Klumpp) * Prefer some SFNT font metadata over internal heuristics (Matthias Klumpp) * Implement Gettext locale processing (Matthias Klumpp) * Use a struct instead of bitfield to store enabled generator features (Matthias Klumpp) * Refactor locale statistics loader (Matthias Klumpp) * Explicitly remove temporary package data after reading locale info (Matthias Klumpp) * Make arch-specific injected metadata override arch:all data (Matthias Klumpp) Bugfixes: * Complain if the user wants to associate a component with a fake package (Matthias Klumpp) * Check that fonts and OS components have an icon (Matthias Klumpp) * Sanitize image URLs before attempting download (Matthias Klumpp) Version 0.7.5 ~~~~~~~~~~~~~~ Released: 2019-01-04 Features: * Load components for Arch Linux (Balló György) * Embed mustache-d code (Matthias Klumpp) * Protect against HTTPS-to-HTTP downgrades (Matthias Klumpp) * Allow asgen maintainer to mark components for removal and inject additional metainfo files (Matthias Klumpp) * docs: Document the metainfo injection feature (Matthias Klumpp) Bugfixes: * Fix a few issues found by dscanner (Matthias Klumpp) * Properly error out on HTTP 404 status codes (Matthias Klumpp) * Add wrapfile for mir-core (Matthias Klumpp) * Also ignore web applications without an icon (Matthias Klumpp) Version 0.7.4 ~~~~~~~~~~~~~~ Released: 2018-08-04 Features: * Write CID<->GCID mapping table as additional output data (Matthias Klumpp) * Don't rebuild GLibD as part of asgen, use the shared library instead (Matthias Klumpp) * Add hint to add launchable tag if .desktop file is missing (Matthias Klumpp) * Use posix_spawn codepath for optipng if possible via GLib (Matthias Klumpp) * Unconditionally add stock-type icon if desktop-file allows for it (Matthias Klumpp) * Allow font languages to be specified in a languages tag (Matthias Klumpp) * Improve font language processing with metainfo hints (Matthias Klumpp) * fonts: Sort selected font styles, prefer regular style for samples (Matthias Klumpp) * fonts: Use a random pangram for fonts supporting English (Matthias Klumpp) * fonts: Assume 100% language support for all locale mentioned in font data (Matthias Klumpp) Bugfixes: * Fix build with phobos 2.081 (Antonio Rojas) * Depend on non-broken Meson version (Matthias Klumpp) * fonts: Always prefer English for samples if font supports it (Matthias Klumpp) * Initialize font icon lookup table only if there are fonts (Matthias Klumpp) Version 0.7.3 ~~~~~~~~~~~~~~ Released: 2018-04-26 Features: * Don't include desktop files with an empty OnlyShowIn and complain about that * Improve decompression code slightly * Improve code to check for inclusion of .desktop files * Optimize category filter function * ubuntu: Use GC managed arrays for langpack info storage * ubuntu: Hold copy of language pack array per package * ubuntu: Don't load language packs multiple times, cleanup index data properly * Close package from a base suite immediately in the seeding step Bugfixes: * debian: Make package index threadsafe again * Don't add data to contents store in parallel * Don't add the same architecture multiple times when generating reports * Enable static check for poor exception handling * Don't run through a synchronized section when fetching package filename * Update dscanner and invoke it from a more versatile Python script Version 0.7.2 ~~~~~~~~~~~~~~ Released: 2018-04-16 Features: * Dramatically reduce memory usage of cruft cleanup operation * Parallelize database cleanup a bit more * debian: Add basic in-memory deduplication of translated texts for packages Bugfixes: * Fix build on 32bit architectures * Make GStreamer entry entirely optional for packages * Ensure we properly merge in desktop-entry data from launchable tags * Never process desktop files twice * Don't append desktop data to mime/category lists * Update wrap files to work with gir-to-d >= 0.15 Version 0.7.1 ~~~~~~~~~~~~~~ Released: 2018-04-06 Features: * debian: Use TagFile class from Laniakea * debian: Use the packages real architecture in its identifier * debian: Implement packageForFile backend feature * Do more complex parsing of metainfo license expressions * ci: Enable more static analysis checks * Improve manual page Bugfixes: * Never upscale icons if we don't need to, use pixmaps directory as last resort * Update wrap files to work with gir-to-d >= 0.14 * Don't try to upscale tiny icons from the pixmaps directory * Register HiDPI icons for fonts properly * Fix some minor style issues found by D-Scanner * Document WorkspaceDir config file option Version 0.7.0 ~~~~~~~~~~~~~~ Released: 2018-04-04 Notes: * Processing single packages and calling publication and processing separately is not a fully supported feature yet! This release lays the groundwork for it, but its final implementation will happen later. Do not use this in production! Features: * stdx.allocator-backed containers library * Use stdx.allocator-backed HashMap in more places * Trigger GC less aggressively, use smaller parallel work unit sizes * Allow setting a workspace in config and calling asgen on a config file * Add backend interface for processing a single package * Allow calling only the metadata publication step * Add framework for extracting data directly from package files * Allow setting icon policy in configuration * Add support for more icon types and customizable icon policy * Allow icon upscaling in certain cases, but complain about it * Modernize Meson definitions * Document how to modify icon policies in configuration * Use AppStream 0.12.0 and format 0.12 by default Bugfixes: * Update wrap files to be compatible with gir-to-d >= 0.13 * Improve ArchiveDecompressor usage * For each array appender, try to guess capacity to reserve better * Properly add remote icon URLs * Fetch JavaScript bits with Yarn instead of Bower * Ensure we can run asgen again from its build directory * Ignore data from immutable suites on cleanup * Don't parallelize cleanup data collection routine * Release memory chunks faster during cleanup operations Version 0.6.8 ~~~~~~~~~~~~~~ Released: 2017-11-06 Notes: * The -Ddownload_js build flag is now -Ddownload-js to follow the naming of other Meson flags more closely. Features: * Encode AppStream library version in version info string on reports * Don't require an install candidate for a webapp * Update hicolor theme fallback definition * Try to filter out symbolic icons for apps * Allow processing only one section in a suite * Relax icon scaling rules Bugfixes: * Move time graph legend to the top left * ci: Use the packaged gir-to-d * Drop an unused gdc conditional * Ensure the validator never tests web URLs for validity * Drop LLVM DC bug workaround and allow cross-module inlining * Make D GIR interface build work again with recent Meson versions * Don't fail if we have a dupe ID with no pkgname, can happen now with webapps Version 0.6.7 ~~~~~~~~~~~~~~ Released: 2017-10-02 Features: * ci: Enable gdc again for testing (Matthias Klumpp) * Make rpmmd backend work (Matthias Klumpp) * Make an empty main function for embedded unittests (Matthias Klumpp) * Add a metainfo file (Matthias Klumpp) * Add a manual page (Matthias Klumpp) * Sort suite names on the index page (Matthias Klumpp) Bugfixes: * use file.exists to check file existence (Antonio Rojas) * arch: Don't ref generator results (Matthias Klumpp) * Improve the icon tarball generation code (Matthias Klumpp) * Resolve deprecation warning (Matthias Klumpp) Version 0.6.6 ~~~~~~~~~~~~~~ Released: 2017-09-22 Features: * Add basic HiDPI support (Corentin Noël) * Generate the HiDPI tarball (Corentin Noël) * Add an 'info' command to dump package information (Matthias Klumpp) * Handle the "Hidden" property in .desktop files and complain about it (Matthias Klumpp) Bugfixes: * arch: XML-escape package descriptions (Matthias Klumpp) * Check for availability of a component-id at the right time (Matthias Klumpp) * Make writing compressed results a bit more robust (Matthias Klumpp) * Resolve all deprecation warnings and slightly improve code (Matthias Klumpp) Version 0.6.5 ~~~~~~~~~~~~~~ Released: 2017-07-02 Features: * Generate type=codec metadata for gstreamer packages (Iain Lane) * Add a feature flag for GStreamer processing (Iain Lane) * ci: Switch to using upstream GirToD (Matthias Klumpp) * Set asgen version in defines.d by Meson as well (Matthias Klumpp) * Format graphs a bit nicer (Matthias Klumpp) Bugfixes: * Find files in DATADIR - not relative to the executable (Iain Lane) * Fix typo in metadata-path hint (Matthias Klumpp) * Try to be more informative when a component has no ID (Matthias Klumpp) * Ensure the rDNS scheme is really followed before editing an ID (Matthias Klumpp) Version 0.6.4 ~~~~~~~~~~~~~~ Released: 2017-05-25 Features: * Automatically generate GIR D bindings at build-time (Matthias Klumpp) * Remove dub build file (Matthias Klumpp) * Use Meson subproject/wrap to fetch mustache-d in case it is missing (Matthias Klumpp) * Add a launchable tag to output if we can add one (Matthias Klumpp) * Make legacy metainfo dir a warning (Matthias Klumpp) * Validate launchable entries and add heuristics in case they aren't present (Matthias Klumpp) Bugfixes: * Import buildPath directly from std.path (Antonio Rojas) * Remove spurious std.stream import (Matthias Klumpp) Version 0.6.3 ~~~~~~~~~~~~~~ Released: 2017-03-08 Features: * Experiment with scoped classes (Matthias Klumpp) * Drop embedded generator copy (Matthias Klumpp) * Use final classes whenever possible (Matthias Klumpp) * Centralize all export-dir locations in base config class (Matthias Klumpp) * Allow to manually configure data export locations (Matthias Klumpp) Bugfixes: * Fix some quirks and make the tests work again (Matthias Klumpp) * ci: Don't build with GDC (Matthias Klumpp) * Don't fail if a suite has no base suite (Matthias Klumpp) * engine: Process base suite packages for contents only (Iain Lane) * Update README.md (#39) (Blake Kostner) * debian: Only select the most recent packages for a scan (Matthias Klumpp) * Streamline final component validity check, resolve a crash (Matthias Klumpp) Version 0.6.2 ~~~~~~~~~~~~~~ Released: 2017-01-24 Notes: * GDC 6.x can't compile the project at time due to a GDC bug. See https://bugzilla.gdcproject.org/show_bug.cgi?id=251 for more information. Features: * Support desktop-app metainfo files without .desktop file if they have an icon set * Write log entry when starting/finishing icon tarball write * Update AppStream bindings * Allow to specify allowed keys * Don't show pedantic validator hints * Allow metainfo file to specify sample texts for fonts * Process XPM icons if they are large enough Bugfixes: * Safeguard against TLD checks with empty-or-null string * Ensure desktop-apps have at least one valid category set * Don't override metainfo name/summary with .desktop values * Quit immediately if the component type is unknown * Correctly read a font's full-name * Don't needlessly reprocess fonts Version 0.6.1 ~~~~~~~~~~~~~~ Released: 2016-12-26 Features: * Add a symlink so that Ubuntu uses the same template as Debian (Iain Lane) * Support linking to "old suites" in the HTML index (Iain Lane) * hints: icon-not-found: Explain the symlink problem for Debian & Ubuntu (Iain Lane) * Add preliminary new asgen logo (Matthias Klumpp) * Use struct for archive (Matthias Klumpp) * Reuse AsMetadata while processing metainfo files (Matthias Klumpp) Bugfixes: * Correctly install templates with Meson (Matthias Klumpp) * meson: Make finding mustache-d more robust (Matthias Klumpp) * Fix build with DMD (Antonio Rojas) * engine: Don't clean packages in base suites (Iain Lane) * download: If we have a last-modified date, set the mtime of the target file (Iain Lane) * Simplify zarchive code a little (Matthias Klumpp) * Use module initializer to initialize global static data (Matthias Klumpp) * Fix build on Debian with GDC (Matthias Klumpp) Version 0.6.0 ~~~~~~~~~~~~~~ Released: 2016-10-03 Notes: * If possible, you should build Meson with the LLVM D compiler and the Meson build system now. Features: * Produce better error messages on failed libarchive actions (Matthias Klumpp) * Add a hook to the desktop file parser to run backend specific code (Iain Lane) * Add an Ubuntu backend to retrieve langpack translations (Iain Lane) * Open databases with NOTLS (Matthias Klumpp) * Tweak the issue messages a bit (Matthias Klumpp) * Complain about stuff in legacy paths (Matthias Klumpp) * Only search for .desktop files if we have a DESKTOP_APP component (Matthias Klumpp) * Create new Font class to read font metadata (Matthias Klumpp) * Render an icon for fonts (Matthias Klumpp) * Add dependencies on Pango, FreeType and Fontconfig (Matthias Klumpp) * Determine languages a font supports (Matthias Klumpp) * Reorganize code for proper namespacing (Matthias Klumpp) * Render font screenshots (Matthias Klumpp) * Map font full names to files in a spec-compliant way (Matthias Klumpp) * Enable fonts support by default (Matthias Klumpp) * Share ContentsStore between threads again (Matthias Klumpp) * Make Meson a first-class buildsystem for asgen (Matthias Klumpp) * Update README (Matthias Klumpp) Bugfixes: * Fix build on non-64-bit architectures (Matthias Klumpp) * Work around LDC bug (Matthias Klumpp) * fclose() the memstream when we are done with it (Iain Lane) * debpkgindex: Be less noisy about translations in debug mode (Iain Lane) * Use getTestSamplesDir (Iain Lane) * Retry on all curl errors, not just timeouts (Iain Lane) * Synchronise downloading of files (Iain Lane) * ubuntu: Don't reference all packages, just the ones we need (langpacks) (Iain Lane) * Use an ugly mutex to work around Fontconfig issues (Matthias Klumpp) * Make FC mutex a bit more fine-grained (Matthias Klumpp) Version 0.5.0 ~~~~~~~~~~~~~~ Released: 2016-08-30 Features: * Add more speed and style optimizations, make Package an abstract class (Matthias Klumpp) * Use a string appender instead of a string array for writing output (Matthias Klumpp) * debian: Support downloading files from a mirror, instead of having them local (Iain Lane) * debian: Support DDTP translations (Iain Lane) * Ignore some more useless categories (Matthias Klumpp) * Add skeleton for RPMMD (Matthias Klumpp) * Add a few optimizations using immutable and appender (Matthias Klumpp) * Do not spawn compressors anymore, use libarchive directly (Matthias Klumpp) * Make compiling with LDC easier (Matthias Klumpp) * Add example for cruft collection script (Matthias Klumpp) * Allow cleaning up statistical data retroactively (Matthias Klumpp) * Rehash hash tables that we query often (Matthias Klumpp) * Always optimize debug builds (Matthias Klumpp) * Make a splitbuild with Meson and Ninja possible (Matthias Klumpp) * Use AppStream's knowledge about TLDs to build smarter global-component-ids (Matthias Klumpp) * Allow specifying the format version metadata should be built for (Matthias Klumpp) * Adjust for AppStream 0.10 release (Matthias Klumpp) * Perform legal checks before allowing metadata to be added to the pool (Matthias Klumpp) Bugfixes: * Fix dataUseJSTime in section overview template (Blake Kostner) * debian: Only retrieve DDTP translations from the section we're looking at (Iain Lane) * Fix deprecation warnings from LDC (Matthias Klumpp) * Work around some weird JSON parser quirk (int incompatible with uint) (Matthias Klumpp) * Prevent creating excess statistic entries (Matthias Klumpp) * Fix potential crash (Matthias Klumpp) * Use native generator when not compiling with GDC (Matthias Klumpp) * Work correctly if CIDs omit the .desktop suffix (Matthias Klumpp) * Only add valid category names to output (Matthias Klumpp) * Don't emit description-from-package multiple times per package. (Matthias Klumpp) Version 0.4.0 ~~~~~~~~~~~~~~ Released: 2016-07-12 Features: * README: Add link to D tour * Run optipng with standard settings * Add command to forget extracted data for a package * Implement immutable suites * Use appender more often and apply some style fixes * Build with parallel by default when using Makefile * Drop some deprecated stuff Bugfixes: * Display an error when processing a suite without section or arch * debian: Fix exception message if directory does not exist * Code improvements: More pure, more safe, more trusted * Drop global data validation result box * Don't crash if media pool directory does not exist and immutable suites are used Version 0.3.0 ~~~~~~~~~~~~~~ Released: 2016-05-24 Features: * Register new backend for Arch Linux (Matthias Klumpp) * arch: Add reader for lists index file and skeleton for PackageIndex (Matthias Klumpp) * Make generic archive class work well with non-Debian packages (Matthias Klumpp) * Add HTML anchors for maintainers on report pages (Matthias Klumpp) * html: Show suite name in section overview (Matthias Klumpp) * Allow not setting a MediaBaseUrl (Matthias Klumpp) * Add some font rendering experiments (Matthias Klumpp) * Allow storing multiple statitics entries per point in time (Matthias Klumpp) * Make icon tarball build reproducible (Matthias Klumpp) * Make ArchiveDecompressor API more powerful and always return const(ubyte)[] (Matthias Klumpp) * arch: Speed up backend by loading all data in one go (Matthias Klumpp) * Split "handleScreenshots" into more useful, fine-grained flags (Matthias Klumpp) * Add timestamp to output and only touch it if something has changed (Matthias Klumpp) * Only update metadata if the indices have been changed (Matthias Klumpp) * Store media in pool subdirectory (Matthias Klumpp) * Add flag to enforce metadata processing, even if nothing changed (Matthias Klumpp) * Allow disabling metadata timestamps (Matthias Klumpp) * Improve scan-skipping code (Matthias Klumpp) Bugfixes: * Fix build on non-64bit arches (Matthias Klumpp) * Handle compressed empty files correctly (Neil Mayhew) * Drop other, non-UTF-8 encodings from language codes too (Matthias Klumpp) * Drop non-printable characters from .desktop file values (Matthias Klumpp) * Loop over more things by-reference to reduce RAM usage (Matthias Klumpp) * debian: Read Packages.xz files if no .gz file is available (Matthias Klumpp) * Don't lie about thumbnail sizes in their filename (Matthias Klumpp) * Don't duplicate architecture tag if there are multiple issues (Matthias Klumpp) * Catch data serialization errors (Matthias Klumpp) * Run bower with allow root to be able to make js as root (Harald Sitter) Version 0.2.0 ~~~~~~~~~~~~~~ Released: 2016-04-24 Features: * Don't ship minified JS * Get rid of (almost) all the embedded JS copies, use Bower * Add makefile for convenience * Use Flot for drawing graphs * Update README * Document all asgen-config settings * Reserve subdb in contents database to cache icon data * Make IconHandler use a pre-filtered list of icons * Allow templates to override only parts of the default branding * Add some default branding for Debian * Show logo on generated HTML pages * debian: Link some interesting resources from the main page Bugfixes: * Find aliased icons correctly * Keep priority sorting of themes * Make the hicolor theme always-available, using an embedded index copy if necessary * Fix counting of issues * Do not accidentally upscale screenshots while creating thumbnails * debian: Don't make a missing package index fatal * Don't fail if we are trying to add statistics too quickly * Rename remove-valid to reflect what it actually does * Ignore errors if we are trying to decompress a 0-byte gzip file * Suite arguments aren't optional for process/remove-found * Demote screenshot-no-thumbnails to info Version 0.1.0 ~~~~~~~~~~~~~~ Released: 2016-04-18 Notes: * Initial release appstream-generator-0.8.6/README.md000066400000000000000000000056011416066706400170000ustar00rootroot00000000000000# AppStream Generator AppStream is an effort to provide additional metadata and unique IDs for all software available in a Linux system. This repository contains the server-side of the AppStream infrastructure, a tool to generate metadata from distribution packages. You can find out more about AppStream collection metadata at [Freedesktop](https://www.freedesktop.org/software/appstream/docs/chap-CollectionData.html). The AppStream generator is currently primarily used by Debian, but is written in a distribution agnostic way. Backends only need to implement [two interfaces](src/asgen/backends/interfaces.d) to to be ready. If you are looking for the AppStream client-tools, the [AppStream repository](https://github.com/ximion/appstream) is where you want to go. ![AppStream Generator Logo](data/templates/default/static/img/asgen.png "AppStream Generator") ## Development ![Build Test](https://github.com/ximion/appstream-generator/workflows/Build%20Test/badge.svg) ### Build dependencies * LDC[1] * Meson (>= 0.46) [2] * GLibD [3] * AppStream [4] * libarchive (>= 3.2) [5] * LMDB [6] * GirToD [7] * LibSoup * Cairo * GdkPixbuf 2.0 * RSvg 2.0 * FreeType * Fontconfig * Pango * Yarn (optional) [8] [1]: https://github.com/ldc-developers/ldc/releases [2]: http://mesonbuild.com/ [3]: https://github.com/gtkd-developers/GlibD [4]: https://github.com/ximion/appstream [5]: https://libarchive.org/ [6]: https://symas.com/lmdb/ [7]: https://github.com/gtkd-developers/gir-to-d [8]: https://yarnpkg.com/ On Debian and derivatives of it, all build requirements can be installed using the following command: ```ShellSession sudo apt install meson ldc gir-to-d libappstream-dev libsoup2.4-dev libarchive-dev \ libgdk-pixbuf2.0-dev librsvg2-dev libcairo2-dev libfreetype6-dev libfontconfig1-dev \ libpango1.0-dev liblmdb-dev libglibd-2.0-dev ``` ### Build instructions To build the tool with Meson, create a `build` subdirectory, change into it and run `meson .. && ninja` to build. In summary: ```ShellSession $ mkdir build && cd build $ meson -Ddownload-js=true .. $ ninja $ sudo ninja install ``` We support several options to be set to influence the build. Change into the build directory and run `mesonconf` to see them all. You might want to perform an optimized debug build by passing `--buildtype=debugoptimized` to `meson` or just do a release build straight away with `--buildtype=release` in case you want to use the resulting binaries productively. By default, the build happens without optimizations which slows down the generator. ## Usage Take a look at the `docs/` directory in the source tree for information on how to use the generator and write configuration files for it. ## Hacking Pull-requests and patches are very welcome! If you are new to D, it is highly recommended to take a few minutes to look at the D tour to get a feeling of what the language can do: https://tour.dlang.org/ appstream-generator-0.8.6/RELEASE000066400000000000000000000017611416066706400165270ustar00rootroot00000000000000AppStream Generator Release Notes 1. Write NEWS entries for AppStream Generator in the same format as usual. git shortlog v0.8.5.. | grep -i -v trivial | grep -v Merge > NEWS.new -------------------------------------------------------------------------------- Version 0.8.6 ~~~~~~~~~~~~~~ Released: 2021-xx-xx Notes: Features: Bugfixes: Contributors: -------------------------------------------------------------------------------- 2. Commit changes in Git: git commit -a -m "Release version 0.8.6" git tag -s -f -m "Release 0.8.6" v0.8.6 git push --tags git push 3. Do post release version bump in meson.build, RELEASE 4. Commit trivial changes: git commit -a -m "trivial: post release version bump" git push 5. Send an email to appstream@lists.freedesktop.org ================================================= AppStream Generator 0.8.6 released! Tarballs available here: https://github.com/ximion/appstream-generator/releases ================================================= appstream-generator-0.8.6/TODO000066400000000000000000000004371416066706400162130ustar00rootroot00000000000000= AppStream Generator TODO List = === Known issues === * No persistent problems at time :-) === Planned Features === * Use feature from the upcoming libappstream-compose === Whishlist / Random Ideas === * Add an icon-cache so we don't render SVG icons in themes multiple times. appstream-generator-0.8.6/contrib/000077500000000000000000000000001416066706400171575ustar00rootroot00000000000000appstream-generator-0.8.6/contrib/cleanup-cruft.sh.example000066400000000000000000000013511416066706400237150ustar00rootroot00000000000000#!/bin/bash # # Script cleaning up the AppStream metadata pool and cache. # This script should be run by a cronjob (e.g. every week). # set -e set -o pipefail set -u WORKSPACE_DIR="/srv/appstream/workspace" # only run one instance of the script LOCKFILE="$WORKSPACE_DIR/.lock" cleanup() { rm -f "$LOCKFILE" } if ! lockfile -r8 $LOCKFILE; then echo "aborting AppStream metadata cleanup because $LOCKFILE has already been locked" exit 0 fi trap cleanup 0 # Start logging logdir="$WORKSPACE_DIR/logs/`date "+%Y/%m"`" mkdir -p $logdir NOW=`date "+%d_%H%M"` LOGFILE="$logdir/${NOW}_cleanup.log" exec >> "$LOGFILE" 2>&1 cd $WORKSPACE_DIR # Cleanup superseded data appstream-generator cleanup # finish logging exec > /dev/null 2>&1 appstream-generator-0.8.6/contrib/girwrap/000077500000000000000000000000001416066706400206325ustar00rootroot00000000000000appstream-generator-0.8.6/contrib/girwrap/APILookup.txt000066400000000000000000000032511416066706400231770ustar00rootroot00000000000000# # Licensed under the GNU Lesser General Public License Version 3 # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the license, or # (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . # define the license to use license: start /* * Licensed under the GNU Lesser General Public License Version 3 * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ // generated automatically - do not change license: end includeComments: y srcDir: . lookup: APILookupGdkPixbuf.txt lookup: APILookupAppStream.txt lookup: APILookupAppStreamCompose.txt appstream-generator-0.8.6/contrib/girwrap/APILookupAppStream.txt000066400000000000000000000026041416066706400250150ustar00rootroot00000000000000# # Licensed under the GNU Lesser General Public License Version 3 # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the license, or # (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . # must start with wrap wrap: appstream file: AppStream-1.0.gir addAliases: start public import glib.c.types; public import gobject.c.types; public import gio.c.types; public import core.stdc.stdio; public import core.stdc.string; addAliases: end move: version_string Utils appstreamVersion move: issue_severity_to_string Utils severityToString move: issue_severity_from_string Utils severityFromString move: utils_is_tld Utils isTld move: utils_is_category_name Utils isCategoryName move: format_version_to_string Utils move: format_version_from_string Utils move: license_is_metadata_license Utils move: spdx_license_tokenize Utils move: component_kind_to_string Utils appstream-generator-0.8.6/contrib/girwrap/APILookupAppStreamCompose.txt000066400000000000000000000033771416066706400263530ustar00rootroot00000000000000# # Licensed under the GNU Lesser General Public License Version 3 # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the license, or # (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . # must start with wrap wrap: ascompose file: AppStreamCompose-1.0.gir addAliases: start public import glib.c.types; public import gobject.c.types; public import gio.c.types; public import appstream.c.types; public import gdkpixbuf.c.types; public import core.stdc.stdio; public import core.stdc.string; addAliases: end move: image_format_from_filename Utils struct: Globals move: globals_get_tmp_dir Globals getTmpDir move: globals_get_tmp_dir_create Globals getTmpDirCreate move: globals_set_tmp_dir Globals setTmpDir move: globals_get_use_optipng Globals useOptipng move: globals_set_use_optipng Globals setUseOptipng move: globals_get_optipng_binary Globals optipngBinary move: globals_set_optipng_binary Globals setOptipngBinary move: globals_add_hint_tag Globals addHintTag move: globals_get_hint_tags Globals getHintTags move: globals_hint_tag_severity Globals hintTagSeverity move: globals_hint_tag_explanation Globals hintTagExplanation struct: Result class: Result move: add_hint_v Result addHint move: add_hint_by_cid_v Result addHintByCid appstream-generator-0.8.6/contrib/girwrap/APILookupGdkPixbuf.txt000066400000000000000000000163741416066706400250150ustar00rootroot00000000000000# This file is part of gtkD. # # gtkD is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # # gtkD is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with gtkD; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################# ### Definitions for wrapping Gtk+ ########### ############################################# # must start with wrap wrap: gdkpixbuf file: GdkPixbuf-2.0.gir file: GdkPixdata-2.0.gir struct: Pixbuf array: new_from_inline data data_length noCode: save_to_bufferv noCode: ref noCode: unref noCode: new_from_resource noCode: new_from_resource_at_scale noCode: get_pixels code: start /** * Saves pixbuf to a new buffer in format @type, which is currently "jpeg", * "tiff", "png", "ico" or "bmp". See gdk_pixbuf_save_to_buffer() * for more details. * * Params: * buffer = location to receive a pointer to the new buffer. * bufferSize = location to receive the size of the new buffer. * type = name of file format. * optionKeys = name of options to set, %NULL-terminated * optionValues = values for named options * * Return: whether an error was set * * Since: 2.4 * * Throws: GException on failure. */ public bool saveToBuffer(out ubyte[] buffer, string type, string[] optionKeys, string[] optionValues) { char* outbuffer = null; size_t bufferSize; GError* err = null; auto p = gdk_pixbuf_save_to_bufferv(gdkPixbuf, &outbuffer, &bufferSize, Str.toStringz(type), Str.toStringzArray(optionKeys), Str.toStringzArray(optionValues), &err) != 0; if (err !is null) { throw new GException( new ErrorG(err) ); } buffer = (cast(ubyte*)outbuffer)[0 .. bufferSize]; return p; } /** * Creates a new pixbuf by loading an image from an resource. * * The file format is detected automatically. * * Params: * resourcePath = the path of the resource file * * Return: A newly-created pixbuf, or null if any of several error * conditions occurred: the file could not be opened, the image format is * not supported, there was not enough memory to allocate the image buffer, * the stream contained invalid data, or the operation was cancelled. * * Since: 2.26 * * Throws: GException on failure. */ public static Pixbuf newFromResource(string resourcePath) { GError* err = null; auto p = gdk_pixbuf_new_from_resource(Str.toStringz(resourcePath), &err); if (err !is null) { throw new GException( new ErrorG(err) ); } return new Pixbuf(cast(GdkPixbuf*) p, true); } /** * Creates a new pixbuf by loading an image from an resource. * * The file format is detected automatically. * * The image will be scaled to fit in the requested size, optionally * preserving the image's aspect ratio. When preserving the aspect ratio, * a width of -1 will cause the image to be scaled to the exact given * height, and a height of -1 will cause the image to be scaled to the * exact given width. When not preserving aspect ratio, a width or * height of -1 means to not scale the image at all in that dimension. * * The stream is not closed. * * Params: * resourcePath = the path of the resource file * width = The width the image should have or -1 to not constrain the width * height = The height the image should have or -1 to not constrain the height * preserveAspectRatio = true to preserve the image's aspect ratio * * Return: A newly-created pixbuf, or null if any of several error * conditions occurred: the file could not be opened, the image format is * not supported, there was not enough memory to allocate the image buffer, * the stream contained invalid data, or the operation was cancelled. * * Since: 2.26 * * Throws: GException on failure. */ public static Pixbuf newFromResource(string resourcePath, int width, int height, bool preserveAspectRatio) { GError* err = null; auto p = gdk_pixbuf_new_from_resource_at_scale(Str.toStringz(resourcePath), width, height, preserveAspectRatio, &err); if (err !is null) { throw new GException( new ErrorG(err) ); } return new Pixbuf(cast(GdkPixbuf*) p, true); } /** * Queries a pointer to the pixel data of a pixbuf. * * Return: A pointer to the pixbuf's pixel data. * Please see the section on [image data](image-data) for information * about how the pixel data is stored in memory. * * This function will cause an implicit copy of the pixbuf data if the * pixbuf was created from read-only data. */ public char* getPixels() { return gdk_pixbuf_get_pixels(gdkPixbuf); } code: end struct: PixbufAnimation noCode: new_from_resource struct: PixbufLoader import: glib.Str noCode: new_with_type noCode: new_with_mime_type code: start /** * Creates a new pixbuf loader object that always attempts to parse * image data as if it were an image of type @image_type, instead of * identifying the type automatically. Useful if you want an error if * the image isn't the expected type, for loading image formats * that can't be reliably identified by looking at the data, or if * the user manually forces a specific type. * * The list of supported image formats depends on what image loaders * are installed, but typically "png", "jpeg", "gif", "tiff" and * "xpm" are among the supported formats. To obtain the full list of * supported image formats, call gdk_pixbuf_format_get_name() on each * of the #GdkPixbufFormat structs returned by gdk_pixbuf_get_formats(). * * Params: * imageType = name of the image format to be loaded with the image * isMimeType = Set to true if type is a mime type * * Return: A newly-created pixbuf loader. * * Throws: GException on failure. */ public this(string type, bool isMimeType=false) { GError* err = null; GdkPixbufLoader* p; if ( isMimeType ) { p = cast(GdkPixbufLoader*)gdk_pixbuf_loader_new_with_mime_type(Str.toStringz(type), &err); } else { p = cast(GdkPixbufLoader*)gdk_pixbuf_loader_new_with_type(Str.toStringz(type), &err); } if (err !is null) { throw new GException( new ErrorG(err) ); } this(cast(GdkPixbufLoader*) p, true); } code: end struct: PixbufSimpleAnim class: PixbufSimpleAnimation struct: PixbufSimpleAnimIter class: PixbufSimpleAnimationIter noCode: true struct: Pixdata noProperty: pixel_data move: pixbuf_from_pixdata Pixbuf from_pixdata # Gir-to-d tries to handle the return value of 'gdk_pixdata_from_pixbuf' as if it is zero-terminated. # Which causes it to try to use 'getArrayLength' on a void*, causing a compilation error. # Temporary fix for now is to just not export that function. noCode: from_pixbuf # Add missing include for FILE* that GdkPixbufModule needs inside 'girepo/gdkpixbuf/c/types.d'. addAliases: start private import core.stdc.stdio : FILE; addAliases: end appstream-generator-0.8.6/contrib/setup/000077500000000000000000000000001416066706400203175ustar00rootroot00000000000000appstream-generator-0.8.6/contrib/setup/.gitignore000066400000000000000000000000301416066706400223000ustar00rootroot00000000000000node_modules/ yarn.lock appstream-generator-0.8.6/contrib/setup/build_js.sh000077500000000000000000000012571416066706400224560ustar00rootroot00000000000000#!/bin/sh set -e if [ -n "$MESON_SOURCE_ROOT" ]; then cd "$MESON_SOURCE_ROOT/contrib/setup/" fi YARNPKG="yarn" if [ ! -z "$1" ] then YARNPKG=$1 fi $YARNPKG install --no-bin-links --prod --no-lockfile --non-interactive JS_TARGET=../../data/templates/default/static/js [ ! -d "$JS_TARGET" ] && mkdir $JS_TARGET [ ! -d "$JS_TARGET/jquery" ] && mkdir $JS_TARGET/jquery install node_modules/jquery/dist/*.min.js -t $JS_TARGET/jquery [ ! -d "$JS_TARGET/flot" ] && mkdir $JS_TARGET/flot install node_modules/jquery-flot/jquery.flot*.js -t $JS_TARGET/flot [ ! -d "$JS_TARGET/highlight" ] && mkdir $JS_TARGET/highlight install node_modules/highlightjs/*.js -t $JS_TARGET/highlight appstream-generator-0.8.6/contrib/setup/meson-install-templates.sh000077500000000000000000000004101416066706400254320ustar00rootroot00000000000000#!/bin/sh set -e cd "$MESON_SOURCE_ROOT" echo "Installing templates..." install -d "${DESTDIR}/${MESON_INSTALL_PREFIX}/share/appstream/templates" cp -dpru --no-preserve=ownership data/templates/* -t "${DESTDIR}/${MESON_INSTALL_PREFIX}/share/appstream/templates" appstream-generator-0.8.6/contrib/setup/package.json000066400000000000000000000001761416066706400226110ustar00rootroot00000000000000{ "flat": true, "dependencies": { "highlightjs": "^9.10.0", "jquery": "^3.3.1", "jquery-flot": "^0.8.3" } } appstream-generator-0.8.6/contrib/update-metadata.sh.example000066400000000000000000000017151416066706400242110ustar00rootroot00000000000000#!/bin/bash # # Example script for updating AppStream metadata using appstream-generator. # This script can easily be run by a cronjob. # set -e set -o pipefail set -u SUITES="sid stretch" WORKSPACE_DIR="/srv/appstream/workspace" PUBLIC_DIR="/srv/appstream/public" # only run one instance of the script LOCKFILE="$WORKSPACE_DIR/.lock" cleanup() { rm -f "$LOCKFILE" } if ! lockfile -r8 $LOCKFILE; then echo "aborting AppStream metadata extraction because $LOCKFILE has already been locked" exit 0 fi trap cleanup 0 # Start logging logdir="$WORKSPACE_DIR/logs/`date "+%Y/%m"`" mkdir -p $logdir NOW=`date "+%d_%H%M"` LOGFILE="$logdir/${NOW}.log" exec >> "$LOGFILE" 2>&1 cd $WORKSPACE_DIR # generate fresh metadata for suite in $SUITES; do appstream-generator process $suite done # Sync updated data to public directory rsync -ak --delete-after --link-dest="$PUBLIC_DIR/" "$WORKSPACE_DIR/export/" "$PUBLIC_DIR/" # finish logging exec > /dev/null 2>&1 appstream-generator-0.8.6/data/000077500000000000000000000000001416066706400164305ustar00rootroot00000000000000appstream-generator-0.8.6/data/.gitignore000066400000000000000000000001551416066706400204210ustar00rootroot00000000000000templates/default/static/js/jquery/ templates/default/static/js/flot/ templates/default/static/js/highlight/ appstream-generator-0.8.6/data/asgen-hints.json000066400000000000000000000153561416066706400215550ustar00rootroot00000000000000{ "internal-error": { "text": [ "A fatal problem appeared in the generator.", "Please report a bug: {{msg}}"], "severity": "error" }, "internal-unknown-tag": { "text": ["The generator emitted a tag '{{tag}}' which is unknown. This is a bug in the metadata generator, please", "file a bugreport."], "severity": "warning" }, "icon-format-unsupported": { "text": "Icon file '{{icon_fname}}' uses an unsupported image file format.", "severity": "error" }, "icon-not-found": { "text": [ "The icon '{{icon_fname}}' was not found in the archive. This issue can have multiple reasons:", "
    ", "
  • The icon is not present in the archive.
  • ", "
  • The icon is in a wrong directory.
  • ", "
  • The icon is not available in a suitable size (at least 64x64px)
  • ", "
  • On Debian and Ubuntu, the icon is a symlink. The generator cannot read symlinks on these distributions - make the icon a real file.
  • ", "
", "To make the icon easier to find, place it in /usr/share/icons/hicolor/<size>/apps and ensure the Icon= value", "of the .desktop file is set correctly." ], "severity": "error" }, "icon-scaled-up": { "text": "Icon file '{{icon_name}}' was scaled up from {{icon_size}}px to {{scale_size}}px. Please try to supply a bigger icon.", "severity": "warning" }, "icon-too-small": { "text": [ "Only a very small icon ('{{icon_name}}', {{icon_size}}px) could be located.", "Please try to supply a bigger icon (at least 64x64px) in the Freedesktop icon path (/usr/share/icons/hicolor/%size%/apps/)." ], "severity": "error" }, "pkg-extract-error": { "text": "Could not extract file '{{fname}}' from package '{{pkg_fname}}'. Error: {{error}}", "severity": "error" }, "pkg-empty-file": { "text": ["Could not extract file '{{fname}}' from package '{{pkg_fname}}'.", "Icon data was empty. The icon might be a symbolic link pointing at a file outside of this package.", "Please do not do that and instead place the icons in their appropriate directories in /usr/share/icons/hicolor/."], "severity": "error" }, "image-write-error": { "text": ["Could write new image generated from '{{fname}}' (package '{{pkg_fname}}'): {{error}}"], "severity": "error" }, "metainfo-validation-error": { "text": "Validation of the MetaInfo file failed: {{msg}}", "severity": "warning" }, "no-install-candidate": { "text": "Component has no install candidate defined. A package must be associated with a package or bundle providing it.", "severity": "error" }, "metainfo-duplicate-id": { "text": ["The component-id '{{cid}}' already appeared in package '{{pkgname}}'. AppStream-IDs must be unique, please resolve which package will be", "providing this component by default.
", "This issue may happen temporarily when metadata is moved from one package to another. In that case, ignore this issue, it will vanish soon."], "severity": "error" }, "missing-desktop-file": { "text": ["Found an AppStream MetaInfo XML file, but the associated .desktop file is missing. This often happens when the .desktop file is renamed, but the", "<launchable type=\"desktop-id\"/> tag value of the AppStream MetaInfo file is not adapted as well, or if the MetaInfo file is located in a different package than the .desktop file.
", "Please fix the packaging or work with upstream to resolve this issue.
", "For older metadata, the desktop-id is inferred from the <id/> tag. If the component metadata has no launchable tag and no", "icon tag of type stock, check if a .desktop file named after the component-ID is located in the same package."], "severity": "error" }, "description-from-package": { "text": ["This software component gets its description from the package it is located in.
", "This has several disadvantages, like poor markup, too technical descriptions for users of software centers, different components having the same description, etc.
", "Please consider to either hide this .desktop file from AppStream by adding a X-AppStream-Ignore=true field to its .desktop file, or to write a MetaInfo file for ", "this component to take the long description upstream. In future, components without MetaInfo file might be dropped from the metadata entirely.", "You can consult the XML quickstart guides for more information on how to write a MetaInfo file."], "severity": "info" }, "no-metainfo": { "text": ["This software component is missing a MetaInfo file to provide metadata.
", "We currently took some data from its desktop-entry file and the long description of the package it is located in.
", "This has several disadvantages, like poor markup, too technical descriptions for users of software centers, different components having the same description, etc.
", "Additionally, a lot of software with desktop-entry files should either not be installable and searchable via the software catalog (like desktop-specific settings applications) or be tagged accordingly via MetaInfo files.
", "Please consider to either hide this desktop-entry file from AppStream by adding a X-AppStream-Ignore=true field to it, or to write a MetaInfo file for this component and send it upstream.
", "Generating components from non-MetaInfo files is deprecated, if you do not add a MetaInfo file, ", "this software may vanish from the metadata catalog (and if it is a GUI application, no longer be visible in software centers) in a future distribution release.
", "You can consult the MetaInfo quickstart guides for more information on how to write a MetaInfo file, ", "or file a bug with the upstream author of this software component."], "severity": "warning" }, "metadata-serialization-failed": { "text": ["Could not create the final metadata. This could have many causes, including a generator bug, but the likeliest cause is an error in the input metadata.
", "The error message was: {{msg}}"], "severity": "error" }, } appstream-generator-0.8.6/data/asgen-logo.svg000066400000000000000000000556251416066706400212210ustar00rootroot00000000000000 image/svg+xml appstream-generator-0.8.6/data/hicolor-theme-index.theme000066400000000000000000001543231416066706400233300ustar00rootroot00000000000000[Icon Theme] Name=Hicolor Comment=Fallback icon theme Hidden=true Directories=16x16/actions,16x16@2/actions,16x16/animations,16x16@2/animations,16x16/apps,16x16@2/apps,16x16/categories,16x16@2/categories,16x16/devices,16x16@2/devices,16x16/emblems,16x16@2/emblems,16x16/emotes,16x16@2/emotes,16x16/filesystems,16x16@2/filesystems,16x16/intl,16x16@2/intl,16x16/mimetypes,16x16@2/mimetypes,16x16/places,16x16@2/places,16x16/status,16x16@2/status,16x16/stock/chart,16x16@2/stock/chart,16x16/stock/code,16x16@2/stock/code,16x16/stock/data,16x16@2/stock/data,16x16/stock/form,16x16@2/stock/form,16x16/stock/image,16x16@2/stock/image,16x16/stock/io,16x16@2/stock/io,16x16/stock/media,16x16@2/stock/media,16x16/stock/navigation,16x16@2/stock/navigation,16x16/stock/net,16x16@2/stock/net,16x16/stock/object,16x16@2/stock/object,16x16/stock/table,16x16@2/stock/table,16x16/stock/text,16x16@2/stock/text,22x22/actions,22x22@2/actions,22x22/animations,22x22@2/animations,22x22/apps,22x22@2/apps,22x22/categories,22x22@2/categories,22x22/devices,22x22@2/devices,22x22/emblems,22x22@2/emblems,22x22/emotes,22x22@2/emotes,22x22/filesystems,22x22@2/filesystems,22x22/intl,22x22@2/intl,22x22/mimetypes,22x22@2/mimetypes,22x22/places,22x22@2/places,22x22/status,22x22@2/status,22x22/stock/chart,22x22@2/stock/chart,22x22/stock/code,22x22@2/stock/code,22x22/stock/data,22x22@2/stock/data,22x22/stock/form,22x22@2/stock/form,22x22/stock/image,22x22@2/stock/image,22x22/stock/io,22x22@2/stock/io,22x22/stock/media,22x22@2/stock/media,22x22/stock/navigation,22x22@2/stock/navigation,22x22/stock/net,22x22@2/stock/net,22x22/stock/object,22x22@2/stock/object,22x22/stock/table,22x22@2/stock/table,22x22/stock/text,22x22@2/stock/text,24x24/actions,24x24@2/actions,24x24/animations,24x24@2/animations,24x24/apps,24x24@2/apps,24x24/categories,24x24@2/categories,24x24/devices,24x24@2/devices,24x24/emblems,24x24@2/emblems,24x24/emotes,24x24@2/emotes,24x24/filesystems,24x24@2/filesystems,24x24/intl,24x24@2/intl,24x24/mimetypes,24x24@2/mimetypes,24x24/places,24x24@2/places,24x24/status,24x24@2/status,24x24/stock/chart,24x24@2/stock/chart,24x24/stock/code,24x24@2/stock/code,24x24/stock/data,24x24@2/stock/data,24x24/stock/form,24x24@2/stock/form,24x24/stock/image,24x24@2/stock/image,24x24/stock/io,24x24@2/stock/io,24x24/stock/media,24x24@2/stock/media,24x24/stock/navigation,24x24@2/stock/navigation,24x24/stock/net,24x24@2/stock/net,24x24/stock/object,24x24@2/stock/object,24x24/stock/table,24x24@2/stock/table,24x24/stock/text,24x24@2/stock/text,32x32/actions,32x32@2/actions,32x32/animations,32x32@2/animations,32x32/apps,32x32@2/apps,32x32/categories,32x32@2/categories,32x32/devices,32x32@2/devices,32x32/emblems,32x32@2/emblems,32x32/emotes,32x32@2/emotes,32x32/filesystems,32x32@2/filesystems,32x32/intl,32x32@2/intl,32x32/mimetypes,32x32@2/mimetypes,32x32/places,32x32@2/places,32x32/status,32x32@2/status,32x32/stock/chart,32x32@2/stock/chart,32x32/stock/code,32x32@2/stock/code,32x32/stock/data,32x32@2/stock/data,32x32/stock/form,32x32@2/stock/form,32x32/stock/image,32x32@2/stock/image,32x32/stock/io,32x32@2/stock/io,32x32/stock/media,32x32@2/stock/media,32x32/stock/navigation,32x32@2/stock/navigation,32x32/stock/net,32x32@2/stock/net,32x32/stock/object,32x32@2/stock/object,32x32/stock/table,32x32@2/stock/table,32x32/stock/text,32x32@2/stock/text,36x36/actions,36x36@2/actions,36x36/animations,36x36@2/animations,36x36/apps,36x36@2/apps,36x36/categories,36x36@2/categories,36x36/devices,36x36@2/devices,36x36/emblems,36x36@2/emblems,36x36/emotes,36x36@2/emotes,36x36/filesystems,36x36@2/filesystems,36x36/intl,36x36@2/intl,36x36/mimetypes,36x36@2/mimetypes,36x36/places,36x36@2/places,36x36/status,36x36@2/status,36x36/stock/chart,36x36@2/stock/chart,36x36/stock/code,36x36@2/stock/code,36x36/stock/data,36x36@2/stock/data,36x36/stock/form,36x36@2/stock/form,36x36/stock/image,36x36@2/stock/image,36x36/stock/io,36x36@2/stock/io,36x36/stock/media,36x36@2/stock/media,36x36/stock/navigation,36x36@2/stock/navigation,36x36/stock/net,36x36@2/stock/net,36x36/stock/object,36x36@2/stock/object,36x36/stock/table,36x36@2/stock/table,36x36/stock/text,36x36@2/stock/text,48x48/actions,48x48@2/actions,48x48/animations,48x48@2/animations,48x48/apps,48x48@2/apps,48x48/categories,48x48@2/categories,48x48/devices,48x48@2/devices,48x48/emblems,48x48@2/emblems,48x48/emotes,48x48@2/emotes,48x48/filesystems,48x48@2/filesystems,48x48/intl,48x48@2/intl,48x48/mimetypes,48x48@2/mimetypes,48x48/places,48x48@2/places,48x48/status,48x48@2/status,48x48/stock/chart,48x48@2/stock/chart,48x48/stock/code,48x48@2/stock/code,48x48/stock/data,48x48@2/stock/data,48x48/stock/form,48x48@2/stock/form,48x48/stock/image,48x48@2/stock/image,48x48/stock/io,48x48@2/stock/io,48x48/stock/media,48x48@2/stock/media,48x48/stock/navigation,48x48@2/stock/navigation,48x48/stock/net,48x48@2/stock/net,48x48/stock/object,48x48@2/stock/object,48x48/stock/table,48x48@2/stock/table,48x48/stock/text,48x48@2/stock/text,64x64/actions,64x64@2/actions,64x64/animations,64x64@2/animations,64x64/apps,64x64@2/apps,64x64/categories,64x64@2/categories,64x64/devices,64x64@2/devices,64x64/emblems,64x64@2/emblems,64x64/emotes,64x64@2/emotes,64x64/filesystems,64x64@2/filesystems,64x64/intl,64x64@2/intl,64x64/mimetypes,64x64@2/mimetypes,64x64/places,64x64@2/places,64x64/status,64x64@2/status,64x64/stock/chart,64x64@2/stock/chart,64x64/stock/code,64x64@2/stock/code,64x64/stock/data,64x64@2/stock/data,64x64/stock/form,64x64@2/stock/form,64x64/stock/image,64x64@2/stock/image,64x64/stock/io,64x64@2/stock/io,64x64/stock/media,64x64@2/stock/media,64x64/stock/navigation,64x64@2/stock/navigation,64x64/stock/net,64x64@2/stock/net,64x64/stock/object,64x64@2/stock/object,64x64/stock/table,64x64@2/stock/table,64x64/stock/text,64x64@2/stock/text,72x72/actions,72x72@2/actions,72x72/animations,72x72@2/animations,72x72/apps,72x72@2/apps,72x72/categories,72x72@2/categories,72x72/devices,72x72@2/devices,72x72/emblems,72x72@2/emblems,72x72/emotes,72x72@2/emotes,72x72/filesystems,72x72@2/filesystems,72x72/intl,72x72@2/intl,72x72/mimetypes,72x72@2/mimetypes,72x72/places,72x72@2/places,72x72/status,72x72@2/status,72x72/stock/chart,72x72@2/stock/chart,72x72/stock/code,72x72@2/stock/code,72x72/stock/data,72x72@2/stock/data,72x72/stock/form,72x72@2/stock/form,72x72/stock/image,72x72@2/stock/image,72x72/stock/io,72x72@2/stock/io,72x72/stock/media,72x72@2/stock/media,72x72/stock/navigation,72x72@2/stock/navigation,72x72/stock/net,72x72@2/stock/net,72x72/stock/object,72x72@2/stock/object,72x72/stock/table,72x72@2/stock/table,72x72/stock/text,72x72@2/stock/text,96x96/actions,96x96@2/actions,96x96/animations,96x96@2/animations,96x96/apps,96x96@2/apps,96x96/categories,96x96@2/categories,96x96/devices,96x96@2/devices,96x96/emblems,96x96@2/emblems,96x96/emotes,96x96@2/emotes,96x96/filesystems,96x96@2/filesystems,96x96/intl,96x96@2/intl,96x96/mimetypes,96x96@2/mimetypes,96x96/places,96x96@2/places,96x96/status,96x96@2/status,96x96/stock/chart,96x96@2/stock/chart,96x96/stock/code,96x96@2/stock/code,96x96/stock/data,96x96@2/stock/data,96x96/stock/form,96x96@2/stock/form,96x96/stock/image,96x96@2/stock/image,96x96/stock/io,96x96@2/stock/io,96x96/stock/media,96x96@2/stock/media,96x96/stock/navigation,96x96@2/stock/navigation,96x96/stock/net,96x96@2/stock/net,96x96/stock/object,96x96@2/stock/object,96x96/stock/table,96x96@2/stock/table,96x96/stock/text,96x96@2/stock/text,128x128/actions,128x128@2/actions,128x128/animations,128x128@2/animations,128x128/apps,128x128@2/apps,128x128/categories,128x128@2/categories,128x128/devices,128x128@2/devices,128x128/emblems,128x128@2/emblems,128x128/emotes,128x128@2/emotes,128x128/filesystems,128x128@2/filesystems,128x128/intl,128x128@2/intl,128x128/mimetypes,128x128@2/mimetypes,128x128/places,128x128@2/places,128x128/status,128x128@2/status,128x128/stock/chart,128x128@2/stock/chart,128x128/stock/code,128x128@2/stock/code,128x128/stock/data,128x128@2/stock/data,128x128/stock/form,128x128@2/stock/form,128x128/stock/image,128x128@2/stock/image,128x128/stock/io,128x128@2/stock/io,128x128/stock/media,128x128@2/stock/media,128x128/stock/navigation,128x128@2/stock/navigation,128x128/stock/net,128x128@2/stock/net,128x128/stock/object,128x128@2/stock/object,128x128/stock/table,128x128@2/stock/table,128x128/stock/text,128x128@2/stock/text,192x192/actions,192x192@2/actions,192x192/animations,192x192@2/animations,192x192/apps,192x192@2/apps,192x192/categories,192x192@2/categories,192x192/devices,192x192@2/devices,192x192/emblems,192x192@2/emblems,192x192/emotes,192x192@2/emotes,192x192/filesystems,192x192@2/filesystems,192x192/intl,192x192@2/intl,192x192/mimetypes,192x192@2/mimetypes,192x192/places,192x192@2/places,192x192/status,192x192@2/status,192x192/stock/chart,192x192@2/stock/chart,192x192/stock/code,192x192@2/stock/code,192x192/stock/data,192x192@2/stock/data,192x192/stock/form,192x192@2/stock/form,192x192/stock/image,192x192@2/stock/image,192x192/stock/io,192x192@2/stock/io,192x192/stock/media,192x192@2/stock/media,192x192/stock/navigation,192x192@2/stock/navigation,192x192/stock/net,192x192@2/stock/net,192x192/stock/object,192x192@2/stock/object,192x192/stock/table,192x192@2/stock/table,192x192/stock/text,192x192@2/stock/text,256x256/actions,256x256@2/actions,256x256/animations,256x256@2/animations,256x256/apps,256x256@2/apps,256x256/categories,256x256@2/categories,256x256/devices,256x256@2/devices,256x256/emblems,256x256@2/emblems,256x256/emotes,256x256@2/emotes,256x256/filesystems,256x256@2/filesystems,256x256/intl,256x256@2/intl,256x256/mimetypes,256x256@2/mimetypes,256x256/places,256x256@2/places,256x256/status,256x256@2/status,256x256/stock/chart,256x256@2/stock/chart,256x256/stock/code,256x256@2/stock/code,256x256/stock/data,256x256@2/stock/data,256x256/stock/form,256x256@2/stock/form,256x256/stock/image,256x256@2/stock/image,256x256/stock/io,256x256@2/stock/io,256x256/stock/media,256x256@2/stock/media,256x256/stock/navigation,256x256@2/stock/navigation,256x256/stock/net,256x256@2/stock/net,256x256/stock/object,256x256@2/stock/object,256x256/stock/table,256x256@2/stock/table,256x256/stock/text,256x256@2/stock/text,512x512/actions,512x512@2/actions,512x512/animations,512x512@2/animations,512x512/apps,512x512@2/apps,512x512/categories,512x512@2/categories,512x512/devices,512x512@2/devices,512x512/emblems,512x512@2/emblems,512x512/emotes,512x512@2/emotes,512x512/filesystems,512x512@2/filesystems,512x512/intl,512x512@2/intl,512x512/mimetypes,512x512@2/mimetypes,512x512/places,512x512@2/places,512x512/status,512x512@2/status,512x512/stock/chart,512x512@2/stock/chart,512x512/stock/code,512x512@2/stock/code,512x512/stock/data,512x512@2/stock/data,512x512/stock/form,512x512@2/stock/form,512x512/stock/image,512x512@2/stock/image,512x512/stock/io,512x512@2/stock/io,512x512/stock/media,512x512@2/stock/media,512x512/stock/navigation,512x512@2/stock/navigation,512x512/stock/net,512x512@2/stock/net,512x512/stock/object,512x512@2/stock/object,512x512/stock/table,512x512@2/stock/table,512x512/stock/text,512x512@2/stock/text,scalable/actions,scalable/animations,scalable/apps,scalable/categories,scalable/devices,scalable/emblems,scalable/emotes,scalable/filesystems,scalable/intl,scalable/mimetypes,scalable/places,scalable/status,scalable/stock/chart,scalable/stock/code,scalable/stock/data,scalable/stock/form,scalable/stock/image,scalable/stock/io,scalable/stock/media,scalable/stock/navigation,scalable/stock/net,scalable/stock/object,scalable/stock/table,scalable/stock/text,symbolic/apps [16x16/actions] Size=16 Context=Actions Type=Threshold [16x16@2/actions] Size=16 Scale=2 Context=Actions Type=Threshold [16x16/animations] Size=16 Context=Animations Type=Threshold [16x16@2/animations] Size=16 Scale=2 Context=Animations Type=Threshold [16x16/apps] Size=16 Context=Applications Type=Threshold [16x16@2/apps] Size=16 Scale=2 Context=Applications Type=Threshold [16x16/categories] Size=16 Context=Categories Type=Threshold [16x16@2/categories] Size=16 Scale=2 Context=Categories Type=Threshold [16x16/devices] Size=16 Context=Devices Type=Threshold [16x16@2/devices] Size=16 Scale=2 Context=Devices Type=Threshold [16x16/emblems] Size=16 Context=Emblems Type=Threshold [16x16@2/emblems] Size=16 Scale=2 Context=Emblems Type=Threshold [16x16/emotes] Size=16 Context=Emotes Type=Threshold [16x16@2/emotes] Size=16 Scale=2 Context=Emotes Type=Threshold [16x16/filesystems] Size=16 Context=FileSystems Type=Threshold [16x16@2/filesystems] Size=16 Scale=2 Context=FileSystems Type=Threshold [16x16/intl] Size=16 Context=International Type=Threshold [16x16@2/intl] Size=16 Scale=2 Context=International Type=Threshold [16x16/mimetypes] Size=16 Context=MimeTypes Type=Threshold [16x16@2/mimetypes] Size=16 Scale=2 Context=MimeTypes Type=Threshold [16x16/places] Size=16 Context=Places Type=Threshold [16x16@2/places] Size=16 Scale=2 Context=Places Type=Threshold [16x16/status] Size=16 Context=Status Type=Threshold [16x16@2/status] Size=16 Scale=2 Context=Status Type=Threshold [16x16/stock/chart] Size=16 Context=Stock Type=Threshold [16x16@2/stock/chart] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/code] Size=16 Context=Stock Type=Threshold [16x16@2/stock/code] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/data] Size=16 Context=Stock Type=Threshold [16x16@2/stock/data] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/form] Size=16 Context=Stock Type=Threshold [16x16@2/stock/form] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/image] Size=16 Context=Stock Type=Threshold [16x16@2/stock/image] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/io] Size=16 Context=Stock Type=Threshold [16x16@2/stock/io] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/media] Size=16 Context=Stock Type=Threshold [16x16@2/stock/media] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/navigation] Size=16 Context=Stock Type=Threshold [16x16@2/stock/navigation] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/net] Size=16 Context=Stock Type=Threshold [16x16@2/stock/net] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/object] Size=16 Context=Stock Type=Threshold [16x16@2/stock/object] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/table] Size=16 Context=Stock Type=Threshold [16x16@2/stock/table] Size=16 Scale=2 Context=Stock Type=Threshold [16x16/stock/text] Size=16 Context=Stock Type=Threshold [16x16@2/stock/text] Size=16 Scale=2 Context=Stock Type=Threshold [22x22/actions] Size=22 Context=Actions Type=Threshold [22x22@2/actions] Size=22 Scale=2 Context=Actions Type=Threshold [22x22/animations] Size=22 Context=Animations Type=Threshold [22x22@2/animations] Size=22 Scale=2 Context=Animations Type=Threshold [22x22/apps] Size=22 Context=Applications Type=Threshold [22x22@2/apps] Size=22 Scale=2 Context=Applications Type=Threshold [22x22/categories] Size=22 Context=Categories Type=Threshold [22x22@2/categories] Size=22 Scale=2 Context=Categories Type=Threshold [22x22/devices] Size=22 Context=Devices Type=Threshold [22x22@2/devices] Size=22 Scale=2 Context=Devices Type=Threshold [22x22/emblems] Size=22 Context=Emblems Type=Threshold [22x22@2/emblems] Size=22 Scale=2 Context=Emblems Type=Threshold [22x22/emotes] Size=22 Context=Emotes Type=Threshold [22x22@2/emotes] Size=22 Scale=2 Context=Emotes Type=Threshold [22x22/filesystems] Size=22 Context=FileSystems Type=Threshold [22x22@2/filesystems] Size=22 Scale=2 Context=FileSystems Type=Threshold [22x22/intl] Size=22 Context=International Type=Threshold [22x22@2/intl] Size=22 Scale=2 Context=International Type=Threshold [22x22/mimetypes] Size=22 Context=MimeTypes Type=Threshold [22x22@2/mimetypes] Size=22 Scale=2 Context=MimeTypes Type=Threshold [22x22/places] Size=22 Context=Places Type=Threshold [22x22@2/places] Size=22 Scale=2 Context=Places Type=Threshold [22x22/status] Size=22 Context=Status Type=Threshold [22x22@2/status] Size=22 Scale=2 Context=Status Type=Threshold [22x22/stock/chart] Size=22 Context=Stock Type=Threshold [22x22@2/stock/chart] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/code] Size=22 Context=Stock Type=Threshold [22x22@2/stock/code] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/data] Size=22 Context=Stock Type=Threshold [22x22@2/stock/data] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/form] Size=22 Context=Stock Type=Threshold [22x22@2/stock/form] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/image] Size=22 Context=Stock Type=Threshold [22x22@2/stock/image] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/io] Size=22 Context=Stock Type=Threshold [22x22@2/stock/io] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/media] Size=22 Context=Stock Type=Threshold [22x22@2/stock/media] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/navigation] Size=22 Context=Stock Type=Threshold [22x22@2/stock/navigation] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/net] Size=22 Context=Stock Type=Threshold [22x22@2/stock/net] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/object] Size=22 Context=Stock Type=Threshold [22x22@2/stock/object] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/table] Size=22 Context=Stock Type=Threshold [22x22@2/stock/table] Size=22 Scale=2 Context=Stock Type=Threshold [22x22/stock/text] Size=22 Context=Stock Type=Threshold [22x22@2/stock/text] Size=22 Scale=2 Context=Stock Type=Threshold [24x24/actions] Size=24 Context=Actions Type=Threshold [24x24@2/actions] Size=24 Scale=2 Context=Actions Type=Threshold [24x24/animations] Size=24 Context=Animations Type=Threshold [24x24@2/animations] Size=24 Scale=2 Context=Animations Type=Threshold [24x24/apps] Size=24 Context=Applications Type=Threshold [24x24@2/apps] Size=24 Scale=2 Context=Applications Type=Threshold [24x24/categories] Size=24 Context=Categories Type=Threshold [24x24@2/categories] Size=24 Scale=2 Context=Categories Type=Threshold [24x24/devices] Size=24 Context=Devices Type=Threshold [24x24@2/devices] Size=24 Scale=2 Context=Devices Type=Threshold [24x24/emblems] Size=24 Context=Emblems Type=Threshold [24x24@2/emblems] Size=24 Scale=2 Context=Emblems Type=Threshold [24x24/emotes] Size=24 Context=Emotes Type=Threshold [24x24@2/emotes] Size=24 Scale=2 Context=Emotes Type=Threshold [24x24/filesystems] Size=24 Context=FileSystems Type=Threshold [24x24@2/filesystems] Size=24 Scale=2 Context=FileSystems Type=Threshold [24x24/intl] Size=24 Context=International Type=Threshold [24x24@2/intl] Size=24 Scale=2 Context=International Type=Threshold [24x24/mimetypes] Size=24 Context=MimeTypes Type=Threshold [24x24@2/mimetypes] Size=24 Scale=2 Context=MimeTypes Type=Threshold [24x24/places] Size=24 Context=Places Type=Threshold [24x24@2/places] Size=24 Scale=2 Context=Places Type=Threshold [24x24/status] Size=24 Context=Status Type=Threshold [24x24@2/status] Size=24 Scale=2 Context=Status Type=Threshold [24x24/stock/chart] Size=24 Context=Stock Type=Threshold [24x24@2/stock/chart] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/code] Size=24 Context=Stock Type=Threshold [24x24@2/stock/code] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/data] Size=24 Context=Stock Type=Threshold [24x24@2/stock/data] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/form] Size=24 Context=Stock Type=Threshold [24x24@2/stock/form] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/image] Size=24 Context=Stock Type=Threshold [24x24@2/stock/image] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/io] Size=24 Context=Stock Type=Threshold [24x24@2/stock/io] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/media] Size=24 Context=Stock Type=Threshold [24x24@2/stock/media] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/navigation] Size=24 Context=Stock Type=Threshold [24x24@2/stock/navigation] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/net] Size=24 Context=Stock Type=Threshold [24x24@2/stock/net] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/object] Size=24 Context=Stock Type=Threshold [24x24@2/stock/object] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/table] Size=24 Context=Stock Type=Threshold [24x24@2/stock/table] Size=24 Scale=2 Context=Stock Type=Threshold [24x24/stock/text] Size=24 Context=Stock Type=Threshold [24x24@2/stock/text] Size=24 Scale=2 Context=Stock Type=Threshold [32x32/actions] Size=32 Context=Actions Type=Threshold [32x32@2/actions] Size=32 Scale=2 Context=Actions Type=Threshold [32x32/animations] Size=32 Context=Animations Type=Threshold [32x32@2/animations] Size=32 Scale=2 Context=Animations Type=Threshold [32x32/apps] Size=32 Context=Applications Type=Threshold [32x32@2/apps] Size=32 Scale=2 Context=Applications Type=Threshold [32x32/categories] Size=32 Context=Categories Type=Threshold [32x32@2/categories] Size=32 Scale=2 Context=Categories Type=Threshold [32x32/devices] Size=32 Context=Devices Type=Threshold [32x32@2/devices] Size=32 Scale=2 Context=Devices Type=Threshold [32x32/emblems] Size=32 Context=Emblems Type=Threshold [32x32@2/emblems] Size=32 Scale=2 Context=Emblems Type=Threshold [32x32/emotes] Size=32 Context=Emotes Type=Threshold [32x32@2/emotes] Size=32 Scale=2 Context=Emotes Type=Threshold [32x32/filesystems] Size=32 Context=FileSystems Type=Threshold [32x32@2/filesystems] Size=32 Scale=2 Context=FileSystems Type=Threshold [32x32/intl] Size=32 Context=International Type=Threshold [32x32@2/intl] Size=32 Scale=2 Context=International Type=Threshold [32x32/mimetypes] Size=32 Context=MimeTypes Type=Threshold [32x32@2/mimetypes] Size=32 Scale=2 Context=MimeTypes Type=Threshold [32x32/places] Size=32 Context=Places Type=Threshold [32x32@2/places] Size=32 Scale=2 Context=Places Type=Threshold [32x32/status] Size=32 Context=Status Type=Threshold [32x32@2/status] Size=32 Scale=2 Context=Status Type=Threshold [32x32/stock/chart] Size=32 Context=Stock Type=Threshold [32x32@2/stock/chart] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/code] Size=32 Context=Stock Type=Threshold [32x32@2/stock/code] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/data] Size=32 Context=Stock Type=Threshold [32x32@2/stock/data] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/form] Size=32 Context=Stock Type=Threshold [32x32@2/stock/form] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/image] Size=32 Context=Stock Type=Threshold [32x32@2/stock/image] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/io] Size=32 Context=Stock Type=Threshold [32x32@2/stock/io] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/media] Size=32 Context=Stock Type=Threshold [32x32@2/stock/media] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/navigation] Size=32 Context=Stock Type=Threshold [32x32@2/stock/navigation] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/net] Size=32 Context=Stock Type=Threshold [32x32@2/stock/net] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/object] Size=32 Context=Stock Type=Threshold [32x32@2/stock/object] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/table] Size=32 Context=Stock Type=Threshold [32x32@2/stock/table] Size=32 Scale=2 Context=Stock Type=Threshold [32x32/stock/text] Size=32 Context=Stock Type=Threshold [32x32@2/stock/text] Size=32 Scale=2 Context=Stock Type=Threshold [36x36/actions] Size=36 Context=Actions Type=Threshold [36x36@2/actions] Size=36 Scale=2 Context=Actions Type=Threshold [36x36/animations] Size=36 Context=Animations Type=Threshold [36x36@2/animations] Size=36 Scale=2 Context=Animations Type=Threshold [36x36/apps] Size=36 Context=Applications Type=Threshold [36x36@2/apps] Size=36 Scale=2 Context=Applications Type=Threshold [36x36/categories] Size=36 Context=Categories Type=Threshold [36x36@2/categories] Size=36 Scale=2 Context=Categories Type=Threshold [36x36/devices] Size=36 Context=Devices Type=Threshold [36x36@2/devices] Size=36 Scale=2 Context=Devices Type=Threshold [36x36/emblems] Size=36 Context=Emblems Type=Threshold [36x36@2/emblems] Size=36 Scale=2 Context=Emblems Type=Threshold [36x36/emotes] Size=36 Context=Emotes Type=Threshold [36x36@2/emotes] Size=36 Scale=2 Context=Emotes Type=Threshold [36x36/filesystems] Size=36 Context=FileSystems Type=Threshold [36x36@2/filesystems] Size=36 Scale=2 Context=FileSystems Type=Threshold [36x36/intl] Size=36 Context=International Type=Threshold [36x36@2/intl] Size=36 Scale=2 Context=International Type=Threshold [36x36/mimetypes] Size=36 Context=MimeTypes Type=Threshold [36x36@2/mimetypes] Size=36 Scale=2 Context=MimeTypes Type=Threshold [36x36/places] Size=36 Context=Places Type=Threshold [36x36@2/places] Size=36 Scale=2 Context=Places Type=Threshold [36x36/status] Size=36 Context=Status Type=Threshold [36x36@2/status] Size=36 Scale=2 Context=Status Type=Threshold [36x36/stock/chart] Size=36 Context=Stock Type=Threshold [36x36@2/stock/chart] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/code] Size=36 Context=Stock Type=Threshold [36x36@2/stock/code] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/data] Size=36 Context=Stock Type=Threshold [36x36@2/stock/data] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/form] Size=36 Context=Stock Type=Threshold [36x36@2/stock/form] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/image] Size=36 Context=Stock Type=Threshold [36x36@2/stock/image] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/io] Size=36 Context=Stock Type=Threshold [36x36@2/stock/io] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/media] Size=36 Context=Stock Type=Threshold [36x36@2/stock/media] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/navigation] Size=36 Context=Stock Type=Threshold [36x36@2/stock/navigation] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/net] Size=36 Context=Stock Type=Threshold [36x36@2/stock/net] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/object] Size=36 Context=Stock Type=Threshold [36x36@2/stock/object] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/table] Size=36 Context=Stock Type=Threshold [36x36@2/stock/table] Size=36 Scale=2 Context=Stock Type=Threshold [36x36/stock/text] Size=36 Context=Stock Type=Threshold [36x36@2/stock/text] Size=36 Scale=2 Context=Stock Type=Threshold [48x48/actions] Size=48 Context=Actions Type=Threshold [48x48@2/actions] Size=48 Scale=2 Context=Actions Type=Threshold [48x48/animations] Size=48 Context=Animations Type=Threshold [48x48@2/animations] Size=48 Scale=2 Context=Animations Type=Threshold [48x48/apps] Size=48 Context=Applications Type=Threshold [48x48@2/apps] Size=48 Scale=2 Context=Applications Type=Threshold [48x48/categories] Size=48 Context=Categories Type=Threshold [48x48@2/categories] Size=48 Scale=2 Context=Categories Type=Threshold [48x48/devices] Size=48 Context=Devices Type=Threshold [48x48@2/devices] Size=48 Scale=2 Context=Devices Type=Threshold [48x48/emblems] Size=48 Context=Emblems Type=Threshold [48x48@2/emblems] Size=48 Scale=2 Context=Emblems Type=Threshold [48x48/emotes] Size=48 Context=Emotes Type=Threshold [48x48@2/emotes] Size=48 Scale=2 Context=Emotes Type=Threshold [48x48/filesystems] Size=48 Context=FileSystems Type=Threshold [48x48@2/filesystems] Size=48 Scale=2 Context=FileSystems Type=Threshold [48x48/intl] Size=48 Context=International Type=Threshold [48x48@2/intl] Size=48 Scale=2 Context=International Type=Threshold [48x48/mimetypes] Size=48 Context=MimeTypes Type=Threshold [48x48@2/mimetypes] Size=48 Scale=2 Context=MimeTypes Type=Threshold [48x48/places] Size=48 Context=Places Type=Threshold [48x48@2/places] Size=48 Scale=2 Context=Places Type=Threshold [48x48/status] Size=48 Context=Status Type=Threshold [48x48@2/status] Size=48 Scale=2 Context=Status Type=Threshold [48x48/stock/chart] Size=48 Context=Stock Type=Threshold [48x48@2/stock/chart] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/code] Size=48 Context=Stock Type=Threshold [48x48@2/stock/code] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/data] Size=48 Context=Stock Type=Threshold [48x48@2/stock/data] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/form] Size=48 Context=Stock Type=Threshold [48x48@2/stock/form] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/image] Size=48 Context=Stock Type=Threshold [48x48@2/stock/image] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/io] Size=48 Context=Stock Type=Threshold [48x48@2/stock/io] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/media] Size=48 Context=Stock Type=Threshold [48x48@2/stock/media] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/navigation] Size=48 Context=Stock Type=Threshold [48x48@2/stock/navigation] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/net] Size=48 Context=Stock Type=Threshold [48x48@2/stock/net] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/object] Size=48 Context=Stock Type=Threshold [48x48@2/stock/object] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/table] Size=48 Context=Stock Type=Threshold [48x48@2/stock/table] Size=48 Scale=2 Context=Stock Type=Threshold [48x48/stock/text] Size=48 Context=Stock Type=Threshold [48x48@2/stock/text] Size=48 Scale=2 Context=Stock Type=Threshold [64x64/actions] Size=64 Context=Actions Type=Threshold [64x64@2/actions] Size=64 Scale=2 Context=Actions Type=Threshold [64x64/animations] Size=64 Context=Animations Type=Threshold [64x64@2/animations] Size=64 Scale=2 Context=Animations Type=Threshold [64x64/apps] Size=64 Context=Applications Type=Threshold [64x64@2/apps] Size=64 Scale=2 Context=Applications Type=Threshold [64x64/categories] Size=64 Context=Categories Type=Threshold [64x64@2/categories] Size=64 Scale=2 Context=Categories Type=Threshold [64x64/devices] Size=64 Context=Devices Type=Threshold [64x64@2/devices] Size=64 Scale=2 Context=Devices Type=Threshold [64x64/emblems] Size=64 Context=Emblems Type=Threshold [64x64@2/emblems] Size=64 Scale=2 Context=Emblems Type=Threshold [64x64/emotes] Size=64 Context=Emotes Type=Threshold [64x64@2/emotes] Size=64 Scale=2 Context=Emotes Type=Threshold [64x64/filesystems] Size=64 Context=FileSystems Type=Threshold [64x64@2/filesystems] Size=64 Scale=2 Context=FileSystems Type=Threshold [64x64/intl] Size=64 Context=International Type=Threshold [64x64@2/intl] Size=64 Scale=2 Context=International Type=Threshold [64x64/mimetypes] Size=64 Context=MimeTypes Type=Threshold [64x64@2/mimetypes] Size=64 Scale=2 Context=MimeTypes Type=Threshold [64x64/places] Size=64 Context=Places Type=Threshold [64x64@2/places] Size=64 Scale=2 Context=Places Type=Threshold [64x64/status] Size=64 Context=Status Type=Threshold [64x64@2/status] Size=64 Scale=2 Context=Status Type=Threshold [64x64/stock/chart] Size=64 Context=Stock Type=Threshold [64x64@2/stock/chart] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/code] Size=64 Context=Stock Type=Threshold [64x64@2/stock/code] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/data] Size=64 Context=Stock Type=Threshold [64x64@2/stock/data] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/form] Size=64 Context=Stock Type=Threshold [64x64@2/stock/form] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/image] Size=64 Context=Stock Type=Threshold [64x64@2/stock/image] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/io] Size=64 Context=Stock Type=Threshold [64x64@2/stock/io] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/media] Size=64 Context=Stock Type=Threshold [64x64@2/stock/media] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/navigation] Size=64 Context=Stock Type=Threshold [64x64@2/stock/navigation] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/net] Size=64 Context=Stock Type=Threshold [64x64@2/stock/net] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/object] Size=64 Context=Stock Type=Threshold [64x64@2/stock/object] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/table] Size=64 Context=Stock Type=Threshold [64x64@2/stock/table] Size=64 Scale=2 Context=Stock Type=Threshold [64x64/stock/text] Size=64 Context=Stock Type=Threshold [64x64@2/stock/text] Size=64 Scale=2 Context=Stock Type=Threshold [72x72/actions] Size=72 Context=Actions Type=Threshold [72x72@2/actions] Size=72 Scale=2 Context=Actions Type=Threshold [72x72/animations] Size=72 Context=Animations Type=Threshold [72x72@2/animations] Size=72 Scale=2 Context=Animations Type=Threshold [72x72/apps] Size=72 Context=Applications Type=Threshold [72x72@2/apps] Size=72 Scale=2 Context=Applications Type=Threshold [72x72/categories] Size=72 Context=Categories Type=Threshold [72x72@2/categories] Size=72 Scale=2 Context=Categories Type=Threshold [72x72/devices] Size=72 Context=Devices Type=Threshold [72x72@2/devices] Size=72 Scale=2 Context=Devices Type=Threshold [72x72/emblems] Size=72 Context=Emblems Type=Threshold [72x72@2/emblems] Size=72 Scale=2 Context=Emblems Type=Threshold [72x72/emotes] Size=72 Context=Emotes Type=Threshold [72x72@2/emotes] Size=72 Scale=2 Context=Emotes Type=Threshold [72x72/filesystems] Size=72 Context=FileSystems Type=Threshold [72x72@2/filesystems] Size=72 Scale=2 Context=FileSystems Type=Threshold [72x72/intl] Size=72 Context=International Type=Threshold [72x72@2/intl] Size=72 Scale=2 Context=International Type=Threshold [72x72/mimetypes] Size=72 Context=MimeTypes Type=Threshold [72x72@2/mimetypes] Size=72 Scale=2 Context=MimeTypes Type=Threshold [72x72/places] Size=72 Context=Places Type=Threshold [72x72@2/places] Size=72 Scale=2 Context=Places Type=Threshold [72x72/status] Size=72 Context=Status Type=Threshold [72x72@2/status] Size=72 Scale=2 Context=Status Type=Threshold [72x72/stock/chart] Size=72 Context=Stock Type=Threshold [72x72@2/stock/chart] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/code] Size=72 Context=Stock Type=Threshold [72x72@2/stock/code] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/data] Size=72 Context=Stock Type=Threshold [72x72@2/stock/data] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/form] Size=72 Context=Stock Type=Threshold [72x72@2/stock/form] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/image] Size=72 Context=Stock Type=Threshold [72x72@2/stock/image] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/io] Size=72 Context=Stock Type=Threshold [72x72@2/stock/io] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/media] Size=72 Context=Stock Type=Threshold [72x72@2/stock/media] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/navigation] Size=72 Context=Stock Type=Threshold [72x72@2/stock/navigation] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/net] Size=72 Context=Stock Type=Threshold [72x72@2/stock/net] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/object] Size=72 Context=Stock Type=Threshold [72x72@2/stock/object] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/table] Size=72 Context=Stock Type=Threshold [72x72@2/stock/table] Size=72 Scale=2 Context=Stock Type=Threshold [72x72/stock/text] Size=72 Context=Stock Type=Threshold [72x72@2/stock/text] Size=72 Scale=2 Context=Stock Type=Threshold [96x96/actions] Size=96 Context=Actions Type=Threshold [96x96@2/actions] Size=96 Scale=2 Context=Actions Type=Threshold [96x96/animations] Size=96 Context=Animations Type=Threshold [96x96@2/animations] Size=96 Scale=2 Context=Animations Type=Threshold [96x96/apps] Size=96 Context=Applications Type=Threshold [96x96@2/apps] Size=96 Scale=2 Context=Applications Type=Threshold [96x96/categories] Size=96 Context=Categories Type=Threshold [96x96@2/categories] Size=96 Scale=2 Context=Categories Type=Threshold [96x96/devices] Size=96 Context=Devices Type=Threshold [96x96@2/devices] Size=96 Scale=2 Context=Devices Type=Threshold [96x96/emblems] Size=96 Context=Emblems Type=Threshold [96x96@2/emblems] Size=96 Scale=2 Context=Emblems Type=Threshold [96x96/emotes] Size=96 Context=Emotes Type=Threshold [96x96@2/emotes] Size=96 Scale=2 Context=Emotes Type=Threshold [96x96/filesystems] Size=96 Context=FileSystems Type=Threshold [96x96@2/filesystems] Size=96 Scale=2 Context=FileSystems Type=Threshold [96x96/intl] Size=96 Context=International Type=Threshold [96x96@2/intl] Size=96 Scale=2 Context=International Type=Threshold [96x96/mimetypes] Size=96 Context=MimeTypes Type=Threshold [96x96@2/mimetypes] Size=96 Scale=2 Context=MimeTypes Type=Threshold [96x96/places] Size=96 Context=Places Type=Threshold [96x96@2/places] Size=96 Scale=2 Context=Places Type=Threshold [96x96/status] Size=96 Context=Status Type=Threshold [96x96@2/status] Size=96 Scale=2 Context=Status Type=Threshold [96x96/stock/chart] Size=96 Context=Stock Type=Threshold [96x96@2/stock/chart] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/code] Size=96 Context=Stock Type=Threshold [96x96@2/stock/code] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/data] Size=96 Context=Stock Type=Threshold [96x96@2/stock/data] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/form] Size=96 Context=Stock Type=Threshold [96x96@2/stock/form] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/image] Size=96 Context=Stock Type=Threshold [96x96@2/stock/image] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/io] Size=96 Context=Stock Type=Threshold [96x96@2/stock/io] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/media] Size=96 Context=Stock Type=Threshold [96x96@2/stock/media] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/navigation] Size=96 Context=Stock Type=Threshold [96x96@2/stock/navigation] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/net] Size=96 Context=Stock Type=Threshold [96x96@2/stock/net] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/object] Size=96 Context=Stock Type=Threshold [96x96@2/stock/object] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/table] Size=96 Context=Stock Type=Threshold [96x96@2/stock/table] Size=96 Scale=2 Context=Stock Type=Threshold [96x96/stock/text] Size=96 Context=Stock Type=Threshold [96x96@2/stock/text] Size=96 Scale=2 Context=Stock Type=Threshold [128x128/actions] Size=128 Context=Actions Type=Threshold [128x128@2/actions] Size=128 Scale=2 Context=Actions Type=Threshold [128x128/animations] Size=128 Context=Animations Type=Threshold [128x128@2/animations] Size=128 Scale=2 Context=Animations Type=Threshold [128x128/apps] Size=128 Context=Applications Type=Threshold [128x128@2/apps] Size=128 Scale=2 Context=Applications Type=Threshold [128x128/categories] Size=128 Context=Categories Type=Threshold [128x128@2/categories] Size=128 Scale=2 Context=Categories Type=Threshold [128x128/devices] Size=128 Context=Devices Type=Threshold [128x128@2/devices] Size=128 Scale=2 Context=Devices Type=Threshold [128x128/emblems] Size=128 Context=Emblems Type=Threshold [128x128@2/emblems] Size=128 Scale=2 Context=Emblems Type=Threshold [128x128/emotes] Size=128 Context=Emotes Type=Threshold [128x128@2/emotes] Size=128 Scale=2 Context=Emotes Type=Threshold [128x128/filesystems] Size=128 Context=FileSystems Type=Threshold [128x128@2/filesystems] Size=128 Scale=2 Context=FileSystems Type=Threshold [128x128/intl] Size=128 Context=International Type=Threshold [128x128@2/intl] Size=128 Scale=2 Context=International Type=Threshold [128x128/mimetypes] Size=128 Context=MimeTypes Type=Threshold [128x128@2/mimetypes] Size=128 Scale=2 Context=MimeTypes Type=Threshold [128x128/places] Size=128 Context=Places Type=Threshold [128x128@2/places] Size=128 Scale=2 Context=Places Type=Threshold [128x128/status] Size=128 Context=Status Type=Threshold [128x128@2/status] Size=128 Scale=2 Context=Status Type=Threshold [128x128/stock/chart] Size=128 Context=Stock Type=Threshold [128x128@2/stock/chart] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/code] Size=128 Context=Stock Type=Threshold [128x128@2/stock/code] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/data] Size=128 Context=Stock Type=Threshold [128x128@2/stock/data] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/form] Size=128 Context=Stock Type=Threshold [128x128@2/stock/form] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/image] Size=128 Context=Stock Type=Threshold [128x128@2/stock/image] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/io] Size=128 Context=Stock Type=Threshold [128x128@2/stock/io] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/media] Size=128 Context=Stock Type=Threshold [128x128@2/stock/media] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/navigation] Size=128 Context=Stock Type=Threshold [128x128@2/stock/navigation] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/net] Size=128 Context=Stock Type=Threshold [128x128@2/stock/net] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/object] Size=128 Context=Stock Type=Threshold [128x128@2/stock/object] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/table] Size=128 Context=Stock Type=Threshold [128x128@2/stock/table] Size=128 Scale=2 Context=Stock Type=Threshold [128x128/stock/text] Size=128 Context=Stock Type=Threshold [128x128@2/stock/text] Size=128 Scale=2 Context=Stock Type=Threshold [192x192/actions] Size=192 Context=Actions Type=Threshold [192x192@2/actions] Size=192 Scale=2 Context=Actions Type=Threshold [192x192/animations] Size=192 Context=Animations Type=Threshold [192x192@2/animations] Size=192 Scale=2 Context=Animations Type=Threshold [192x192/apps] Size=192 Context=Applications Type=Threshold [192x192@2/apps] Size=192 Scale=2 Context=Applications Type=Threshold [192x192/categories] Size=192 Context=Categories Type=Threshold [192x192@2/categories] Size=192 Scale=2 Context=Categories Type=Threshold [192x192/devices] Size=192 Context=Devices Type=Threshold [192x192@2/devices] Size=192 Scale=2 Context=Devices Type=Threshold [192x192/emblems] Size=192 Context=Emblems Type=Threshold [192x192@2/emblems] Size=192 Scale=2 Context=Emblems Type=Threshold [192x192/emotes] Size=192 Context=Emotes Type=Threshold [192x192@2/emotes] Size=192 Scale=2 Context=Emotes Type=Threshold [192x192/filesystems] Size=192 Context=FileSystems Type=Threshold [192x192@2/filesystems] Size=192 Scale=2 Context=FileSystems Type=Threshold [192x192/intl] Size=192 Context=International Type=Threshold [192x192@2/intl] Size=192 Scale=2 Context=International Type=Threshold [192x192/mimetypes] Size=192 Context=MimeTypes Type=Threshold [192x192@2/mimetypes] Size=192 Scale=2 Context=MimeTypes Type=Threshold [192x192/places] Size=192 Context=Places Type=Threshold [192x192@2/places] Size=192 Scale=2 Context=Places Type=Threshold [192x192/status] Size=192 Context=Status Type=Threshold [192x192@2/status] Size=192 Scale=2 Context=Status Type=Threshold [192x192/stock/chart] Size=192 Context=Stock Type=Threshold [192x192@2/stock/chart] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/code] Size=192 Context=Stock Type=Threshold [192x192@2/stock/code] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/data] Size=192 Context=Stock Type=Threshold [192x192@2/stock/data] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/form] Size=192 Context=Stock Type=Threshold [192x192@2/stock/form] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/image] Size=192 Context=Stock Type=Threshold [192x192@2/stock/image] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/io] Size=192 Context=Stock Type=Threshold [192x192@2/stock/io] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/media] Size=192 Context=Stock Type=Threshold [192x192@2/stock/media] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/navigation] Size=192 Context=Stock Type=Threshold [192x192@2/stock/navigation] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/net] Size=192 Context=Stock Type=Threshold [192x192@2/stock/net] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/object] Size=192 Context=Stock Type=Threshold [192x192@2/stock/object] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/table] Size=192 Context=Stock Type=Threshold [192x192@2/stock/table] Size=192 Scale=2 Context=Stock Type=Threshold [192x192/stock/text] Size=192 Context=Stock Type=Threshold [192x192@2/stock/text] Size=192 Scale=2 Context=Stock Type=Threshold [256x256/actions] MinSize=64 Size=256 MaxSize=256 Context=Actions Type=Scalable [256x256@2/actions] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Actions Type=Scalable [256x256/animations] MinSize=64 Size=256 MaxSize=256 Context=Animations Type=Scalable [256x256@2/animations] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Animations Type=Scalable [256x256/apps] MinSize=64 Size=256 MaxSize=256 Context=Applications Type=Scalable [256x256@2/apps] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Applications Type=Scalable [256x256/categories] MinSize=64 Size=256 MaxSize=256 Context=Categories Type=Scalable [256x256@2/categories] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Categories Type=Scalable [256x256/devices] MinSize=64 Size=256 MaxSize=256 Context=Devices Type=Scalable [256x256@2/devices] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Devices Type=Scalable [256x256/emblems] MinSize=64 Size=256 MaxSize=256 Context=Emblems Type=Scalable [256x256@2/emblems] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Emblems Type=Scalable [256x256/emotes] MinSize=64 Size=256 MaxSize=256 Context=Emotes Type=Scalable [256x256@2/emotes] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Emotes Type=Scalable [256x256/filesystems] MinSize=64 Size=256 MaxSize=256 Context=FileSystems Type=Scalable [256x256@2/filesystems] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=FileSystems Type=Scalable [256x256/intl] MinSize=64 Size=256 MaxSize=256 Context=International Type=Scalable [256x256@2/intl] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=International Type=Scalable [256x256/mimetypes] MinSize=64 Size=256 MaxSize=256 Context=MimeTypes Type=Scalable [256x256@2/mimetypes] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=MimeTypes Type=Scalable [256x256/places] MinSize=64 Size=256 MaxSize=256 Context=Places Type=Scalable [256x256@2/places] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Places Type=Scalable [256x256/status] MinSize=64 Size=256 MaxSize=256 Context=Status Type=Scalable [256x256@2/status] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Status Type=Scalable [256x256/stock/chart] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/chart] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/code] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/code] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/data] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/data] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/form] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/form] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/image] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/image] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/io] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/io] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/media] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/media] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/navigation] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/navigation] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/net] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/net] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/object] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/object] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/table] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/table] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [256x256/stock/text] MinSize=64 Size=256 MaxSize=256 Context=Stock Type=Scalable [256x256@2/stock/text] MinSize=64 Size=256 Scale=2 MaxSize=256 Context=Stock Type=Scalable [512x512/actions] MinSize=64 Size=512 MaxSize=512 Context=Actions Type=Scalable [512x512@2/actions] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Actions Type=Scalable [512x512/animations] MinSize=64 Size=512 MaxSize=512 Context=Animations Type=Scalable [512x512@2/animations] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Animations Type=Scalable [512x512/apps] MinSize=64 Size=512 MaxSize=512 Context=Applications Type=Scalable [512x512@2/apps] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Applications Type=Scalable [512x512/categories] MinSize=64 Size=512 MaxSize=512 Context=Categories Type=Scalable [512x512@2/categories] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Categories Type=Scalable [512x512/devices] MinSize=64 Size=512 MaxSize=512 Context=Devices Type=Scalable [512x512@2/devices] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Devices Type=Scalable [512x512/emblems] MinSize=64 Size=512 MaxSize=512 Context=Emblems Type=Scalable [512x512@2/emblems] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Emblems Type=Scalable [512x512/emotes] MinSize=64 Size=512 MaxSize=512 Context=Emotes Type=Scalable [512x512@2/emotes] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Emotes Type=Scalable [512x512/filesystems] MinSize=64 Size=512 MaxSize=512 Context=FileSystems Type=Scalable [512x512@2/filesystems] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=FileSystems Type=Scalable [512x512/intl] MinSize=64 Size=512 MaxSize=512 Context=International Type=Scalable [512x512@2/intl] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=International Type=Scalable [512x512/mimetypes] MinSize=64 Size=512 MaxSize=512 Context=MimeTypes Type=Scalable [512x512@2/mimetypes] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=MimeTypes Type=Scalable [512x512/places] MinSize=64 Size=512 MaxSize=512 Context=Places Type=Scalable [512x512@2/places] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Places Type=Scalable [512x512/status] MinSize=64 Size=512 MaxSize=512 Context=Status Type=Scalable [512x512@2/status] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Status Type=Scalable [512x512/stock/chart] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/chart] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/code] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/code] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/data] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/data] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/form] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/form] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/image] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/image] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/io] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/io] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/media] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/media] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/navigation] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/navigation] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/net] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/net] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/object] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/object] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/table] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/table] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [512x512/stock/text] MinSize=64 Size=512 MaxSize=512 Context=Stock Type=Scalable [512x512@2/stock/text] MinSize=64 Size=512 Scale=2 MaxSize=512 Context=Stock Type=Scalable [scalable/actions] MinSize=1 Size=128 MaxSize=256 Context=Actions Type=Scalable [scalable/animations] MinSize=1 Size=128 MaxSize=256 Context=Animations Type=Scalable [scalable/apps] MinSize=1 Size=128 MaxSize=256 Context=Applications Type=Scalable [scalable/categories] MinSize=1 Size=128 MaxSize=256 Context=Categories Type=Scalable [scalable/devices] MinSize=1 Size=128 MaxSize=512 Context=Devices Type=Scalable [scalable/emblems] MinSize=1 Size=128 MaxSize=256 Context=Emblems Type=Scalable [scalable/emotes] MinSize=1 Size=128 MaxSize=512 Context=Emotes Type=Scalable [scalable/filesystems] MinSize=1 Size=128 MaxSize=256 Context=FileSystems Type=Scalable [scalable/intl] MinSize=1 Size=128 MaxSize=512 Context=International Type=Scalable [scalable/mimetypes] MinSize=1 Size=128 MaxSize=256 Context=MimeTypes Type=Scalable [scalable/places] MinSize=1 Size=128 MaxSize=512 Context=Places Type=Scalable [scalable/status] MinSize=1 Size=128 MaxSize=256 Context=Status Type=Scalable [scalable/stock/chart] MinSize=1 Size=128 MaxSize=512 Context=Stock Type=Scalable [scalable/stock/code] MinSize=1 Size=128 MaxSize=256 Context=Stock Type=Scalable [scalable/stock/data] MinSize=1 Size=128 MaxSize=512 Context=Stock Type=Scalable [scalable/stock/form] MinSize=1 Size=128 MaxSize=256 Context=Stock Type=Scalable [scalable/stock/image] MinSize=1 Size=128 MaxSize=512 Context=Stock Type=Scalable [scalable/stock/io] MinSize=1 Size=128 MaxSize=256 Context=Stock Type=Scalable [scalable/stock/media] MinSize=1 Size=128 MaxSize=512 Context=Stock Type=Scalable [scalable/stock/navigation] MinSize=1 Size=128 MaxSize=256 Context=Stock Type=Scalable [scalable/stock/net] MinSize=1 Size=128 MaxSize=512 Context=Stock Type=Scalable [scalable/stock/object] MinSize=1 Size=128 MaxSize=256 Context=Stock Type=Scalable [scalable/stock/table] MinSize=1 Size=128 MaxSize=512 Context=Stock Type=Scalable [scalable/stock/text] MinSize=1 Size=128 MaxSize=256 Context=Stock Type=Scalable [symbolic/apps] MinSize=8 Size=16 MaxSize=512 Context=Applications Type=Scalable appstream-generator-0.8.6/data/meson.build000066400000000000000000000014371416066706400205770ustar00rootroot00000000000000 # data install_data('asgen-hints.json', install_dir: 'share/appstream') install_data('hicolor-theme-index.theme', install_dir: 'share/appstream') metainfo_file = files('org.freedesktop.appstream.generator.metainfo.xml') install_data(metainfo_file, install_dir: join_paths (get_option ('datadir'), 'metainfo')) appstreamcli_exe = find_program('appstreamcli', required: false) if appstreamcli_exe.found() # Validate our MetaInfo file test('asgen-validate_metainfo', appstreamcli_exe, args: ['validate', '--no-net', '--pedantic', metainfo_file] ) endif # templates #install_subdir('data/templates/', install_dir: 'share/appstream') # FIXME: Doesn't handle dir symlinks correctly meson.add_install_script(source_root + '/contrib/setup/meson-install-templates.sh') appstream-generator-0.8.6/data/org.freedesktop.appstream.generator.metainfo.xml000066400000000000000000000035751416066706400300460ustar00rootroot00000000000000 org.freedesktop.appstream.generator FSFAP LGPL-3.0+ AppStream Generator A fast AppStream metadata generator

AppStream is a metadata specification which permits software components to provide information about themselves to automated systems and end-users before the software is actually installed.

The appstream-generator tool generates AppStream metadata from the repositories of a software distribution. It currently supports the following repository formats / distributions: Debian, Ubuntu, Arch Linux, RPM-MD (Fedora, Mageia).

The generator will produce AppStream collection metadata files in the AppStream YAML or XML format to be shipped to users, as well as a detailed HTML report about found components and HTML and JSON reports on issues detected with the scanned metadata. It reads .desktop files as well as metainfo files, renders fonts, scales images, caches screenshots etc. to produce high-quality metadata for AppStream based software centers to consume. Usually, appstream-generator is integrated with the existing software build & packaging workflow of a distribution.

https://github.com/ximion/appstream-generator https://github.com/ximion/appstream-generator/blob/master/docs/index.md https://github.com/ximion/appstream-generator/issues Freedesktop appstream-generator
appstream-generator-0.8.6/data/templates/000077500000000000000000000000001416066706400204265ustar00rootroot00000000000000appstream-generator-0.8.6/data/templates/debian/000077500000000000000000000000001416066706400216505ustar00rootroot00000000000000appstream-generator-0.8.6/data/templates/debian/base.html000077700000000000000000000000001416066706400271022../default/base.htmlustar00rootroot00000000000000appstream-generator-0.8.6/data/templates/debian/main.html000066400000000000000000000061771416066706400234750ustar00rootroot00000000000000{{#partial}}{title}Start{{/partial}} {{#partial}}{ header_content } AppStream data hints for {{project_name}} {{/partial}} {{#partial}}{ content }

Welcome!

Welcome to the AppStream Generator HTML pages!

These pages exist to provide a user-friendly view on the issues discovered by the AppStream metadata generator while extracting metadata from packages in the {{project_name}} archive. They can also be used to take a look at the raw metadata, to spot possible problems with the data itself or the generation process.

Select a suite

{{#suites}}

{{suite}}

{{/suites}}
{{#oldsuites}}

{{suite}}

{{/oldsuites}}

AppStream Generator Logo

What is AppStream and DEP-11?

AppStream is a cross-distro XML format to provide metadata for software components and to assign unique identifiers to software.
In Debian, we parse all XML provided by upstream projects as well as other metadata (.desktop-files, ...), and compile a single YAML metadata file from it, which is then shipped to users via APT.

While the official AppStream specification is based on XML, Debian uses a YAML version of the format for easier use in existing scripts and for better archive integration. This format is called DEP-11, and initially had a much wider scope in enhancing archive metadata than AppStream had. Today AppStream covers that as well, and DEP-11 is only a YAML version of AppStream.

The generated metadata can for example be used by software centers like GNOME Software or KDE Discover to display a user-friendly application-centric view on the package archive.
It can also be used by other software to find missing plugins, codecs, fonts, etc. or simply by users to install software on any Linux distribution without knowing the exact package name.

More information

See AppStream @ wiki.d.o for information about AppStream integration and usage in Debian.
The offical AppStream specification can be found at freedesktop.org, a description of the DEP-11 YAML format is hosted there as well.

You can find the source-code of the AppStream Generator here.

Log files of the generator runs are stored in logs/, machine-readable issue hints can be found in hints/, valid metadata is located in data/ and all exported media is made available via media/.

{{/partial}} {{> base}} appstream-generator-0.8.6/data/templates/debian/static/000077500000000000000000000000001416066706400231375ustar00rootroot00000000000000appstream-generator-0.8.6/data/templates/debian/static/css/000077500000000000000000000000001416066706400237275ustar00rootroot00000000000000appstream-generator-0.8.6/data/templates/debian/static/css/highlight.css000077700000000000000000000000001416066706400354302../../../default/static/css/highlight.cssustar00rootroot00000000000000appstream-generator-0.8.6/data/templates/debian/static/css/style.css000066400000000000000000000075331416066706400256110ustar00rootroot00000000000000html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust:100%; height:100%; } body { border-sizing: border-box; font-family: Cantarell,"Helvetica Neue",Helvetica,Arial,sans-serif; font-size: 14px; line-height: 20px; color: #333333; margin: 0; height: 93%; } a { color: #337ab7; text-decoration: none; background-color: transparent; } .headbar { border-radius: 4px; display: block; border: 1px solid transparent; margin-bottom: 20px; min-height: 50px; position: relative; background-color: #f8f8f8; border-color: #e7e7e7; border-width: 0 0 1px; z-index: 1000; border-radius: 0; margin-bottom: 14px; } .headbar-content { font-size: 18px; line-height: 20px; padding: 15px; float: left; } .headbar-content-right { font-size: 14px; line-height: 20px; padding: 15px; float: right; } .content { padding: 0em 1em 0em 1em; } .wrapper { width: 60%; } .wrapper hr { border-top: none; border-bottom: 1px solid #819eb7; margin-bottom: 1em; } img.fit { max-width: 99%; max-height: 99%; } hr { border-top: none; border-bottom: 1px solid #d70a53; margin-bottom: 1em; } footer { text-align: center; margin-top: 1em; } span.avoidwrap { display: inline-block; } .infobox { border-color: #eee; border-image: none; border-radius: 3px; border-style: solid; border-width: 1px 1px 1px 5px; margin: 20px 0; padding: 20px; } .infobox h2 { margin-bottom: 5px; margin-top: 0; } .infobox p:last-child { margin-bottom: 0 } .infobox-hint { border-left-color: #1b809e; } .infobox-hint h2 { color: #1b809e; } .infobox-error { border-left-color: #ce4844; } .infobox-error h2 { color: #ce4844; } .infobox-warning { border-left-color: #aa6708; } .infobox-warning h2 { color: #aa6708; } /* label styles copied from Bootstrap */ .label { border-radius: 0.25em; color: #fff; display: inline; font-size: 75%; font-weight: 700; line-height: 1; padding: 0.2em 0.6em 0.3em; text-align: center; vertical-align: baseline; white-space: nowrap; } .label-info { background-color: #5bc0de; } .label-warning { background-color: #f0ad4e; } .label-error { background-color: #d9534f; } .label-neutral { background-color: #777; } .overviewlisting a { color: #000000; text-decoration: none; } .overviewlisting li { padding: 2px 4px 2px; } code { background-color: #f9f2f4; border-radius: 4px; color: #c7254e; font-size: 90%; padding: 2px 4px; } .well{ min-height:20px; padding:19px; margin-bottom:20px; background-color:#f5f5f5; border:1px solid #e3e3e3; border-radius:4px; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.05); box-shadow:inset 0 1px 1px rgba(0,0,0,.05) } .progress { background-color: #f5f5f5; border-radius: 4px; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) inset; height: 20px; margin-bottom: 20px; overflow: hidden; } .progress-bar { background-color: #337ab7; box-shadow: 0 -1px 0 rgba(0, 0, 0, 0.15) inset; color: #fff; float: left; font-size: 12px; height: 100%; line-height: 20px; text-align: center; transition: width 0.6s ease 0s; width: 0; } .progress-bar-blue { background-color: #5bc0de; } .progress-bar-green { background-color: #5cb85c; } .progress-bar-yellow { background-color: #f0ad4e; } .progress-bar-red { background-color: #d9534f; } .sr-only { border: 0 none; clip: rect(0px, 0px, 0px, 0px); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } .permalink { font-size: 75%; color: #999; line-height: 100%; font-weight: normal; text-decoration: none; } appstream-generator-0.8.6/data/templates/debian/static/img000077700000000000000000000000001416066706400277502../../default/static/img/ustar00rootroot00000000000000appstream-generator-0.8.6/data/templates/debian/static/js000077700000000000000000000000001416066706400273712../../default/static/jsustar00rootroot00000000000000appstream-generator-0.8.6/data/templates/default/000077500000000000000000000000001416066706400220525ustar00rootroot00000000000000appstream-generator-0.8.6/data/templates/default/base.html000066400000000000000000000021621416066706400236530ustar00rootroot00000000000000 {{#block}}{ head } AppStream Report for {{project_name}} - {{#block}}{ title }{{/block}} {{#block}}{ head_extra }{{/block}} {{/block}}
{{#block}}{ header_content }{{/block}}
{{#block}}{ float_right }{{/block}}
{{#block}}{ content }{{/block}}

Generated by appstream-generator (v{{generator_version}}).

{{#block}}{ page_details }{{/block}}
appstream-generator-0.8.6/data/templates/default/issues_index.html000066400000000000000000000024261416066706400254460ustar00rootroot00000000000000{{#partial}}{title}Hints summary for {{suite}}/{{section}}{{/partial}} {{#partial}}{ header_content } ⇦ | Hints summary for {{suite}}/{{section}} {{/partial}} {{#partial}}{ content }

Metadata processing hints found for {{suite}}/{{section}}

{{#summaries}}

{{maintainer}}

    {{#packages}}
  • {{pkgname}}  {{#has_info_count}} Infos: {{info_count}} {{/has_info_count}} {{#has_warning_count}} Warnings: {{warning_count}} {{/has_warning_count}} {{#has_error_count}} Errors: {{error_count}} {{/has_error_count}}
  • {{/packages}}
{{/summaries}} {{/partial}} {{#partial}}{ float_right } Last updated on: {{time}} {{/partial}} {{> base}} appstream-generator-0.8.6/data/templates/default/issues_page.html000066400000000000000000000035401416066706400252510ustar00rootroot00000000000000{{#partial}}{title}Issues for {{package_name}} in {{suite}}/{{section}}{{/partial}} {{#partial}}{ float_right } Last updated on: {{time}} {{/partial}} {{#partial}}{ header_content } ⇦ | {{package_name}} [{{section}}] {{/partial}} {{#partial}}{ content }

Hints for {{package_name}} in {{section}}

{{#entries}}

{{component_id}} {{#architectures}} ⚙ {{arch}} {{/architectures}}

{{#has_errors}}

Errors

    {{#errors}}
  • {{error_tag}}
    {{{error_description}}}
  • {{/errors}}
{{/has_errors}} {{#has_warnings}}

Warnings

    {{#warnings}}
  • {{warning_tag}}
    {{{warning_description}}}
  • {{/warnings}}
{{/has_warnings}} {{#has_infos}}

Hints

    {{#infos}}
  • {{info_tag}}
    {{{info_description}}}
  • {{/infos}}
{{/has_infos}} {{/entries}}
{{/partial}} {{> base}} appstream-generator-0.8.6/data/templates/default/main.html000066400000000000000000000041571416066706400236730ustar00rootroot00000000000000{{#partial}}{title}Start{{/partial}} {{#partial}}{ header_content } AppStream data hints for {{project_name}} {{/partial}} {{#partial}}{ content }

Welcome!

Welcome to the AppStream Generator HTML pages!

These pages exist to provide a user-friendly view on the issues discovered by the AppStream metadata generator while extracting metadata from packages in the {{project_name}} archive. They can also be used to take a look at the raw metadata, to spot possible problems with the data itself or the generation process.

Select a suite

{{#suites}}

{{suite}}

{{/suites}}
{{#oldsuites}}

{{suite}}

{{/oldsuites}}

AppStream Generator Logo

What is AppStream?

AppStream is a cross-distro XML format to provide metadata for software components and to assign unique identifiers to software.
In {{project_name}}, we parse all XML provided by upstream projects as well as other metadata (.desktop-files, ...), and compile a single metadata file from it, which is then shipped to users.

The generated metadata can for example be used by software centers like GNOME Software or KDE Discover to display a user-friendly application-centric view on the package archive.
It can also be used by other software to find missing plugins, codecs, fonts, etc. or simply by users to install software on any Linux distribution without knowing the exact package name.

More information

The offical AppStream specification can be found at freedesktop.org.

You can find the source-code of the AppStream Generator here.

{{/partial}} {{> base}} appstream-generator-0.8.6/data/templates/default/metainfo_index.html000066400000000000000000000017341416066706400257360ustar00rootroot00000000000000{{#partial}}{title}Components in {{suite}}/{{section}}{{/partial}} {{#partial}}{ header_content } ⇦ | Components summary for {{suite}}/{{section}} {{/partial}} {{#partial}}{ content }

Metadata for {{suite}}/{{section}}

{{#summaries}}

{{maintainer}}

    {{#packages}}
  • {{pkgname}} 
      {{#components}}
    • {{cid}}
    • {{/components}}
  • {{/packages}}
{{/summaries}} {{/partial}} {{#partial}}{ float_right } Last updated on: {{time}} {{/partial}} {{> base}} appstream-generator-0.8.6/data/templates/default/metainfo_page.html000066400000000000000000000022251416066706400255370ustar00rootroot00000000000000{{#partial}}{title}{{package_name}} in {{suite}}/{{section}}{{/partial}} {{#partial}}{ float_right } Last updated on: {{time}} {{/partial}} {{#partial}}{ head_extra } {{/partial}} {{#partial}}{ header_content } ⇦ | {{package_name}} [{{section}}] {{/partial}} {{#partial}}{ content }

Metadata for {{package_name}} in {{section}}

{{#cpts}}

{{component_id}} {{#architectures}} ⚙ {{arch}} {{/architectures}}

Icon
{{metadata}}
{{/cpts}}
{{/partial}} {{> base}} appstream-generator-0.8.6/data/templates/default/section_page.html000066400000000000000000000125261416066706400254060ustar00rootroot00000000000000{{#partial}}{title}Data for {{suite}}/{{section}}{{/partial}} {{#partial}}{ head_extra } {{/partial}} {{#partial}}{ header_content } ⇦ | AppStream data for {{project_name}}/{{suite}}/{{section}} {{/partial}} {{#partial}}{ content }

Overview for {{suite}}/{{section}}

Data

Issues - Issues found while extracting the data

Metainfo - Per-package view of the generated data

Health

Issue overview

{{valid_percentage}}% Valid
{{info_percentage}}% Infos
{{warning_percentage}}% Warnings
{{error_percentage}}% Errors
  • {{metainfo_count}} valid components
  • {{error_count}} errors
  • {{warning_count}} warnings
  • {{info_count}} infos/hints
{{/partial}} {{#partial}}{ float_right } Last updated on: {{time}} {{/partial}} {{> base}} appstream-generator-0.8.6/data/templates/default/sections_index.html000066400000000000000000000074741416066706400257720ustar00rootroot00000000000000{{#partial}}{title}Data for the {{suite}} suite{{/partial}} {{#partial}}{ header_content } ⇦ | AppStream data for {{project_name}}/{{suite}} {{/partial}} {{#partial}}{ head_extra } {{/partial}} {{#partial}}{ content }

Select an archive section

Sections

{{#sections}}

{{section}}

{{/sections}}

Health of suite "{{suite}}"

{{#sections}}
{{section}}
{{/sections}}
{{/partial}} {{#partial}}{ float_right } Last updated on: {{time}} {{/partial}} {{> base}} appstream-generator-0.8.6/data/templates/default/static/000077500000000000000000000000001416066706400233415ustar00rootroot00000000000000appstream-generator-0.8.6/data/templates/default/static/css/000077500000000000000000000000001416066706400241315ustar00rootroot00000000000000appstream-generator-0.8.6/data/templates/default/static/css/highlight.css000066400000000000000000000021741416066706400266160ustar00rootroot00000000000000/* github.com style (c) Vasily Polovnyov */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f5f5f5; } .hljs-comment, .hljs-quote { color: #998; font-style: italic; } .hljs-keyword, .hljs-selector-tag, .hljs-subst { color: #333; font-weight: bold; } .hljs-number, .hljs-literal, .hljs-variable, .hljs-template-variable, .hljs-tag .hljs-attr { color: #008080; } .hljs-string, .hljs-doctag { color: #d14; } .hljs-title, .hljs-section, .hljs-selector-id { color: #900; font-weight: bold; } .hljs-subst { font-weight: normal; } .hljs-type, .hljs-class .hljs-title { color: #458; font-weight: bold; } .hljs-tag, .hljs-name, .hljs-attribute { color: #000080; font-weight: normal; } .hljs-regexp, .hljs-link { color: #009926; } .hljs-symbol, .hljs-bullet { color: #990073; } .hljs-built_in, .hljs-builtin-name { color: #0086b3; } .hljs-meta { color: #999; font-weight: bold; } .hljs-deletion { background: #fdd; } .hljs-addition { background: #dfd; } .hljs-emphasis { font-style: italic; } .hljs-strong { font-weight: bold; } appstream-generator-0.8.6/data/templates/default/static/css/style.css000066400000000000000000000075331416066706400260130ustar00rootroot00000000000000html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust:100%; height:100%; } body { border-sizing: border-box; font-family: Cantarell,"Helvetica Neue",Helvetica,Arial,sans-serif; font-size: 14px; line-height: 20px; color: #333333; margin: 0; height: 93%; } a { color: #337ab7; text-decoration: none; background-color: transparent; } .headbar { border-radius: 4px; display: block; border: 1px solid transparent; margin-bottom: 20px; min-height: 50px; position: relative; background-color: #f8f8f8; border-color: #e7e7e7; border-width: 0 0 1px; z-index: 1000; border-radius: 0; margin-bottom: 14px; } .headbar-content { font-size: 18px; line-height: 20px; padding: 15px; float: left; } .headbar-content-right { font-size: 14px; line-height: 20px; padding: 15px; float: right; } .content { padding: 0em 1em 0em 1em; } .wrapper { width: 60%; } .wrapper hr { border-top: none; border-bottom: 1px solid #819eb7; margin-bottom: 1em; } img.fit { max-width: 99%; max-height: 99%; } hr { border-top: none; border-bottom: 1px solid #0a630d; margin-bottom: 1em; } footer { text-align: center; margin-top: 1em; } span.avoidwrap { display: inline-block; } .infobox { border-color: #eee; border-image: none; border-radius: 3px; border-style: solid; border-width: 1px 1px 1px 5px; margin: 20px 0; padding: 20px; } .infobox h2 { margin-bottom: 5px; margin-top: 0; } .infobox p:last-child { margin-bottom: 0 } .infobox-hint { border-left-color: #1b809e; } .infobox-hint h2 { color: #1b809e; } .infobox-error { border-left-color: #ce4844; } .infobox-error h2 { color: #ce4844; } .infobox-warning { border-left-color: #aa6708; } .infobox-warning h2 { color: #aa6708; } /* label styles copied from Bootstrap */ .label { border-radius: 0.25em; color: #fff; display: inline; font-size: 75%; font-weight: 700; line-height: 1; padding: 0.2em 0.6em 0.3em; text-align: center; vertical-align: baseline; white-space: nowrap; } .label-info { background-color: #5bc0de; } .label-warning { background-color: #f0ad4e; } .label-error { background-color: #d9534f; } .label-neutral { background-color: #777; } .overviewlisting a { color: #000000; text-decoration: none; } .overviewlisting li { padding: 2px 4px 2px; } code { background-color: #f9f2f4; border-radius: 4px; color: #c7254e; font-size: 90%; padding: 2px 4px; } .well{ min-height:20px; padding:19px; margin-bottom:20px; background-color:#f5f5f5; border:1px solid #e3e3e3; border-radius:4px; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.05); box-shadow:inset 0 1px 1px rgba(0,0,0,.05) } .progress { background-color: #f5f5f5; border-radius: 4px; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) inset; height: 20px; margin-bottom: 20px; overflow: hidden; } .progress-bar { background-color: #337ab7; box-shadow: 0 -1px 0 rgba(0, 0, 0, 0.15) inset; color: #fff; float: left; font-size: 12px; height: 100%; line-height: 20px; text-align: center; transition: width 0.6s ease 0s; width: 0; } .progress-bar-blue { background-color: #5bc0de; } .progress-bar-green { background-color: #5cb85c; } .progress-bar-yellow { background-color: #f0ad4e; } .progress-bar-red { background-color: #d9534f; } .sr-only { border: 0 none; clip: rect(0px, 0px, 0px, 0px); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } .permalink { font-size: 75%; color: #999; line-height: 100%; font-weight: normal; text-decoration: none; } appstream-generator-0.8.6/data/templates/default/static/img/000077500000000000000000000000001416066706400241155ustar00rootroot00000000000000appstream-generator-0.8.6/data/templates/default/static/img/asgen.png000066400000000000000000000365031416066706400257270ustar00rootroot00000000000000PNG  IHDR sBIT|d pHYs tEXtSoftwarewww.inkscape.org<?ٔ >}ϟھww{*pc\ǹ9q2'x,ظyM~RW,ԕR~97zf"< sӝx 1uQ~'C~?oIYDIĺ u[o /8{RA-\>e{loӘO^k{J{1_!u Qyیk }O!y:O]Y}oڨ8{T==KnU"D ӭFtn?>==ܕl{{7zQb(gMԼqt@kP;L>$wmOUvh!ڱhѝhZzD!}=d%R.M4)ԘϵLS)(wy艴,j>cmzs9n=,N=~OfS(Q#iACRdD.A)`'oF}9~)c!k^P1#Qϴ Ϩ+RiY u=Nw$Z#+CR*ٵy A3\,Y^usm4<-@I]#ԜFK IS$Q37̀pr2x+"߆; /Q+~^OE3gH{ΜB[N/):Bt2/N>:pӈ1/Ԑw-ҏةSd N7F%zR,} 0Zz)&!8IOZsk60_xH{=)>qM5"jCL1rlk[WHJ.mV%6}5cGcV`MO&!CM}YWN8!ft3Hq年.fs1;=#TJM1W R4)F,>;oIJ8]W$`7H'N<,gK?:9,+RO-,~h+q>2O\ gΜyxر"nCǦp{BjՄ<:ΰ;ͯ 575ǎ} Ɂig5@Sn1S&Φ}-9tLNN.<2== @1BD/ etuOOMzP7E}7=!=:i\looނ?G F˻'gL.~hZ>sHh!$hg{_>zte;yA3=υf#{ԤBUS裩=hZrh'A^Y=S!e; m 0HMn}uBڹRx/}-sr$_%Lxdj5Tz:<5e~6[4MhZzdӃ<*AtD4{Vb١%BjH㓓MM!|zфǎ7Z \|s#?EH %!}]!(brV%Yvd ;#!}2Kgq<81gONU_g1co4BccK*E-b1D--U67vՅzzdp9xהLAʉY4"vm!U4#vs-ߵET^esPzuZ+ѣG_1q(NMAJ“QRn.*!aMy/&'P8pd7D4t"iRH*/e7 ?`7 wݨbpz[%9@t'bfY IJB[֘br0\9!۷lTvpg!H{=OPFxZ1J$sbVvB, Bn!17CL.^ ^r^Q=7}W9vhى<:K)Rz!Rsgw.⧳UJJ/. uĄALKCUPoTc>x8N 67II.G"2IIIPݻQJ~>!f,mB%hjB{29F}xLPHp_ZĮSݪ+"K|t'38rf c < D`RSQ\f&jH(?K}@$$BB'y(%%PȐ\^46.N|f ( iW%$4)qq`Rq!܄ y110|>ʂldlL!DĬ߅}{@w7\H5)1b2+G^0g^PDKBJ. &#(6k|y<=^ 8g`q nAWLXA3&SA̡w"(HVdymsNLoj"NEqYYhqbƾSK_>I[IGLA#T@„&*x'>]%jR XOTWD2h^+^.oxak\?(1[uތŮKS( Ȯ@Ðѫ ng?Ev+2 1/m\4800D[H%2ގF ' Y9K-($EB !V 6 ;5dsU>b5͍9U3o{D 1aO&:hcR2%`k7޸k|CY9]uȹ2ƵzuDq792)Ku س #ךĘGSWSA'ڈz(N̡gpr 5إB)g:e]Sw'9W䃘eݳV2`ƃ&wV64H!Qڳ$\=#-޿'矧 3gCo)#"^+l n lpoGki ρ^E)$A zj{DC@=3/7H@I[ɹ>x {SBJƂg"sfK|K^ssȒ=}S[kU{(yt5!-ҒC-Lb[ŮCxխl%]rZs#5* ?䷘mMK[ >5 qZɠu>\4R},)sȒٓkFC:E]R'zMr,&ƽJSj؝l]!e w_{kjOgE^kL?ZhFHMw\Ư q3p(ҩfIKy̙Og$ӧNļ&< #%/rnM 3 2N%!*ݰu&-ɋGzT2$ElB((Ufr5TÚ.|2˴kIrmCܶNA2=}Ộu+~B꼵!/r1a"9HDSăl\n 9LO7Xr' d8{cjK?E~GSI%A!iHIWBY<i&?\=EU 6_Ϭ Ud\'I##xAc IL$_n7x\1LM=Qūnh%%hQRM5"]]':5J+ȣ[Rc=$-ԛ(!^6` A{\ bpŔQSƊI$C}{Qd Ԃa{u 0b\D&(#N; 9{]!{Mhm3Ay* ,^֜b>˸l9`*؏otM ؘK,GkXLNk܈rrl>iWT3dUѱ W R+a7n+Kz^Y״"c)ʩ5-Id\ d!@K4SvH~pz@dwt*Tb'cj2}JR j]6I$r~GSt'蹖vx78'eKi}=}kC{.1_nMWr1>D<Vf#Yo 8hSv޹e:{!{Xh IsZM1 /!XWw3Q1xw5eZ~ I"7 d3VޑX kk)^~\9GGW1Վ8bULy>z=z}gNB>vg' O@9E TSN, bB]֣yD*!`WVVefɹ6ԎDohyNpb}#>BNx($['"k;VNL RMcbYBGlXwx#!nvKLH5)xS.VZY'($$ B(WJ00 f,x_I21E"@osD F^F[L<Ԙ!F30JL +T)_b'h,֒2eݳR!1$2} n"8kHl f|̰o*LOOo TSWd`fS=}}?xsBB 9:륷ICN2 * TǽE6 G=7}*9>"yI D9 S~5xInq14ttYg(VI$ H b6Bb\Zśn6C*&DGQx): 9 \VuJU$ݮ..sݗ}_s?>[ʰY? ygƀ DL?IgD|T4tut1ۑ`i^k(;(*!)MA%:v=*JK(@ĆdRz`5Nk&16 ܔVQJ(Ax\8{n,]5!s /`,xbјxSs)uCgei#HĬimEd76B*q=0(H^]o9!R* &5Ѡ~'I3dI~5vA/`ZsqA3[StAjSՖ}0<,Yޞ#:XH1ڏ55[܂IdPwG EN)*!ɤԐWZC=MȽ^#J ^W#lܽllٸ`}_ X@Oʠ]%nƂQ%mQxzAs=Q=0j.fjAhh@ǘJ5N*kɑ\ڙ#sb`.|/G^ه!5 B9 vjJ)ë=뵎(HuQ~cp^:UoNQOvtJ5EN$&8lsjl RHC|TV_OHk.xV63 ~M)&v]rR.y!4~b!^4BRT Ѱ?K&隸Uʅqϗ~kí3ULŜR<t9ƍʉ$uL9{{hJ3 fC(v8iACxҗ9r'BKLuqm-!%X[RAڕm,kTmAN ;sjrNFQQoaP䘔Yur-(׈vQ I\9NT\C$Ismi*d?˄xnx(@[w= ySŒ)teO1<+>ub bp!JQ%7H%f(YM*lF *b]،آ^R!Yr!󈵒pvK(ؑHdjNZ$3r˭W>Љz{dz{{q/d>z#ׅʈBQ]HZa9,f~U*9-I~mmKf8Y=ґ'}]l"hϧ,ݫqH+Jj +mcA |׸6ؾ] SM.a$b˷v לszc)=YH0CN;m!ԳʩʫDWA/ jV׀Kn7CQ}YpK[d׷!feM%K*&g2fwGx`9qdbbu9*Wzα9l7{-pGn3)-eܑcefVɢBl$SF7]JɅԒ %XXMNnKw-b#=={{z@H&f70r!If=;u!dIUnUկ2KJb 2U@7$,2(B \D zFLu*׆Z'޻#?i/_?fΒq]J/vGw/!)bo6rԄ!uV[ڜ9bqpDrq4x0LLL@b%5$]}_ EI# iS)&+LɺR$p-!RPɌGmm܊mmBL.db"w9C;JQ U FW5W'JAj]![eE9(.R mըَ%nF`n6x[@@*f$ol%5c. B^&]Ebv!z2x!S!V.WVHEMNZ$?Dgt6db*MIEEI])}mpn DR'J%f|\DM!Z.Ud#25t1(pW:1|C-468s+_b&&#QRc4!B)ꯄ:S$ cKL(W3yBH?!3O)ё݈H(3Uז N 2rWT6Crmm%w211M P:QR6CO/ #mBBD[a3]}>,T{W@M eR))n5DCl( z 5#VVqBB$)&p3o=i[e%rrfnƢ40Rى2[PRS3ЂҚ[PfKu>:L% IȎB5݈۾BEPEU'!Ϯ>kUkcH>Xo,:v~Q"[IIC droBŵȅoT{B$h縳hHP9-.vrfe=\XYC$*tohB+J+t-l*[rE5+SlOA8ҐRHa;mW-U4Իw[xWd(fȰCٽ oLDz"Bnex${Ŷ[D,bDj U";ed]`cGAAy}GjY)/pm,O*jWUF"[WzIP3Nj lOEI =Hcݪ0{CGw-%Zق\2+cT6b~/+WmC"R!beVl(@x *Z2*2,b%9"䔕şrssT(QՍ%A6(-_ :Au'G}mAzqTbbmi(% q%o+4!ǘJ9~!8sm4Ჺ ?Ewy.1DMUS!( 1|E i>%(=DBNSI[m+r-"4Ф^b"{YiCpXjVYXJ"b%5R(Șa>1g0cJ{KET#e s^? /<'@$DoǴII.vk$|6])]A6d>҆EEzTώ^ Sccb[) HS k{qPIΙ9 gf՜ VYcRu,6UҨ!D-AMʨfG jjEqb J٢6rH6~8!6ThXBkA1rHa!.eS"6*i Ou͇/C)7T QhQ Yp-b;9nM-IT\*2!"Jဋ8d3B}ȉXsA$ 9,fHN,RDR`Ԫ#ZT~ת66Y?Ӑԋ) Ł>h3&PU+VuɆjA\ c7G'!;}n[\ gnl1OچČ3ALWH2h aU۬*o>؆Yl(cr)vB9}&f CEIv@C2#f,gˍqߴmBZ 9]Kul(A.ngC;B0>y!-ܬj/WN)|x D4MI/g/K6 ~fQ^D}NDфLmݬVu^fHʉEP"\\X7Е,Q݈LSg lK^KVrò 7vr[N ^}S]R.5ȵ iYWyG6nF̨xČMf=}KZN/ƈ&'r,3E*\l=6-咥mR)1K.G dbLJBL~2ҳh7x\" :;< yЊ׽E8B+ďf g$]t[ɢE߻HwzMXZB*#Ӊ}Z Rws]N"\9YFBd2 ^FxӼe& =)`qNzMw,QlkNԐRmN r[1:H1/?KY2ՓS3oEB`_07őL/gcb[ɶtx~}N1%.kFID7 Fs-3r霋ԗnXx˦DTH%_f^0/ٶhzjth2Sd.fVL]Iv[+GΈF4)ID$K;rr^z'h+!&b` %'E4"QŶDҀd3O#g\/ɛҭx2{[frnD+= kjFrU,:M% !q,!!S/Xe̵y'nhJx'ă>< hJyL(GZoF  /V53>+6k(u< 4pVvX 5(| FK&^ux %zL7OtNh n-9Ly5q ٿ6m0= JSr߶e B a[~`0-OK_HqBN$dR 5bJgn~S:OK0(Ad[A!@[_*-cJcJ-~)$_-ə%eTaR[өs~uJP(%գLš5}9nd>PDp9LGm"29lƂq'G6˱q-b#S,U6m`1!ᎹPWw_`>pL{+]@4r_WIp)$Id\'lk||t\6+P(~r/%842/[ $:/?eU`R!k $ӽ"/PH*}噲J'[)͍)tfuXYL-w;(O+IxJVý$10y676ؠ|uO\c$*Ŏ8#]l ^6KGC2"w?p(W',M)vBi 0W謍=x#K6m/}&)YCI[il[-\*ʹheYwku7-]rL ~5? tC66-s8*%hDL&GpGtTS$zR: ,ّnm lHe7I(ZLyEhg-B5Xo1x.SM/! hOGe&ZDf9|&?%Lr <&uOE@SW$N%*{|Й,,:b2y=W2iAI7X&T |0-S#]>~ (VTAeb闠wD(P@bÀᑒrۈl 8]`I(HN]B˹4gRR64a`RhXIZY43yG[k OyI=o^9%P pBZ20O|?H zA lńtdo%::DL7 & ~!ODZ"-sU)_VG29}5Be,2ixP1"a)Q*N=a*X^ ȼA %m?rIENDB`appstream-generator-0.8.6/data/templates/default/static/img/favicon.png000066400000000000000000000101311416066706400262440ustar00rootroot00000000000000PNG  IHDR00WbKGD pHYs tIME 9}IDATh͚yUu?;!/sBhC(# ,"˶J:DYjKavYD*ZDF1U)$( C< wtRe[wsg߽Ne_>L7d+Z_f8}޵T럪,w<-~ JVu gbY(TJcT iAgr /sJW5.V8@UU^I*~zsW|RFDDUwc#Dշ; iJբE$Isc 0P(P&ʀؑx`$Nﶰ.aY't3c7v}N6mϘ/ AZGd5Ʃ2^208&u,9 "G. ,ctwujٚպ"-S Y^}w-[i&">O^g׾}l"̈"RBbDu?y\|RD14[RD>Pmuhb"2+i^AN}Fyznno:@,U^s4FU0p",@}rrOP |VM1^t݆&Sc{l*gYf /pjRpQ$= ēccIwͲyȱ 0 wՂz>g2M LgR9)l--J-'ƴ?ۊj6J6DqYdnڵZZztM4e`p,䛝eCCnhDѽs5ᑑ CCygx N? 'Hwfv "5Zz3ñW1atm;}kr ƘyS&uF'&@Ģ 7ݴxiG=c>gSbVtRuz˃O:_ c~X=~=wwxڙ9H-xu:/NLZ<^1c?=,XI\ߊ%QHL#cce۶EY}b霹7[_d~ "ABP* TIrJtm1m߾$˲llΝ$IKW,EDNJ]+'-/1ƄkX8y UU]h5SoEFc(oj:W(`DQĊ)- K{{,1oW7Q[Y#Tc9 T92k2ɡ=z(řE9p$MyѲeÐ(QyҰs|k [m\mspnCH3$!91*=o~ |.4:3@G)`8>Z!:Dex1a;En:c&O/k:޶۳cazqf;v(6= S@U}MaA`lVWi8I84x*0dڅNsh&&pcULwCHTe yޞy DV*cNLPK2~=\Fj- XUrg-W~+.U^ t51QT޳{uۆ^#A+ejC'<٧Pݺ# JbǀtZe:v71l/Mso]k3rfj;_D>%Q8Ř|ƪo^ U~)q?{{ǏikoitQWLoWc*˺4ǡj%<_?rM5.W3q\OPM 5ƛCni(Zks)1m` t+z] $i\P-LRR=zzǬ+D9͈sl^rqz˛߽_ v^N9%s&d& msR [EjLDl,ǭֽΘwa ըHF݁ϢgqFt[n`Ĭ!|}YV f766[`m;1ڜgSmwQ$D#Y(/L}9 ᛌKK}֢hS([fNDwS<Ү#MV?29*t,6yLxGƥ4}f58MOw}w}4!{1gzǜU ͲQSApQL3pDXɌnM6-!3n2pΎ:O6X%Hj ߵ CYuQ3Z7kߡbzŘ25shc-\ksߟdAr> Y]6fݪZuIvGƍltx`sSu6Yc>{mg˥&,HE20@ y\B$(`rH|`ƑK;fK'6^JXZe"Ԍ ɝмypY~pvfT[h+%鯲$zjolΚ~ПSvaX|[fc+]9L)ISmx&?Zw7ݰnol"\yC9Hƙ.M ;4l﵎ iNGyRFe# Ή5LGg . c9+/W/nm+FJ 䑩)Ui_p?ciduUӕulש)SNN>Klt#ⷹLqVSlRۙIENDB`appstream-generator-0.8.6/data/templates/default/static/img/no-image.png000066400000000000000000000017521416066706400263240ustar00rootroot00000000000000PNG  IHDR@@iqbKGD pHYs P%tIME6jǠOOϠOMJ(rx)E,-ڔ+=IxI! ^2)!Ps `9s"@"wY:JO40|(DKBFǦ^3I=UmBl|F*E^3s/o?FW: ߤ׈pE.f ]_p~U!k7U/c(#'&f5 ɱo"ޖ0 P3A20wE 0pBlS+"P G`9pM/٬60.P |EInY pV3!7ׇͰm³;tMC|@X7%9]`Җ׏'l$Qn`2M+ǣU 4sLTV(cnCi%+" Jma? |S>$WI2KOXk8(}" "$E EȩPǿ OYB-3W '@Jc1NğYCjUi,hjO]I$^A~C<Aq4ha_#G|< QOU)#r {X:a-|m+!qU8i;[-x-xl-U)b)hfY͢_)"!dƒ}IENDB`appstream-generator-0.8.6/data/templates/ubuntu000077700000000000000000000000001416066706400230332debianustar00rootroot00000000000000appstream-generator-0.8.6/docs/000077500000000000000000000000001416066706400164475ustar00rootroot00000000000000appstream-generator-0.8.6/docs/appstream-generator.1.xml000066400000000000000000000170421416066706400233140ustar00rootroot00000000000000 18 April,2016"> GNU"> GPL"> ]> appstream-generator 2016-2018 Matthias Klumpp AppStream Generator &date; appstream-generator 1 &package; Generate AppStream metadata from distribution repositories &package; Description This manual page documents briefly the &package; command. &package; generates AppStream metadata from the repositories of a software distribution. It currently supports the following repository formats / distributions: Debian, Ubuntu, Arch Linux, RPM-MD (Fedora, Mageia). The generator will produce AppStream collection metadata files in the AppStream YAML or XML format to be shipped to users, as well as detailed HTML reports about found components and HTML and JSON reports on issues found while compiling the metadata. It reads .desktop files as well as metainfo files, renders fonts, scales images, caches screenshots etc. to produce high-quality metadata for AppStream based software centers and other tools to consume. Usually, &package; is integrated with the existing software build & delivery workflow of a distribution. The &package; tool is based on the libappstream library for metadata conversion and analysis. If you just want to embed AppStream metadata processing into another tool, using libappstream directly is likely a better choice. The generator tool does some heavy lifting like rendering fonts and scaling images, which might not be necessary for simple cases. To use &package;, a asgen-config.json file is required. Its format is described in detail in the asgen-config.json documentation. The generator supports a wide range of features that can individually configured to fit the needs of different projects and adjust the generated metadata to specific use cases. Refer to the configuration file documentation for information on what options are available. For more information about the AppStream project and the other components which are part of it, take a look at the AppStream pages at Freedesktop.org. Options Process new metadata for the given distribution suite and publish it. Cleanup old/expired metadata and media files from the cache and directories. It is recommended to run this command every week, or at least every month, depending on how many changes happen in the software repository. Export all metadata and publish reports in the export directories. You usually do not want to run this task explicitly, because it is already automatically performed by the command. Drop all valid processed metadata and hints from the selected suite. Drop all information we have about this (partial) package-id. A package-id consists of a name/version/arch triplet. For this command, the version and architecture can be omitted to forget all packages that match a particular name or name-version combination. Show information associated with this (full) package-id. A package-id consists of a name/version/arch triplet. Define the workspace location. If this flag is omitted, and no workspace directory is given in the generator configuration file, the current directory is assumed as the workspace location. This parameter, if given, overrides any workspace location defined elsewhere. Define a configuration file. Explicitly set a generator configuration JSON file. If this flag is omitted, the asgen-config.json file in the current workspace directory is used. If no workspace directory is defined in the configuration file itself, the directory it is located in is used as workspace. This can be overridden by defining a workspace explicitly with . Enforce the command. Show extra debugging information. Display the version number of &package;. See Also appstreamcli (1). AUTHOR This manual page was written by Matthias Klumpp matthias@tenstral.net. appstream-generator-0.8.6/docs/asgen-config.md000066400000000000000000000253351416066706400213410ustar00rootroot00000000000000# Generator Project Configuration This document describes the options and fields which can be set in an `asgen-config.json` file. ## JSON file example An example `asgen-config.json` file may look like this: ```JSON { "ProjectName": "Tanglu", "ArchiveRoot": "/srv/archive.tanglu.org/tanglu/", "MediaBaseUrl": "http://metadata.tanglu.org/appstream/media", "HtmlBaseUrl": "http://metadata.tanglu.org/appstream/", "Backend": "debian", "Features": { "validateMetainfo": true }, "Suites": { "chromodoris": { "sections": ["main", "contrib"], "architectures": ["amd64", "i386"] }, "chromodoris-updates": { "dataPriority": 10, "baseSuite": "chromodoris", "sections": ["main", "contrib"], "architectures": ["amd64", "i386"] } }, "Icons": { "64x64": {"cached": true, "remote": false}, "128x128": {"cached": false, "remote": true} } } ``` ## Description of fields ### Toplevel fields Key | Comment ------------ | ------------- ProjectName | The name of your project or distribution which ships AppStream metadata. Backend | The backend that should be used to obtain the raw data. Defaults to `debian` if not set. MetadataType | The type of the resulting AppStream metadata. Can be one of `YAML` or `XML`. If omitted, the backend's default value is used. ArchiveRoot | A local URL to the mirror of your archive, containing the dists/ and pool/ directories MediaBaseUrl | The http or https URL which should be used in the generated metadata to fetch media like screenshots or icons HtmlBaseUrl | The http or https URL to the web location where the HTML hints will be published. (This setting is optional, but recommended) Oldsuites | This key exists to support migration from an alternative appstream generator. Given a list of suite names, the output HTML will link to `suitename/index.html`. Suites | Suites which should be recognized by the generator. Each suite has the components and architectures which should be searched for metadata as children. See below for more information. Features | Disable or enable selected generator features. For a detailed description see below. CAInfo | Set the CA certificate bundle file to use for SSL peer verification. If this is not set, the generator will use the system default. AllowedCustomKeys | Set which keys of the tag are allowed to be propagated to the collection metadata output. This key takes a list of custom-key strings as value. ExportDirs | Set where to export data. The dictionary requires full paths set for the "Media", "Data", "Hints" or "Html" key. In case a value is missing, the default locations are used. ExtraMetainfoDir | Path to a directory where additional injected metainfo files are located. If not set, the `extra-metainfo` directory in the project workspace is used. WorkspaceDir | Explicitly set the location of the workspace. Only makes sense if the generator is meant to be used with a lot of configuration files and the configuration is passed to it via the `-c` flag. Icons | Customize the icon policy. See below for more details. MaxScreenshotFileSize | The maximum size of downloaded screenshot image or video files in MiB. `0` means unlimited. *Default: `14`* ### Suite fields The `Suites` field contains a dictionary of the suites which should be processed as value. These suites can contain a selection of properties: Key | Comment ------------ | ------------- sections | A list of sections the suite possesses. The "sections" are also known as archive components in the Debian world. *(required)* architectures | A list of architectures which should be processed for this suite. *(required)* baseSuite | An optional base suite name which should be used in addition to the child suite to resolve icons (only the `main` section of that suite is considered). dataPriority | An integer value representing the priority the data generated for this suite should have. Metadata with a higher priority will override existing data (think of an `-updates` suite wanting to override data shipped with the base suite). If this is not set, AppStream client tools will assume the priority being `0`. useIconTheme | Set a specific icon theme name with highest priority for this suite. This is useful if you want a different default icon theme providing icons for generic icon names (by default, the default themes of KDE and GNOME are used). immutable | If set to `true`, the state of the metadata files and exported data will be frozen, and no more changes to the data for this suite will be allowed. This only works if the `immutableSuites` feature is enabled. ### Enabling and disabling features Several features of the metadata generator can be toggled to make it work in different scenarios. The following feature values are recognized, and can be enabled or disabled in the JSON document. If no explicit value is set for a feature, the generator will pick its default value, which is sane in most cases. Name | Comment ------------ | ------------- validateMetainfo | Validate the AppStream upstream metadata. The validation is slow, but will produce better feedback and issue hints if enabled. *Default: `ON`* processDesktop | Process .desktop files which do not have a metainfo file. If disabled, all data without metainfo file will be ignored. *Default: `ON`* noDownloads | Do not attempt any downloads. This will implicitly disable any handling of screenshots and possibly other features. Using this flag is discouraged. *Default: `OFF`* createScreenshotsStore | Mirror screenshots and create thumbnails of them in `media/`. This will yield the best experience with software-centers, and also allow full control over which screenshots are displayed. Disabling this will make clients pull screenshots from 3rd-party upstream servers. *Default: `ON`* optimizePNGSize | Use `optipng` to reduce the size of PNG images. Optipng needs to be installed. *Default: `ON`* metadataTimestamps | Write timestamps into generated metadata files. *Default: `ON`* immutableSuites | Allow suites to be marked as immutable. This is useful for distributions with fixed releases, but not for rolling release distributions or continuously updated repositories. *Default: `ON`* processFonts | Include font metadata and render fonts. *Default: `ON`* allowIconUpscaling | Allows upscaling of small 48x48px icons to 64x64px to make applications show up. Icons are only upscaled as a last resort. *Default: `ON`* processGStreamer | Synthesise `type=codec` metadata from available GStreamer packages. Requires support in the backend, currently only implemented for Debian. *Default: `ON`* processLocale | Try to extract the software's localization status from Gettext data. *Default: `ON`* screenshotVideos | Permit videos in screenshots and cache them if downloads are permitted. *Default: `ON`* propagateMetaInfoArtifacts | Release artifact information is filtered out by default if a package is set for the selected metadata. Set this flag to propagate artifact information unconditionally. *Default: `OFF`* ### Configuring icon policies The `Icons` field allows to customize the icon policy used for a generator run. It decides which icon sizes are extracted, and whether they are stored as cached icon, remote icons or both. The field contains a dictionary with icon sizes as keys. Valid icon sizes are `48x48`, `64x64` and `128x128` and their HiDPI variants (e.g. `64x64@2`). The values for the icon-size keys are dictionaries with two boolean keys, `cached` and `remote`, to select the storage method for the icon size. Cached means an icon tarball is generated for the icon size that can be made available locally, while remote means the icon can be downloaded on-demand by the software center and no local cache of all icons exists. Icon sizes not mentioned, or with both `cached` and `remote` set to `false` will not be extracted. The `64x64` icon size must always be present and be cached. If this is not the case, appstream-generator will adjust the configuration internally and emit a warning. If no `Icons` field is present, appstream-generator will use a default policy for icons (creating cache tarballs for all sizes, and remote links for sizes >= 129x128px). ## Injecting extra metainfo / removing components Sometimes injecting metainfo files directly into the generation process instead of packaging them makes sense. This can be done for example for `web-application` components, `operating-system` components or for components which are merged into others. It is discouraged to use this feature for any components that are directly tied to a package. Metainfo XML files can be placed in `%ExtraMetainfoDir%/suite/section/(arch)`, where `%ExtraMetainfoDir%` usually is the `extra-metainfo` directory in the generator's current workspace, unless this default is overriden in the configuration file. If the `arch` part of the path is omitted, a metainfo file is added to all active architectures. E.g. a XML file placed in `%ExtraMetainfoDir%/buster/main/` will apply to all active architectures for `buster`, while files in `%ExtraMetainfoDir%/buster/main/amd64/` will only apply to the `amd64` architecture. Additionally to metainfo XML files, a `removed-components.json` file may be placed in the extra-metainfo directories. This JSON file contains a list of component IDs that should be removed from the metadata pool on client machines. By listing IDs in this file, the generator will create a special component that will trigger a component with the same ID to be removed from the metadata pool if its priority is lower. This feature is useful for overlay suites, for example if the `buster-updates` suite (with priority 10) wants to completely remove a component from the `buster` suite (with priority 0) from the user's eyes. This may be necessary if a component improperly changes its ID, if you are maintaining an overlay distribution and can not control the composition of packages in the base suites, or in case of legal issues where a component has to be removed retroactively from a frozen distribution suite. It may also be useful to hide `web-application` components in a stable distribution in case their service is discontinued before the distribution release is out of support as well. Internally, injected metainfo data is grouped under a special fake package name, in order to allow the generator to properly record hints for the added components and to associate the data with the right suite(s). ## Minimal configuration file A minimal configuration file can look like this: ```JSON { "ProjectName": "Tanglu", "ArchiveRoot": "/srv/archive.tanglu.org/tanglu/", "MediaBaseUrl": "http://metadata.tanglu.org/appstream/media", "HtmlBaseUrl": "http://metadata.tanglu.org/appstream/", "Backend": "debian", "Suites": { "chromodoris": { "sections": ["main", "contrib"], "architectures": ["amd64", "i386"] } } } ``` appstream-generator-0.8.6/docs/index.md000066400000000000000000000002271416066706400201010ustar00rootroot00000000000000# AppStream Generator Docs For general information check out the [README file](../README.md) See [usage.md](usage.md) for general usage information. appstream-generator-0.8.6/docs/meson.build000066400000000000000000000012051416066706400206070ustar00rootroot00000000000000# Meson definition for AppStream Generator Documentation # make manual page xsltproc = find_program('xsltproc') custom_target('man-asgen', input: 'appstream-generator.1.xml', output: 'appstream-generator.1', install: true, install_dir: join_paths(get_option('mandir'), 'man1'), command: [ xsltproc, '--nonet', '--stringparam', 'man.output.quietly', '1', '--stringparam', 'funcsynopsis.style', 'ansi', '--stringparam', 'man.th.extra1.suppress', '1', '-o', '@OUTPUT@', 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl', '@INPUT@' ] ) appstream-generator-0.8.6/docs/usage.md000066400000000000000000000045651416066706400201070ustar00rootroot00000000000000# AppStream Generator Usage ## How to use ### Generating distro metadata To generate AppStream distribution metadata for your repository, create a local mirror of the repository first. Then create a new folder, and write a `asgen-config.json` configuration file for the metadata generator. Details on the file and an example can be found in [the asgen-config docs](asgen-config.md). After the config file has been written, you can generate the metadata as follows: ```Bash cd /srv/asgen/workspace # path where the asgen-config.json file is located appstream-generator process chromodoris # replace "chromodoris" with the name of the suite you want to analyze ``` The generator is assuming you have enough memory and disk space on your machine to cache stuff. Resulting metadata will be placed in `export/data/`, machine-readable issue-hints can be found in `export/hints/` and the processed screenshots and icons are located in `export/media/`. In order to drop old packages and cruft from the databases, you should run ```Bash appstream-generator cleanup ``` every once in a while. This will drop all superseded packages and data from the caches. If you do not want to `cd` into the workspace directory, you can also use the `--workspace|-w` flag to define a workspace. ### Validating metadata You can validate the resulting metadata using the AppStream client tools. Use `appstreamcli validate .xml.gz` for XML metadata, and `dep11-validate .yml.gz` for YAML. This will check the files for mistakes and compliance with the specification. Keep in mind that the generator will always generate spec-compliant metadata, but might - depending on the input - produce data which has smaller flaws (e.g. formatting issues in the long descriptions). In these cases, issue-hints will have been emitted, so the package maintainers can address the metadata issues. ## Troubleshooting ### Memory Usage The `appstream-generator` will not hesitate to use RAM, and also decide to use lots of it if enough is available. This is especially true when scanning new packages for their contents and storing the information in the LMDB database. Ideally make sure that you are running a 64bit system with at least 4GB of RAM if you want to use the generator properly. For the generator, speed matters more than RAM usage. You can use cgroups to limit the amount of memory the generator uses. ### Profiling TODO appstream-generator-0.8.6/meson.build000066400000000000000000000043361416066706400176670ustar00rootroot00000000000000project('AppStream Generator', 'd', meson_version : '>=0.46', subproject_dir : 'contrib/subprojects', license : 'LGPL-3.0+', version : '0.8.6' ) asgen_version = meson.project_version() source_root = meson.source_root() build_root = meson.build_root() # # Dependencies # src_dir = include_directories('src/') glib_dep = dependency('glib-2.0', version: '>= 2.62') glibd_dep = dependency('glibd-2.0') appstream_dep = dependency('appstream', version : '>= 0.14.6') ascompose_dep = dependency('appstream-compose', version : '>= 0.15.1') lmdb_dep = dependency('lmdb', version : '>= 0.9.22') archive_dep = dependency('libarchive', version : '>= 3.2') curl_dep = dependency('libcurl') # # Build interfaces from GIR # gir_to_d_prog = find_program('girtod') gir_wrap_dir = source_root + '/contrib/girwrap/' gir_d_intf_dir = build_root + '/girepo/' message('Generating AppStream D interfaces from GIR...') girtod_gen = run_command(gir_to_d_prog, '-i', gir_wrap_dir, '-o', gir_d_intf_dir, '--print-files', 'relative,' + source_root) if girtod_gen.returncode() != 0 error('Unable to build D interfaces from GIR:\n' + girtod_gen.stderr()) endif gir_bind_dir = include_directories('girepo') gir_binding_sources = girtod_gen.stdout().strip().split('\n') # static library of bindings automatically generated from GIR girbind_lib = static_library('girbindings', [gir_binding_sources], include_directories: [gir_bind_dir], dependencies: [glibd_dep] ) # # Download JS stuff and additional sources if we couldn't find them # if get_option('download-js') yarn_exe = find_program('/usr/share/yarn/bin/yarn', 'yarnpkg') # check for the yarn executable message('Downloading JavaScript libraries...') if run_command('[', '-d', 'data/templates/default/static/js', ']').returncode() != 0 getjs_cmd = run_command([source_root + '/contrib/setup/build_js.sh', yarn_exe]) if getjs_cmd.returncode() != 0 error('Unable to download JavaScript files with Yarn:\n' + getjs_cmd.stdout() + getjs_cmd.stderr()) endif endif endif # asgen sources subdir('src') # documentation subdir('docs') #data subdir('data') appstream-generator-0.8.6/meson_options.txt000066400000000000000000000003001416066706400211450ustar00rootroot00000000000000# # Options for AppStream Generator # option('download-js', type: 'boolean', value: true, description: 'Whether we should download JavaScript with Yarn automatically.' ) appstream-generator-0.8.6/snapcraft.yaml000066400000000000000000000156601416066706400203740ustar00rootroot00000000000000# # Copyright (C) 2020 Matthias Klumpp # Copyright (C) 2020 Canonical Ltd # Author: Iain Lane # # SPDX-License-Identifier: FSFAP # # This Snapcraft file and the resulting Snap is NOT OFFICIALLY SUPPORTED. # If you can, please use a provided native build for your distribution. # Patches to improve this file are very welcome though, and so is testing the Snap! # # This snap is strict, but it has an important limitation: # - it needs read/write access to wherever the user puts the workspace # definition (could be anywhere, but most commonly it's in /srv, in /home or # in /var), access to a local archive mount (can be in /mnt), and that should # be it (unless optipng and ffprobe have extra requirements). # # Until that is overcome, this snap should be installed using `--devmode` to # effectively disable the confinement. # # This Snap is autobuilt, you can check its status at https://build.snapcraft.io/user/ximion/appstream-generator name: appstream-generator license: LGPL-3.0 base: core20 adopt-info: appstream-generator confinement: strict grade: stable architectures: - build-on: [amd64] run-on: [amd64] apps: appstream-generator: command: usr/bin/appstream-generator common-id: org.freedesktop.appstream.generator plugs: - network - home environment: GSETTINGS_SCHEMA_DIR: "$SNAP/usr/share/glib-2.0/schemas" GIO_EXTRA_MODULES: "$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/gio/modules" layout: /usr/share/appstream: bind: $SNAP/usr/share/appstream /usr/lib/$SNAPCRAFT_ARCH_TRIPLET/gdk-pixbuf-2.0: bind: $SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/gdk-pixbuf-2.0 /usr/share/i18n: bind: $SNAP/usr/share/i18n /usr/share/mime: bind: $SNAP/usr/share/mime parts: appstream-generator: source: . source-type: git parse-info: [usr/share/metainfo/org.freedesktop.appstream.generator.metainfo.xml] override-pull: | snapcraftctl pull # set version from Git snapcraftctl set-version "$(git describe --always | sed -e 's/v//;s/-/+git/;y/-/./')" # adjust to an absolute path to help finding the GIR file from the AppStream part sed -i 's|AppStream-1.0.gir|$SNAPCRAFT_STAGE/usr/share/gir-1.0/AppStream-1.0.gir|g' ${SNAPCRAFT_PART_SRC}/contrib/girwrap/APILookupAppStream.txt sed -i 's|AppStreamCompose-1.0.gir|$SNAPCRAFT_STAGE/usr/share/gir-1.0/AppStreamCompose-1.0.gir|g' ${SNAPCRAFT_PART_SRC}/contrib/girwrap/APILookupAppStreamCompose.txt plugin: meson build-environment: - LD_LIBRARY_PATH: $SNAPCRAFT_STAGE/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/ override-build: | # install yarn curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list apt-get update && apt-get install --no-install-recommends -y yarn nodejs # actually build asgen - we need to run everything manually here, # because snapcraft will kill the build if run with maximum amount of ninja jobs, # and I found no way to limit the amount of permitted ninja jobs other than overriding everything meson --prefix=/usr --buildtype=debugoptimized -Ddownload-js=true ${SNAPCRAFT_PART_BUILD} ${SNAPCRAFT_PART_SRC} ninja -C "${SNAPCRAFT_PART_BUILD}" -j4 meson test -C "${SNAPCRAFT_PART_BUILD}" --verbose DESTDIR=${SNAPCRAFT_PART_INSTALL} ninja -C "${SNAPCRAFT_PART_BUILD}" install override-prime: | set -eux snapcraftctl prime for dir in usr/lib/*/gdk-pixbuf-2.0; do if [ -d "${dir}" ]; then for subdir in "${dir}"/*; do if [ -d "${subdir}" ] && [ -d "${subdir}/loaders" ]; then GDK_PIXBUF_MODULEDIR="$(pwd)/${subdir}/loaders" "${dir}/gdk-pixbuf-query-loaders" > "${subdir}/loaders.cache" sed -i "s,$(pwd),," "${subdir}/loaders.cache" fi done fi done update-mime-database -V usr/share/mime/ usr/lib/${SNAPCRAFT_ARCH_TRIPLET}/glib-2.0/glib-compile-schemas usr/share/glib-2.0/schemas build-packages: - curl - meson - docbook-xsl - docbook-xml - xsltproc - ffmpeg - libgdk-pixbuf2.0-dev - libarchive-dev - librsvg2-dev - liblmdb-dev - libglib2.0-dev - libcurl4-gnutls-dev - libcairo2-dev - libfreetype6-dev - libfontconfig1-dev - libpango1.0-dev stage-packages: - gdb - glib-networking - ffmpeg - optipng - libc-bin - libglu1-mesa - freeglut3 - libslang2 - libarchive13 - libcairo2 - libcroco3 - libdatrie1 - libfontconfig1 - libfreetype6 - libgdk-pixbuf2.0-0 - libglib2.0-0 - libglib2.0-bin - libgraphite2-3 - libharfbuzz0b - libpango-1.0-0 - libpangocairo-1.0-0 - libpangoft2-1.0-0 - libpixman-1-0 - libpng16-16 - librsvg2-2 - librsvg2-common - libcurl3-gnutls - libthai0 - libx11-6 - libxau6 - libxcb-render0 - libxcb-shm0 - libxcb1 - libxdmcp6 - libxext6 - libxrender1 - locales - locales-all - shared-mime-info after: - appstream - ldc - gir-to-d - glibd appstream: source: https://github.com/ximion/appstream.git source-type: git source-tag: v0.15.1 plugin: meson meson-parameters: - --prefix=/usr - --buildtype=debugoptimized - -Dcompose=true - -Dapidocs=false build-packages: - build-essential - docbook-xsl - docbook-xml - gettext - gobject-introspection - gperf - itstool - libgirepository1.0-dev - libglib2.0-dev - libxmlb-dev - liblzma-dev - libcurl4-gnutls-dev - libstemmer-dev - libxml2-dev - libyaml-dev - meson - xsltproc stage-packages: - libicu66 - liblmdb0 - libcurl3-gnutls - libstemmer0d - libxml2 gir-to-d: source: https://github.com/gtkd-developers/gir-to-d.git source-type: git source-tag: v0.22.0 plugin: meson meson-parameters: - --prefix=/usr - --buildtype=release build-packages: - meson after: - ldc glibd: source: https://github.com/gtkd-developers/GlibD.git source-type: git source-tag: v2.3.0 plugin: meson meson-parameters: - --prefix=/usr - --buildtype=release build-packages: - meson - gobject-introspection - libgirepository1.0-dev - libglib2.0-dev after: - gir-to-d - ldc ldc: plugin: dump source: https://github.com/ldc-developers/ldc/releases/download/v1.28.0/ldc2-1.28.0-linux-x86_64.tar.xz override-build: | # link shared by default sed -i '/\sswitches =/a \ "-link-defaultlib-shared",' etc/ldc2.conf # remove unneeded libs rm -rf lib32 snapcraftctl build appstream-generator-0.8.6/src/000077500000000000000000000000001416066706400163065ustar00rootroot00000000000000appstream-generator-0.8.6/src/app.d000066400000000000000000000161311416066706400172350ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ import std.stdio; import std.path : buildPath; import std.file : getcwd; import std.getopt; import std.string : format; import std.array : empty; import core.stdc.stdlib : exit; import asgen.logging; import asgen.config; import asgen.engine; import asgen.defines : ASGEN_VERSION; private immutable helpText = "Usage: appstream-generator [OPTION...] - AppStream Generator. AppStream Metadata Generator Subcommands: run SUITE [SECTION] - Process new metadata for the given distribution suite and publish it. cleanup - Cleanup old metadata and media files. publish SUITE [SECTION] - Export all metadata and publish reports in the export directories. remove-found SUITE - Drop all valid processed metadata and hints. forget PKID - Drop all information we have about this (partial) package-id. info PKID - Show information associated with this (full) package-id. Help Options: -h, --help Show help options Application Options: --version Show the program version. --verbose Show extra debugging information. --force Force action. -w|--workspace Define the workspace location. -c|--config Use the given configuration file. --export-dir Override the workspace root export directory."; version (unittest) { void main () {} } else { private void createXdgRuntimeDir () { // Ubuntu's Snappy package manager doesn't create the runtime // data dir, and some of the libraries we depend on expect it // to be available. Try to create the directory import std.process : environment; import std.array : empty; import std.file : exists, mkdirRecurse, setAttributes; import std.algorithm : startsWith; import std.conv : octal; immutable xdgRuntimeDir = environment.get("XDG_RUNTIME_DIR"); if (xdgRuntimeDir.empty || !xdgRuntimeDir.startsWith ("/")) return; // nothing to do here if (xdgRuntimeDir.exists) return; // directory already exists try { mkdirRecurse (xdgRuntimeDir); xdgRuntimeDir.setAttributes(octal!700); } catch (Exception e) { logDebug ("Unable to create XDG runtime dir: %s", e.msg); return; } logDebug ("Created missing XDG runtime dir: %s", xdgRuntimeDir); } void main(string[] args) { string command; bool verbose; bool showHelp; bool showVersion; bool forceAction; string wdir; string exportDir; string configFname; // parse command-line options try { getopt (args, "help|h", &showHelp, "verbose", &verbose, "version", &showVersion, "force", &forceAction, "workspace|w", &wdir, "config|c", &configFname, "export-dir", &exportDir); } catch (Exception e) { writeln ("Unable to parse parameters: ", e.msg); exit (1); } if (showHelp) { writeln (helpText); return; } if (showVersion) { writeln ("Generator version: ", ASGEN_VERSION); return; } if (args.length < 2) { writeln ("No subcommand specified!"); return; } // globally enable verbose mode, if requested if (verbose) asgen.logging.setVerbose (true); auto conf = Config.get (); if (configFname.empty) { // if we don't have an explicit config file set, and also no // workspace, take the current directory if (wdir.empty) wdir = getcwd (); configFname = buildPath (wdir, "asgen-config.json"); } try { conf.loadFromFile (configFname, wdir, exportDir); } catch (Exception e) { writefln ("Unable to load configuration: %s", e.msg); exit (4); } scope (exit) { // ensure we clean up when the generator is done import std.file : rmdirRecurse, exists; if (conf.getTmpDir.exists) rmdirRecurse (conf.getTmpDir ()); } // ensure runtime dir exists, in case we are installed with Snappy createXdgRuntimeDir (); auto engine = new Engine (); engine.forced = forceAction; void ensureSuiteAndOrSectionParameterSet () { if (args.length < 3) { writeln ("Invalid number of parameters: You need to specify at least a suite name."); exit (1); } if (args.length > 4) { writeln ("Invalid number of parameters: You need to specify a suite name and (optionally) a section name."); exit (1); } } command = args[1]; switch (command) { case "run": case "process": ensureSuiteAndOrSectionParameterSet (); if (args.length == 3) engine.run (args[2]); else engine.run (args[2], args[3]); break; case "process-file": if (args.length < 5) { writeln ("Invalid number of parameters: You need to specify a suite name, a section name and at least one file to process."); exit (1); } engine.processFile (args[2], args[3], args[4..$]); break; case "publish": ensureSuiteAndOrSectionParameterSet (); if (args.length == 3) engine.publish (args[2]); else engine.publish (args[2], args[3]); break; case "cleanup": engine.runCleanup (); break; case "remove-found": if (args.length != 3) { writeln ("Invalid number of parameters: You need to specify a suite name."); exit (1); } engine.removeHintsComponents (args[2]); break; case "forget": if (args.length != 3) { writeln ("Invalid number of parameters: You need to specify a package-id (partial IDs are allowed)."); exit (1); } engine.forgetPackage (args[2]); break; case "info": if (args.length != 3) { writeln ("Invalid number of parameters: You need to specify a package-id."); exit (1); } engine.printPackageInfo (args[2]); break; default: writeln (format ("The command '%s' is unknown.", command)); exit (1); } } } appstream-generator-0.8.6/src/asgen/000077500000000000000000000000001416066706400174035ustar00rootroot00000000000000appstream-generator-0.8.6/src/asgen/backends/000077500000000000000000000000001416066706400211555ustar00rootroot00000000000000appstream-generator-0.8.6/src/asgen/backends/alpinelinux/000077500000000000000000000000001416066706400235055ustar00rootroot00000000000000appstream-generator-0.8.6/src/asgen/backends/alpinelinux/apkindexutils.d000066400000000000000000000101351416066706400265360ustar00rootroot00000000000000module asgen.backends.alpinelinux.apkindexutils; import std.algorithm : remove; import std.algorithm.iteration : map; import std.algorithm.searching : canFind; import std.array : join, split; import std.file : exists; import std.format : format; import std.path : buildPath; import std.range : empty, InputRange, isForwardRange; import std.string : splitLines, startsWith, strip; import std.utf : validate; import std.stdio; import asgen.backends.alpinelinux.apkpkg; import asgen.downloader : Downloader, DownloadException; import asgen.logging : logDebug; import asgen.utils : isRemote; /** * Struct representing a block inside of an APKINDEX. Each block, seperated by * a newline, contains information about exactly one package. */ struct ApkIndexBlock { string arch; string maintainer; string pkgname; string pkgversion; string pkgdesc; @property string archiveName () { return format ("%s-%s.apk", this.pkgname, this.pkgversion); } } /** * Range for looping over the contents of an APKINDEX, block by block. */ struct ApkIndexBlockRange { this (string contents) { this.lines = contents.splitLines; this.getNextBlock(); } @property ApkIndexBlock front () const { return this.currentBlock; } @property bool empty () { return this.m_empty; } void popFront () { this.getNextBlock (); } @property ApkIndexBlockRange save () { return this; } private: void getNextBlock () { string[] completePair; uint iterations = 0; currentBlock = ApkIndexBlock(); foreach (currentLine; this.lines[this.lineDelta .. $]) { iterations++; if (currentLine == "") { // next block for next package started break; } if (currentLine.canFind (":")) { if (completePair.empty) { completePair = [currentLine]; continue; } auto pair = completePair.join (" ").split (":"); this.setCurrentBlock (pair[0], pair[1]); completePair = [currentLine]; } else { completePair ~= currentLine.strip (); } } this.lineDelta += iterations; this.m_empty = this.lineDelta == this.lines.length; } void setCurrentBlock (string key, string value) { switch (key) { case "A": this.currentBlock.arch = value; break; case "m": this.currentBlock.maintainer = value; break; case "P": this.currentBlock.pkgname = value; break; case "T": this.currentBlock.pkgdesc = value; break; case "V": this.currentBlock.pkgversion = value; break; default: // We dont care about other keys break; } } string[] lines; ApkIndexBlock currentBlock; bool m_empty; uint lineDelta; } static assert (isForwardRange!ApkIndexBlockRange); /** * Download apkindex if required. Returns the path to the local copy of the APKINDEX. */ immutable (string) downloadIfNecessary (const string prefix, const string destPrefix, const string srcFileName, const string destFileName) { auto downloader = Downloader.get; immutable filePath = buildPath (prefix, srcFileName); immutable destFilePath = buildPath (destPrefix, destFileName); if (filePath.isRemote) { try { downloader.downloadFile (filePath, destFilePath); return destFilePath; } catch (DownloadException e) { logDebug ("Unable to download: %s", e.msg); } } else { if (filePath.exists) return filePath; } /* all extensions failed, so we failed */ throw new Exception ("Could not obtain any file matching %s".format (buildPath (prefix, srcFileName))); } appstream-generator-0.8.6/src/asgen/backends/alpinelinux/apkpkg.d000066400000000000000000000076531416066706400251420ustar00rootroot00000000000000/* * Copyright (C) 2020 Rasmus Thomsen * * Based on the archlinux backend, which is: * Copyright (C) 2016 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.alpinelinux.apkpkg; import std.array : empty; import std.format : format; import std.path : baseName, buildNormalizedPath, buildPath; import asgen.backends.interfaces; import asgen.config : Config; import asgen.downloader : Downloader; import asgen.utils : isRemote; import asgen.zarchive : ArchiveDecompressor; final class AlpinePackage : Package { private: string pkgname; string pkgver; string pkgarch; string pkgmaintainer; string[string] desc; string pkgFname; string localPkgFName; string tmpDir; string[] contentsL = null; ArchiveDecompressor archive; public: this (string pkgname, string pkgver, string pkgarch) { this.pkgname = pkgname; this.pkgver = pkgver; this.pkgarch = pkgarch; auto conf = Config.get (); this.tmpDir = buildPath (conf.getTmpDir (), format ("%s-%s_%s", name, ver, arch)); } override @property string name () const { return this.pkgname; } @property void name (string val) { this.pkgname = val; } override @property string ver () const { return this.pkgver; } @property void ver (string val) { this.pkgver = val; } override @property string arch () const { return this.pkgarch; } @property void arch(string val) { this.pkgarch = val; } override @property const(string[string]) description () const { return this.desc; } @property void filename (string fname) { this.pkgFname = fname; } override @property string getFilename () { if (!this.localPkgFName.empty) return this.localPkgFName; if (pkgFname.isRemote) { synchronized (this) { auto dl = Downloader.get; immutable path = buildNormalizedPath (this.tmpDir, this.pkgFname.baseName); dl.downloadFile (this.pkgFname, path); this.localPkgFName = path; return this.localPkgFName; } } else { this.localPkgFName = pkgFname; return this.localPkgFName; } } override @property string maintainer () const { return this.pkgmaintainer; } @property void maintainer (string maint) { this.pkgmaintainer = maint; } void setDescription (string text, string locale) { this.desc[locale] = text; } override const(ubyte)[] getFileData (string fname) { if (!this.archive.isOpen()) this.archive.open (this.getFilename); return this.archive.readData (fname); } @property override string[] contents () { if (!this.contentsL.empty) return this.contentsL; ArchiveDecompressor ad; ad.open (this.getFilename); this.contentsL = ad.readContents (); return this.contentsL; } @property void contents (string[] c) { this.contentsL = c; } override void finish () { } } appstream-generator-0.8.6/src/asgen/backends/alpinelinux/apkpkgindex.d000066400000000000000000000105341416066706400261620ustar00rootroot00000000000000/* * Copyright (C) 2020 Rasmus Thomsen * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.alpinelinux.apkpkgindex; import std.array : appender; import std.conv : to; import std.exception : enforce; import std.file : exists; import std.format : format; import std.path : baseName, buildPath; import std.utf : validate; import asgen.config : Config; import asgen.logging : logError; import asgen.zarchive : ArchiveDecompressor; import asgen.utils : escapeXml, isRemote; import asgen.backends.interfaces; import asgen.backends.alpinelinux.apkpkg; import asgen.backends.alpinelinux.apkindexutils; final class AlpinePackageIndex : PackageIndex { private: string rootDir; string tmpDir; Package[][string] pkgCache; public: this (string dir) { if (!dir.isRemote) enforce (exists (dir), format ("Directory '%s' does not exist.", dir)); this.rootDir = dir; auto conf = Config.get (); tmpDir = buildPath (conf.getTmpDir, dir.baseName); } override void release () { pkgCache = null; } private void setPkgDescription (AlpinePackage pkg, string pkgDesc) { if (pkgDesc is null) return; auto desc = "

%s

".format (pkgDesc.escapeXml); pkg.setDescription (desc, "C"); } private Package[] loadPackages (string suite, string section, string arch) { auto apkRootPath = buildPath (rootDir, suite, section, arch); auto indexFPath = downloadIfNecessary(apkRootPath, tmpDir, "APKINDEX.tar.gz", format("APKINDEX-%s-%s-%s.tar.gz", suite, section, arch)); AlpinePackage[string] pkgsMap; ArchiveDecompressor ad; ad.open (indexFPath); auto indexString = cast(string) ad.readData ("APKINDEX"); validate (indexString); auto range = ApkIndexBlockRange (indexString); foreach (pkgInfo; range) { auto fileName = pkgInfo.archiveName; AlpinePackage pkg; if (fileName in pkgsMap) { pkg = pkgsMap[fileName]; } else { pkg = new AlpinePackage (pkgInfo.pkgname, pkgInfo.pkgversion, pkgInfo.arch); pkgsMap[fileName] = pkg; } pkg.filename = buildPath(rootDir, suite, section, arch, fileName); pkg.maintainer = pkgInfo.maintainer; setPkgDescription(pkg, pkgInfo.pkgdesc); } // perform a sanity check, so we will never emit invalid packages auto pkgs = appender!(Package[]); if (pkgsMap.length > 20) pkgs.reserve ((pkgsMap.length.to!long - 10).to!size_t); foreach (ref pkg; pkgsMap.byValue ()) if (pkg.isValid) pkgs ~= pkg; else logError ("Found an invalid package (name, architecture or version is missing). This is a bug."); return pkgs.data; } override Package[] packagesFor (string suite, string section, string arch, bool withLongDescs = true) { immutable id = "%s-%s-%s".format (suite, section, arch); if (id !in pkgCache) { auto pkgs = loadPackages (suite, section, arch); synchronized (this) pkgCache[id] = pkgs; } return pkgCache[id]; } Package packageForFile (string fname, string suite = null, string section = null) { // Alpine currently doesn't have a way other than querying a web API // to tell what package owns a file, unless that packages is installed // on the system. return null; } bool hasChanges (DataStore dstore, string suite, string section, string arch) { return true; } } appstream-generator-0.8.6/src/asgen/backends/alpinelinux/package.d000066400000000000000000000016571416066706400252560ustar00rootroot00000000000000/* * Copyright (C) 2020 Rasmus Thomsen * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.alpinelinux; public import asgen.backends.alpinelinux.apkpkg; public import asgen.backends.alpinelinux.apkpkgindex; appstream-generator-0.8.6/src/asgen/backends/archlinux/000077500000000000000000000000001416066706400231525ustar00rootroot00000000000000appstream-generator-0.8.6/src/asgen/backends/archlinux/alpkg.d000066400000000000000000000047201416066706400244200ustar00rootroot00000000000000/* * Copyright (C) 2016 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.archlinux.alpkg; import std.stdio; import std.string; import std.array : empty; import asgen.logging; import asgen.zarchive; import asgen.backends.interfaces; final class ArchPackage : Package { private: string pkgname; string pkgver; string pkgarch; string pkgmaintainer; string[string] desc; string pkgFname; string[] contentsL; ArchiveDecompressor archive; public: override @property string name () const { return pkgname; } @property void name (string val) { pkgname = val; } override @property string ver () const { return pkgver; } @property void ver (string val) { pkgver = val; } override @property string arch () const { return pkgarch; } @property void arch (string val) { pkgarch = val; } override @property const(string[string]) description () const { return desc; } @property void filename (string fname) { pkgFname = fname; } override @property string getFilename () const { return pkgFname; } override @property string maintainer () const { return pkgmaintainer; } @property void maintainer (string maint) { pkgmaintainer = maint; } void setDescription (string text, string locale) { desc[locale] = text; } override const(ubyte)[] getFileData (string fname) { if (!archive.isOpen ()) { archive.open (this.getFilename); } return archive.readData (fname); } @property override string[] contents () { return contentsL; } @property void contents (string[] c) { contentsL = c; } override void finish () { } } appstream-generator-0.8.6/src/asgen/backends/archlinux/alpkgindex.d000066400000000000000000000122561416066706400254530ustar00rootroot00000000000000/* * Copyright (C) 2016 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.archlinux.alpkgindex; import std.stdio; import std.path; import std.string; import std.algorithm : canFind; import std.array : appender; import std.conv : to; static import std.file; import asgen.logging; import asgen.zarchive; import asgen.utils : escapeXml; import asgen.backends.interfaces; import asgen.backends.archlinux.alpkg; import asgen.backends.archlinux.listfile; final class ArchPackageIndex : PackageIndex { private: string rootDir; Package[][string] pkgCache; public: this (string dir) { this.rootDir = dir; if (!std.file.exists (dir)) throw new Exception ("Directory '%s' does not exist.", dir); } void release () { pkgCache = null; } private void setPkgDescription (ArchPackage pkg, string pkgDesc) { if (pkgDesc is null) return; auto desc = "

%s

".format (pkgDesc.escapeXml); pkg.setDescription (desc, "C"); } private Package[] loadPackages (string suite, string section, string arch) { auto pkgRoot = buildPath (rootDir, suite, section, "os", arch); auto listsTarFname = buildPath (pkgRoot, format ("%s.files.tar.gz", section)); if (!std.file.exists (listsTarFname)) { logWarning ("Package lists tarball '%s' does not exist.", listsTarFname); return []; } ArchiveDecompressor ad; ad.open (listsTarFname); logDebug ("Opened: %s", listsTarFname); ArchPackage[string] pkgsMap; foreach (entry; ad.read ()) { auto archPkid = dirName (entry.fname); ArchPackage pkg; if (archPkid in pkgsMap) { pkg = pkgsMap[archPkid]; } else { pkg = new ArchPackage (); pkgsMap[archPkid] = pkg; } auto infoBaseName = baseName (entry.fname); if (infoBaseName == "desc") { // we have the description file, add information to this package auto descF = new ListFile (); descF.loadData (entry.data); pkg.name = descF.getEntry ("NAME"); pkg.ver = descF.getEntry ("VERSION"); pkg.arch = descF.getEntry ("ARCH"); pkg.maintainer = descF.getEntry ("PACKAGER"); pkg.filename = buildPath (pkgRoot, descF.getEntry ("FILENAME")); setPkgDescription (pkg, descF.getEntry ("DESC")); } else if (infoBaseName == "files") { // we found a content index, add content information to the package auto filesF = new ListFile (); filesF.loadData (entry.data); auto filesStr = filesF.getEntry ("FILES"); if (filesStr is null) { if (!pkg.name.canFind ("-meta")) { logWarning ("Package '%s' has no file list set. Ignoring it.", pkg.toString ()); continue; } } auto contents = appender!(string[]); foreach (l; filesStr.splitLines ()) contents ~= "/" ~ l; pkg.contents = contents.data; } } // perform a sanity check, so we will never emit invalid packages auto pkgs = appender!(Package[]); if (pkgsMap.length > 20) pkgs.reserve ((pkgsMap.length.to!long - 10).to!size_t); foreach (ref pkg; pkgsMap.byValue ()) { if (pkg.isValid) pkgs ~= pkg; else logError ("Found an invalid package (name, architecture or version is missing). This is a bug."); } return pkgs.data; } Package[] packagesFor (string suite, string section, string arch, bool withLongDescs = true) { if ((suite == "arch") || (suite == "archlinux")) suite = ""; immutable id = "%s-%s-%s".format (suite, section, arch); if (id !in pkgCache) { auto pkgs = loadPackages (suite, section, arch); synchronized (this) pkgCache[id] = pkgs; } return pkgCache[id]; } Package packageForFile (string fname, string suite = null, string section = null) { return null; // FIXME: not implemented } bool hasChanges (DataStore dstore, string suite, string section, string arch) { return true; } } appstream-generator-0.8.6/src/asgen/backends/archlinux/listfile.d000066400000000000000000000050561416066706400251400ustar00rootroot00000000000000/* * Copyright (C) 2016 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.archlinux.listfile; import std.stdio; import std.string; final class ListFile { private: string[string] entries; public: this () { } void loadData (const(ubyte)[] data) { string[] content; auto dataStr = cast(string) data; content = dataStr.splitLines (); string blockName = null; foreach (l; content) { if ((l.startsWith ("%")) && (l.endsWith ("%"))) { blockName = l[1..$-1]; continue; } if (l == "") { blockName = null; continue; } if (blockName !is null) { if (blockName in entries) entries[blockName] ~= "\n" ~ l; else entries[blockName] = l; continue; } } } string getEntry (string name) { auto resP = name in entries; if (resP is null) // we found nothing return null; return *resP; } } unittest { writeln ("TEST: ", "Backend::Archlinux - ListFile"); string data = "%FILENAME% a2ps-4.14-6-x86_64.pkg.tar.xz %NAME% a2ps %VERSION% 4.14-6 %DESC% An Any to PostScript filter %CSIZE% 629320 %MULTILINE% Blah1 BLUBB2 EtcEtcEtc3 %SHA256SUM% a629a0e0eca0d96a97eb3564f01be495772439df6350600c93120f5ac7f3a1b5"; auto lf = new ListFile (); lf.loadData (cast(ubyte[]) data); assert (lf.getEntry ("FILENAME") == "a2ps-4.14-6-x86_64.pkg.tar.xz"); assert (lf.getEntry ("VERSION") == "4.14-6"); assert (lf.getEntry ("MULTILINE") == "Blah1\nBLUBB2\nEtcEtcEtc3"); assert (lf.getEntry ("SHA256SUM") == "a629a0e0eca0d96a97eb3564f01be495772439df6350600c93120f5ac7f3a1b5"); } appstream-generator-0.8.6/src/asgen/backends/archlinux/package.d000066400000000000000000000002041416066706400247060ustar00rootroot00000000000000 module asgen.backends.archlinux; public import asgen.backends.archlinux.alpkg; public import asgen.backends.archlinux.alpkgindex; appstream-generator-0.8.6/src/asgen/backends/debian/000077500000000000000000000000001416066706400223775ustar00rootroot00000000000000appstream-generator-0.8.6/src/asgen/backends/debian/debpkg.d000066400000000000000000000256331416066706400240110ustar00rootroot00000000000000/* * Copyright (C) 2016-2017 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.debian.debpkg; import std.stdio : File, writeln; import std.string : format, strip, endsWith, splitLines, join, split; import std.path : buildNormalizedPath, buildPath, baseName; import std.array : empty, appender; import std.file : rmdirRecurse, mkdirRecurse; import std.typecons : Nullable; static import std.file; import asgen.config; import asgen.logging; import asgen.zarchive; import asgen.backends.interfaces; import asgen.downloader : Downloader; import asgen.utils : isRemote; /** * Helper class for simple deduplication of package descriptions * between packages of different architectures in memory. */ final class DebPackageLocaleTexts { string[string] summary; /// map of localized package short summaries string[string] description; /// map of localized package descriptions void setDescription (string text, string locale) { synchronized (this) description[locale] = text; } void setSummary (string text, string locale) { synchronized (this) summary[locale] = text; } } /** * Representation of a Debian binary package */ class DebPackage : Package { private: string pkgname; string pkgver; string pkgarch; string pkgmaintainer; DebPackageLocaleTexts descTexts; Nullable!GStreamer gstreamer; bool contentsRead; string[] contentsL; string tmpDir; ArchiveDecompressor controlArchive; ArchiveDecompressor dataArchive; string debFname; string localDebFname; public: final @property override string name () const { return pkgname; } final @property override string ver () const { return pkgver; } final @property override string arch () const { return pkgarch; } final @property void name (string s) { pkgname = s; } final @property void ver (string s) { pkgver = s; } final @property void arch (string s) { pkgarch = s; } final @property override Nullable!GStreamer gst () { return gstreamer; } final @property void gst (GStreamer gst) { gstreamer = gst; } final @property override const(string[string]) description () const { return descTexts.description; } final @property override const(string[string]) summary () const { return descTexts.summary; } final @property void filename (string fname) { debFname = fname; localDebFname = null; } override final string getFilename () { if (!localDebFname.empty) return localDebFname; if (debFname.isRemote) { synchronized (this) { auto dl = Downloader.get; immutable path = buildNormalizedPath (tmpDir, debFname.baseName); dl.downloadFile (debFname, path); localDebFname = path; return localDebFname; } } else { localDebFname = debFname; return debFname; } } override final @property string maintainer () const { return pkgmaintainer; } final @property void maintainer (string maint) { pkgmaintainer = maint; } this (string pname, string pver, string parch, DebPackageLocaleTexts l10nTexts = null) { pkgname = pname; pkgver = pver; pkgarch = parch; descTexts = l10nTexts; if (descTexts is null) descTexts = new DebPackageLocaleTexts; contentsRead = false; updateTmpDirPath (); } ~this () { // FIXME: We can't properly clean up because we can't GC-allocate in a destructor (leads to crashes), // see if this is fixed in a future version of D, or simply don't use the GC in close (). // close (); } final void updateTmpDirPath () { auto conf = Config.get (); tmpDir = buildPath (conf.getTmpDir (), format ("%s-%s_%s", name, ver, arch)); } final void setDescription (string text, string locale) { descTexts.setDescription (text, locale); } final void setSummary (string text, string locale) { descTexts.setSummary (text, locale); } final setLocalizedTexts (DebPackageLocaleTexts l10nTexts) { assert (l10nTexts !is null); descTexts = l10nTexts; } @property final DebPackageLocaleTexts localizedTexts () { return descTexts; } private auto openPayloadArchive () { import std.regex : ctRegex; synchronized(this) { if (dataArchive.isOpen) return dataArchive; ArchiveDecompressor ad; // extract the payload to a temporary location first ad.open (this.getFilename); mkdirRecurse (tmpDir); auto files = ad.extractFilesByRegex (ctRegex!(r"data\.*"), tmpDir); if (files.length == 0) throw new Exception ("Unable to find the payload tarball in Debian package: %s".format (this.getFilename)); immutable dataArchiveFname = files[0]; dataArchive.open (dataArchiveFname); return dataArchive; } } final void extractPackage (const string dest = buildPath (tmpDir, name)) { import std.file : exists; import std.regex : ctRegex; synchronized(this) { if (!dest.exists) mkdirRecurse (dest); auto pa = openPayloadArchive (); pa.extractArchive (dest); } } private final auto openControlArchive () { import std.regex : ctRegex; synchronized(this) { if (controlArchive.isOpen) return controlArchive; ArchiveDecompressor ad; // extract the payload to a temporary location first ad.open (this.getFilename); mkdirRecurse (tmpDir); auto files = ad.extractFilesByRegex (ctRegex!(r"control\.*"), tmpDir); if (files.empty) throw new Exception ("Unable to find control data in Debian package: %s".format (this.getFilename)); immutable controlArchiveFname = files[0]; controlArchive.open (controlArchiveFname); return controlArchive; } } override final const(ubyte)[] getFileData (string fname) { auto pa = openPayloadArchive (); return pa.readData (fname); } @property override final string[] contents () { import std.utf : toUTF8; if (contentsRead) return contentsL; if (pkgname.endsWith ("icon-theme")) { // the md5sums file does not contain symbolic links - while that is okay-ish for regular // packages, it is not acceptable for icon themes, since those rely on symlinks to provide // aliases for certain icons. So, use the slow method for reading contents information here. auto pa = openPayloadArchive (); contentsL = pa.readContents (); contentsRead = true; return contentsL; } // use the md5sums file of the .deb control archive to determine // the contents of this package. // this is way faster than going through the payload directly, and // has the same accuracy. auto ca = openControlArchive (); const(ubyte)[] md5sumsData; try { md5sumsData = ca.readData ("./md5sums"); } catch (Exception e) { logWarning ("Could not read md5sums file for package %s: %s", this.id, e.msg); return []; } auto md5sums = cast(string) md5sumsData; try { md5sums = md5sums.toUTF8; } catch (Exception e) { logError ("Could not decode md5sums file for package %s: %s", this.id, e.msg); return []; } auto contentsAppender = appender!(string[]); contentsAppender.reserve (20); foreach (line; md5sums.splitLines ()) { auto parts = line.split (" "); if (parts.length <= 0) continue; string c = join (parts[1..$], " "); contentsAppender.put ("/" ~ c); } contentsL = contentsAppender.data; contentsRead = true; return contentsL; } /** * Get the "control" file information from the control archive * in the Debian package. * This is useful to get information from a package directly, e.g. * for processing a single package. */ final auto readControlInformation () { import std.utf : toUTF8; import asgen.backends.debian.tagfile : TagFile; auto ca = openControlArchive (); const(ubyte)[] controlData; try { controlData = ca.readData ("./control"); } catch (Exception e) { logError ("Could not read control file for package %s: %s", this.id, e.msg); return null; } auto controlStr = cast(string) controlData; try { controlStr = controlStr.toUTF8; } catch (Exception e) { logError ("Could not decode control file for package %s: %s", this.id, e.msg); return null; } auto tf = new TagFile (); tf.load (controlStr); return tf; } override final void cleanupTemp () { synchronized(this) { if (controlArchive.isOpen) controlArchive.close (); if (dataArchive.isOpen) dataArchive.close (); try { if (std.file.exists (tmpDir)) { /* Whenever we delete the temporary directory, we need to * forget about the local file too, since (if it's remote) that * was downloaded into there. */ logDebug ("Deleting temporary directory %s", tmpDir); localDebFname = null; rmdirRecurse (tmpDir); } } catch (Exception e) { // we ignore any error logDebug ("Unable to remove temporary directory: %s (%s)", tmpDir, e.msg); } } } override final void finish () { cleanupTemp (); } } appstream-generator-0.8.6/src/asgen/backends/debian/debpkgindex.d000066400000000000000000000323151416066706400250340ustar00rootroot00000000000000/* * Copyright (C) 2016-2018 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.debian.debpkgindex; import std.stdio; import std.path; import std.string; import std.algorithm : remove; import std.array : appender, array; import std.conv : to; import std.typecons : scoped; static import std.file; import asgen.logging; import asgen.backends.interfaces; import asgen.backends.debian.tagfile; import asgen.backends.debian.debpkg; import asgen.backends.debian.debutils; import asgen.config; import asgen.utils : escapeXml, getTextFileContents, isRemote; class DebianPackageIndex : PackageIndex { private: string rootDir; Package[][string] pkgCache; // index of localized text for a specific package name DebPackageLocaleTexts[string] l10nTextIndex; bool[string] indexChanged; protected: string tmpDir; public: this (string dir) { pkgCache.clear (); this.rootDir = dir; if (!dir.isRemote && !std.file.exists (dir)) throw new Exception ("Directory '%s' does not exist.".format (dir)); auto conf = Config.get (); tmpDir = buildPath (conf.getTmpDir, dir.baseName); } void release () { pkgCache.clear (); l10nTextIndex.clear (); indexChanged = null; } private immutable(string[]) findTranslations (const string suite, const string section) { import std.regex : matchFirst, regex; immutable inRelease = buildPath (rootDir, "dists", suite, "InRelease"); auto translationregex = r"%s/i18n/Translation-(\w+)$".format (section).regex; bool[string] ret; try { synchronized (this) { const inReleaseContents = getTextFileContents (inRelease); foreach (const ref entry; inReleaseContents) { auto match = entry.matchFirst (translationregex); if (match.empty) continue; ret[match[1]] = true; } } } catch (Exception ex) { logWarning ("Could not get %s, will assume 'en' is available.", inRelease); return ["en"]; } return cast(immutable) array (ret.byKey); } /** * Convert a Debian package description to a description * that looks nice-ish in AppStream clients. */ private string packageDescToAppStreamDesc (string[] lines) { // TODO: We actually need a Markdown-ish parser here if we want // to support listings in package descriptions properly. auto description = appender!string; description.reserve (80); description ~= "

"; bool first = true; foreach (l; lines) { if (l.strip () == ".") { description ~= "

\n

"; first = true; continue; } if (first) first = false; else description ~= " "; description ~= escapeXml (l); } description ~= "

"; return description.data; } private void loadPackageLongDescs (ref DebPackage[string] pkgs, string suite, string section) { immutable langs = findTranslations (suite, section); logDebug ("Found translations for: %s", langs.join(", ")); foreach (const ref lang; langs) { string fname; immutable fullPath = buildPath ("dists", suite, section, "i18n", /* here we explicitly substitute a * "%s", because * downloadIfNecessary will put the * file extension there */ "Translation-%s.%s".format(lang, "%s")); try { synchronized (this) { fname = downloadIfNecessary (rootDir, tmpDir, fullPath); } } catch (Exception ex) { logDebug ("No translations for %s in %s/%s", lang, suite, section); continue; } auto tagf = scoped!TagFile (); tagf.open (fname); do { immutable pkgname = tagf.readField ("Package"); immutable rawDesc = tagf.readField ("Description-%s".format (lang)); if (!pkgname) continue; if (!rawDesc) continue; auto pkg = pkgs.get (pkgname, null); if (pkg is null) continue; immutable textPkgId = "%s/%s".format (pkg.name, pkg.ver); DebPackageLocaleTexts l10nTexts; synchronized (this) l10nTexts = l10nTextIndex.get (textPkgId, null); if (l10nTexts !is null) { // we already fetched this information pkg.setLocalizedTexts (l10nTexts); } // read new localizations l10nTexts = pkg.localizedTexts; synchronized (this) l10nTextIndex[textPkgId] = l10nTexts; auto split = rawDesc.split ("\n"); if (split.length < 2) continue; if (lang == "en") l10nTexts.setSummary (split[0], "C"); l10nTexts.setSummary (split[0], lang); // NOTE: .remove() removes the element, but does not alter the // length of the array. Bug? (this is why we slice the array // here) split = split[1..$]; immutable description = packageDescToAppStreamDesc (split); if (lang == "en") l10nTexts.setDescription (description, "C"); l10nTexts.setDescription (description, lang); pkg.setLocalizedTexts (l10nTexts); } while (tagf.nextSection ()); } } private string getIndexFile (string suite, string section, string arch) { immutable path = buildPath ("dists", suite, section, "binary-%s".format (arch)); synchronized (this) { return downloadIfNecessary (rootDir, tmpDir, buildPath (path, "Packages.%s")); } } protected DebPackage newPackage (string name, string ver, string arch) { return new DebPackage (name, ver, arch); } private DebPackage[] loadPackages (string suite, string section, string arch, bool withLongDescs = true) { auto indexFname = getIndexFile (suite, section, arch); if (!std.file.exists (indexFname)) { logWarning ("Archive package index file '%s' does not exist.", indexFname); return []; } auto tagf = scoped!TagFile (); tagf.open (indexFname); logDebug ("Opened: %s", indexFname); DebPackage[string] pkgs; do { import std.algorithm : map; import std.array : array; auto name = tagf.readField ("Package"); auto ver = tagf.readField ("Version"); auto fname = tagf.readField ("Filename"); auto pkgArch = tagf.readField ("Architecture"); if (!name) continue; // sanity check: We only allow arch:all mixed in with packages from other architectures if (pkgArch != "all") pkgArch = arch; auto pkg = newPackage (name, ver, pkgArch); pkg.filename = buildPath (rootDir, fname); pkg.maintainer = tagf.readField ("Maintainer"); immutable decoders = tagf.readField("Gstreamer-Decoders") .split(";") .map!strip.array; immutable encoders = tagf.readField("Gstreamer-Encoders") .split(";") .map!strip.array; immutable elements = tagf.readField("Gstreamer-Elements") .split(";") .map!strip.array; immutable uri_sinks = tagf.readField("Gstreamer-Uri-Sinks") .split(";") .map!strip.array; immutable uri_sources = tagf.readField("Gstreamer-Uri-Sources") .split(";") .map!strip.array; auto gst = new GStreamer (decoders, encoders, elements, uri_sinks, uri_sources); if (gst.isNotEmpty) pkg.gst = gst; if (!pkg.isValid ()) { logWarning ("Found invalid package (%s)! Skipping it.", pkg.toString ()); continue; } // filter out the most recent package version in the packages list auto epkg = pkgs.get (name, null); if (epkg !is null) { if (compareVersions (epkg.ver, pkg.ver) > 0) continue; } pkgs[name] = pkg; } while (tagf.nextSection ()); // load long descriptions if (withLongDescs) loadPackageLongDescs (pkgs, suite, section); return array(pkgs.byValue); } Package[] packagesFor (string suite, string section, string arch, bool withLongDescs = true) { immutable id = "%s/%s/%s".format (suite, section, arch); if (id !in pkgCache) { auto pkgs = loadPackages (suite, section, arch, withLongDescs); synchronized (this) pkgCache[id] = to!(Package[]) (pkgs); } return pkgCache[id]; } Package packageForFile (string fname, string suite = null, string section = null) { auto pkg = newPackage ("", "", ""); pkg.filename = fname; auto tf = pkg.readControlInformation (); if (tf is null) throw new Exception ("Unable to read control information for package %s".format (fname)); pkg.name = tf.readField ("Package"); pkg.ver = tf.readField ("Version"); pkg.arch = tf.readField ("Architecture"); if (pkg.name is null || pkg.ver is null || pkg.arch is null) throw new Exception ("Unable to get control data for package %s".format (fname)); immutable rawDesc = tf.readField ("Description"); auto dSplit = rawDesc.split ("\n"); if (dSplit.length >= 2) { pkg.setSummary (dSplit[0], "C"); dSplit = dSplit[1..$]; immutable description = packageDescToAppStreamDesc (dSplit); pkg.setDescription (description, "C"); } // ensure we have a meaningful temporary directory name pkg.updateTmpDirPath (); return pkg.to!Package; } final bool hasChanges (DataStore dstore, string suite, string section, string arch) { import std.json; auto indexFname = getIndexFile (suite, section, arch); // if the file doesn't exit, we will emit a warning later anyway, so we just ignore this here if (!std.file.exists (indexFname)) return true; // check our cache on whether the index had changed if (indexFname in indexChanged) return indexChanged[indexFname]; std.datetime.SysTime mtime; std.datetime.SysTime atime; std.file.getTimes (indexFname, atime, mtime); auto currentTime = mtime.toUnixTime (); auto repoInfo = dstore.getRepoInfo (suite, section, arch); scope (exit) { repoInfo.object["mtime"] = JSONValue (currentTime); dstore.setRepoInfo (suite, section, arch, repoInfo); } if ("mtime" !in repoInfo.object) { indexChanged[indexFname] = true; return true; } auto pastTime = repoInfo["mtime"].integer; if (pastTime != currentTime) { indexChanged[indexFname] = true; return true; } indexChanged[indexFname] = false; return false; } } unittest { import std.algorithm.sorting : sort; import asgen.utils : getTestSamplesDir; writeln ("TEST: ", "DebianPackageIndex"); auto pi = new DebianPackageIndex (buildPath (getTestSamplesDir (), "debian")); assert (sort(pi.findTranslations ("sid", "main").dup) == sort(["en", "ca", "cs", "da", "de", "de_DE", "el", "eo", "es", "eu", "fi", "fr", "hr", "hu", "id", "it", "ja", "km", "ko", "ml", "nb", "nl", "pl", "pt", "pt_BR", "ro", "ru", "sk", "sr", "sv", "tr", "uk", "vi", "zh", "zh_CN", "zh_TW"])); } appstream-generator-0.8.6/src/asgen/backends/debian/debutils.d000066400000000000000000000175561416066706400243750ustar00rootroot00000000000000/* * Copyright (C) 2016 Matthias Klumpp * Copyright (C) The APT development team. * Copyright (C) 2016 Canonical Ltd * Author(s): Iain Lane * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.debian.debutils; import std.string; static import std.file; import asgen.logging; import asgen.downloader : Downloader, DownloadException; import asgen.utils : isRemote; /** * If prefix is remote, download the first of (prefix + suffix).{xz,bz2,gz}, * otherwise check if any of (prefix + suffix).{xz,bz2,gz} exists. * * Returns: Path to the file, which is guaranteed to exist. * * Params: * prefix = First part of the address, i.e. * "http://ftp.debian.org/debian/" or "/srv/mirrors/debian/" * destPrefix = If the file is remote, the directory to save it under, * which is created if necessary. * suffix = the rest of the address, so that (prefix + * suffix).format({xz,bz2,gz}) is a full path or URL, i.e. * "dists/unstable/main/binary-i386/Packages.%s". The suffix must * contain exactly one "%s"; this function is only suitable for * finding `.xz`, `.bz2` and `.gz` files. */ immutable (string) downloadIfNecessary (const string prefix, const string destPrefix, const string suffix, Downloader downloader = null) { import std.path : buildPath; if (downloader is null) downloader = Downloader.get; immutable exts = ["xz", "bz2", "gz"]; foreach (ref ext; exts) { immutable fileName = format (buildPath (prefix, suffix), ext); immutable destFileName = format (buildPath (destPrefix, suffix), ext); if (fileName.isRemote) { try { downloader.downloadFile (fileName, destFileName); return destFileName; } catch (DownloadException e) { logDebug ("Unable to download: %s", e.msg); } } else { if (std.file.exists (fileName)) return fileName; } } /* all extensions failed, so we failed */ throw new Exception ("Could not obtain any file matching %s".format (buildPath (prefix, suffix))); } /** * This compares a fragment of the version. This is a slightly adapted * version of what dpkg uses in dpkg/lib/dpkg/version.c. * In particular, the a | b = NULL check is removed as we check this in the * caller, we use an explicit end for a | b strings and we check ~ explicit. */ private int order (char c) pure { import std.ascii : isAlpha, isDigit; if (c.isDigit) return 0; else if (c.isAlpha) return c; else if (c == '~') return -1; else if (c) return c + 256; else return 0; } /** * Iterate over the whole string * What this does is to split the whole string into groups of * numeric and non numeric portions. For instance: * a67bhgs89 * Has 4 portions 'a', '67', 'bhgs', '89'. A more normal: * 2.7.2-linux-1 * Has '2', '.', '7', '.' ,'-linux-','1' */ private int cmpFragment (const(immutable(char)*) a, const(immutable(char)*) aEnd, const(immutable(char)*) b, const(immutable(char)*) bEnd) @trusted pure { import std.ascii; immutable(char) *lhs = a; immutable(char) *rhs = b; while (lhs != aEnd && rhs != bEnd) { int first_diff = 0; while (lhs != aEnd && rhs != bEnd && (!(*lhs).isDigit || !(*rhs).isDigit)) { int vc = order (*lhs); int rc = order (*rhs); if (vc != rc) return vc - rc; ++lhs; ++rhs; } while (*lhs == '0') ++lhs; while (*rhs == '0') ++rhs; while ((*lhs).isDigit && (*rhs).isDigit) { if (!first_diff) first_diff = *lhs - *rhs; ++lhs; ++rhs; } if ((*lhs).isDigit) return 1; if ((*rhs).isDigit) return -1; if (first_diff) return first_diff; } // The strings must be equal if (lhs == aEnd && rhs == bEnd) return 0; // lhs is shorter if (lhs == aEnd) { if (*rhs == '~') return 1; return -1; } // rhs is shorter if (rhs == bEnd) { if (*lhs == '~') return -1; return 1; } // Shouldn't happen return 1; } // import from string.h, needs glibc private extern(C) void *memrchr (const void *s, int c, size_t n) @system pure; /** * Compare two Debian-style version numbers. */ int compareVersions (const string a, const string b) @trusted pure { import core.stdc.string; immutable(char) *ac = a.toStringz; immutable(char) *bc = b.toStringz; immutable(char*) aEnd = ac + a.length; immutable(char*) bEnd = bc + b.length; // Strip off the epoch and compare it auto lhs = cast(immutable(char)*) memchr (ac, ':', aEnd - ac); auto rhs = cast(immutable(char)*) memchr (bc, ':', bEnd - bc); if (lhs is null) lhs = ac; if (rhs is null) rhs = bc; // Special case: a zero epoch is the same as no epoch, // so remove it. if (lhs != ac) { for (; *ac == '0'; ++ac) {} if (ac == lhs) { ++ac; ++lhs; } } if (rhs != bc) { for (; *bc == '0'; ++bc) {} if (bc == rhs) { ++bc; ++rhs; } } // Compare the epoch auto res = cmpFragment (ac, lhs, bc, rhs); if (res != 0) return res; // Skip the : if (lhs != ac) lhs++; if (rhs != bc) rhs++; // Find the last - auto dlhs = cast(immutable(char)*) memrchr (lhs, '-', aEnd - lhs); auto drhs = cast(immutable(char)*) memrchr (rhs, '-', bEnd - rhs); if (dlhs is null) dlhs = aEnd; if (drhs is null) drhs = bEnd; // Compare the main version res = cmpFragment (lhs, dlhs, rhs, drhs); if (res != 0) return res; // Skip the - if (dlhs != lhs) dlhs++; if (drhs != rhs) drhs++; // no debian revision need to be treated like -0 if (*(dlhs - 1) == '-' && *(drhs - 1) == '-') { return cmpFragment (dlhs, aEnd, drhs, bEnd); } else if (*(dlhs - 1) == '-') { immutable(char)* zeroZ = "0"; return cmpFragment (dlhs, aEnd, zeroZ, zeroZ + 1); } else if (*(drhs - 1) == '-') { immutable(char)* zeroZ = "0"; return cmpFragment (zeroZ, zeroZ + 1, drhs, bEnd); } else { return 0; } } unittest { import std.stdio : writeln; writeln ("TEST: ", "DebianUtils"); assert (compareVersions ("6", "8") < 0); assert (compareVersions ("0.6.12b-d", "0.6.12a") > 0); assert (compareVersions ("7.4", "7.4") == 0); assert (compareVersions ("ab.d", "ab.f") < 0); assert (compareVersions ("0.6.16", "0.6.14") > 0); assert (compareVersions ("3.0.rc2", "3.0.0") > 0); assert (compareVersions ("3.0.0~rc2", "3.0.0") < 0); assert (compareVersions ("4:5.6-2", "8.0-6") > 0); assert (compareVersions ("1:1.0-4", "3:0.8-2") < 0); } appstream-generator-0.8.6/src/asgen/backends/debian/package.d000066400000000000000000000016471416066706400241470ustar00rootroot00000000000000/* * Copyright (C) 2016 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.debian; public import asgen.backends.debian.debpkg; public import asgen.backends.debian.debpkgindex; appstream-generator-0.8.6/src/asgen/backends/debian/tagfile.d000066400000000000000000000110221416066706400241530ustar00rootroot00000000000000/* * Copyright (C) 2016-2018 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.debian.tagfile; import std.stdio : File; import std.string : startsWith, indexOf, chompPrefix, strip, split, splitLines; import std.typecons : Flag, Yes; import std.array : appender, empty; import std.conv : to; import std.path : buildPath; import asgen.zarchive; import asgen.logging; /** * Parser for Debian's RFC2822-style metadata. */ final class TagFile { private: string[] content; uint pos; string[string] currentBlock; string _fname; public: this () @trusted { currentBlock.clear (); } void open (string fname, Flag!"compressed" compressed = Yes.compressed) @trusted { _fname = fname; if (compressed) { auto data = decompressFile (fname); load (data); } else { import std.stdio; auto f = File (fname, "r"); auto data = appender!string; string line; while ((line = f.readln ()) !is null) data ~= line; load (data.data); } } @property string fname () const { return _fname; } void load (string data) { content = data.splitLines (); pos = 0; readCurrentBlockData (); } void first () { pos = 0; } private void readCurrentBlockData () @trusted { currentBlock.clear (); immutable clen = content.length; for (auto i = pos; i < clen; i++) { if (content[i] == "") break; // check whether we are in a multiline value field, and just skip forward in that case if (startsWith (content[i], " ")) continue; immutable separatorIndex = indexOf (content[i], ':'); if (separatorIndex <= 0) continue; // this is no field auto fieldName = content[i][0..separatorIndex]; auto fdata = content[i][separatorIndex+1..$]; if ((i+1 >= clen) || (!startsWith (content[i+1], " "))) { // we have a single-line field currentBlock[fieldName] = fdata.strip (); } else { // we have a multi-line field auto fdata_ml = appender!string (); fdata_ml ~= fdata.strip (); for (auto j = i+1; j < clen; j++) { auto slice = chompPrefix (content[j], " "); if (slice == content[j]) break; if (fdata_ml.data == "") { fdata_ml = appender!string (); fdata_ml ~= slice; } else { fdata_ml ~= "\n"; fdata_ml ~= slice; } } currentBlock[fieldName] = fdata_ml.data; } } } bool nextSection () @trusted { bool breakNext = false; immutable clen = content.length; currentBlock.clear (); if (pos >= clen) return false; uint i; for (i = pos; i < clen; i++) { if (content[i] == "") { pos = i + 1; breakNext = true; } else if (breakNext) { break; } } // check if we reached the end of this file if (i == clen) pos = cast(uint) clen; if (pos >= clen) return false; readCurrentBlockData (); return true; } string readField (string name, string defaultValue = null) @trusted { auto dataP = name in currentBlock; if (dataP is null) return defaultValue; // we found nothing else return *dataP; } } appstream-generator-0.8.6/src/asgen/backends/dummy/000077500000000000000000000000001416066706400223105ustar00rootroot00000000000000appstream-generator-0.8.6/src/asgen/backends/dummy/dummypkg.d000066400000000000000000000047571416066706400243270ustar00rootroot00000000000000/* * Copyright (C) 2016 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.dummy.dummypkg; import std.stdio; import std.string; import std.array : empty; import asgen.backends.interfaces; import asgen.logging; final class DummyPackage : Package { private: string pkgname; string pkgver; string pkgarch; string pkgmaintainer; string[string] desc; string testPkgFname; PackageKind _kind; public: @property override string name () const { return pkgname; } @property override string ver () const { return pkgver; } @property override string arch () const { return pkgarch; } @property override const(string[string]) description () const { return desc; } override @property string getFilename () const { return testPkgFname; } @property void filename (string fname) { testPkgFname = fname; } override @property string maintainer () const { return pkgmaintainer; } @property void maintainer (string maint) { pkgmaintainer = maint; } this (string pname, string pver, string parch) { pkgname = pname; pkgver = pver; pkgarch = parch; _kind = PackageKind.PHYSICAL; } void setDescription (string text, string locale) { desc[locale] = text; } override ubyte[] getFileData (string fname) { if (fname == "TEST") return ['N', 'O', 'T', 'H', 'I', 'N', 'G']; return []; } @property override string[] contents () { return ["NOTHING1", "NOTHING2"]; } override void finish () { } @property override PackageKind kind () @safe pure { return this._kind; } @property void kind (PackageKind v) @safe pure { this._kind = v; } } appstream-generator-0.8.6/src/asgen/backends/dummy/package.d000066400000000000000000000016431416066706400240540ustar00rootroot00000000000000/* * Copyright (C) 2016 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.dummy; public import asgen.backends.dummy.dummypkg; public import asgen.backends.dummy.pkgindex; appstream-generator-0.8.6/src/asgen/backends/dummy/pkgindex.d000066400000000000000000000031711416066706400242700ustar00rootroot00000000000000/* * Copyright (C) 2016 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.dummy.pkgindex; import std.stdio; import std.path; import std.string; import std.algorithm : remove; import asgen.logging; import asgen.backends.interfaces; import asgen.backends.dummy.dummypkg; final class DummyPackageIndex : PackageIndex { private: Package[][string] pkgCache; public: this (string dir) { } void release () { pkgCache = null; } Package[] packagesFor (string suite, string section, string arch, bool withLongDescs = true) { return [new DummyPackage ("test", "1.0", "amd64")]; } Package packageForFile (string fname, string suite = null, string section = null) { return null; // FIXME: not implemented } bool hasChanges (DataStore dstore, string suite, string section, string arch) { return true; } } appstream-generator-0.8.6/src/asgen/backends/interfaces.d000066400000000000000000000160161416066706400234510ustar00rootroot00000000000000/* * Copyright (C) 2016-2017 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.interfaces; import std.typecons : Nullable; import std.string : format; import std.array : empty; import appstream.Component; import glib.KeyFile : KeyFile; public import asgen.datastore; class GStreamer { immutable string[] decoders; immutable string[] encoders; immutable string[] elements; immutable string[] uri_sinks; immutable string[] uri_sources; @property @safe pure bool isNotEmpty () const { return !(decoders.empty && encoders.empty && elements.empty && uri_sinks.empty && uri_sources.empty); } this () { decoders = encoders = elements = uri_sinks = uri_sources = []; } this (immutable string[] decoders, immutable string[] encoders, immutable string[] elements, immutable string[] uri_sinks, immutable string[] uri_sources) { this.decoders = decoders; this.encoders = encoders; this.elements = elements; this.uri_sinks = uri_sinks; this.uri_sources = uri_sources; } } /** * Type of a package that can be processed. * Allows distinguishing "real" packages from * virtual or fake ones that are used internally. */ enum PackageKind { UNKNOWN, PHYSICAL, FAKE } /** * Represents a distribution package in the generator. */ abstract class Package { @property string name () const @safe pure; @property string ver () const @safe pure; @property string arch () const @safe pure; @property string maintainer () const; /** * Type of this package (whether it actually exists or is a fake/virtual package) * You pretty much always want PHYSICAL. */ @property PackageKind kind () @safe pure { return PackageKind.PHYSICAL; } /** * A associative array containing package descriptions. * Key is the language (or locale), value the description. * * E.g.: ["en": "A description.", "de": "Eine Beschreibung"] */ @property const(string[string]) description () const; /** * A associative array containing package summaries. * Key is the language (or locale), value the summary. * * E.g.: ["en": "foo the bar"] */ @property const(string[string]) summary () const { return (string[string]).init; } /** * Local filename of the package. This string is only used for * issue reporting and other information, the file is never * accessed directly (all data is retrieved via getFileData()). * * This function should return a local filepath, backends might * download missing packages on-demand from a web location. */ string getFilename (); /** * A list payload files this package contains. */ @property string[] contents (); /** * Obtain data for a specific file in the package. */ abstract const(ubyte)[] getFileData (string fname); /** * Remove temporary data that might have been created while loading information from * this package. This function can be called to avoid excessive use of disk space. * As opposed to `close()`, the package may be reopened afterwards. */ void cleanupTemp () {} /** * Close the package. This function is called when we will * no longer request any file data from this package. */ abstract void finish () {} @property Nullable!GStreamer gst () { return Nullable!GStreamer (); } /** * Retrieve backend-specific translations. * * (currently only used by the Ubuntu backend) */ string[string] getDesktopFileTranslations (KeyFile desktopFile, const string text) { return null; } @property bool hasDesktopFileTranslations () const { return false; } private string pkid; /** * Get the unique identifier for this package. * The ID is supposed to be unique per backend, it should never appear * multiple times in suites/sections. */ @property final string id () @safe pure { import std.array : empty; if (pkid.empty) pkid = "%s/%s/%s".format (this.name, this.ver, this.arch); return pkid; } /** * Check if the package is valid. * A Package must at least have a name, version and architecture defined. */ @safe pure final bool isValid () { import std.array : empty; return (!name.empty) && (!ver.empty) && (!arch.empty); } @safe pure override string toString () { return id; } } /** * An index of information about packages in a distribution. */ interface PackageIndex { /** * Called after a set of operations has completed, which allows the index to * release memory it might have allocated for cached data, or delete temporary * files. **/ void release (); /** * Get a list of packages for the given suite/section/arch triplet. * The PackageIndex should cache the data if obtaining it is an expensive * operation, since the generator might query the data multiple times. **/ Package[] packagesFor (string suite, string section, string arch, bool withLongDescs = true); /** * Get an abstract package representation for a physical package * file. A suite name and section name is obviously given. * This function is used in case only processing of one particular * package is requested. * Backends should return null if the feature is not implemented. **/ Package packageForFile (string fname, string suite = null, string section = null); /** * Check if the index for the given suite/section/arch triplet has changed since * the last generator run. The index can use the (get/set)RepoInfo methods on DataCache * to store mtime or checksum data for the given suite. * For the lifetime of the PackagesIndex, this method must return the same result, * which means an internal cache is useful. */ bool hasChanges (DataStore dstore, string suite, string section, string arch); } appstream-generator-0.8.6/src/asgen/backends/rpmmd/000077500000000000000000000000001416066706400222745ustar00rootroot00000000000000appstream-generator-0.8.6/src/asgen/backends/rpmmd/package.d000066400000000000000000000001721416066706400240340ustar00rootroot00000000000000 module asgen.backends.rpmmd; public import asgen.backends.rpmmd.rpmpkg; public import asgen.backends.rpmmd.rpmpkgindex; appstream-generator-0.8.6/src/asgen/backends/rpmmd/rpmpkg.d000066400000000000000000000050661416066706400237500ustar00rootroot00000000000000/* * Copyright (C) 2016-2017 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.rpmmd.rpmpkg; import std.stdio; import std.string; import std.array : empty; import asgen.logging; import asgen.zarchive; import asgen.backends.interfaces; final class RPMPackage : Package { private: string pkgname; string pkgver; string pkgarch; string pkgmaintainer; string[string] desc; string[string] summ; string pkgFname; string[] contentsL; ArchiveDecompressor archive; public: override @property string name () const { return pkgname; } @property void name (string val) { pkgname = val; } override @property string ver () const { return pkgver; } @property void ver (string val) { pkgver = val; } override @property string arch () const { return pkgarch; } @property void arch (string val) { pkgarch = val; } override @property const(string[string]) description () const { return desc; } override @property string getFilename () const { return pkgFname; } @property void filename (string fname) { pkgFname = fname; } override @property string maintainer () const { return pkgmaintainer; } @property void maintainer (string maint) { pkgmaintainer = maint; } void setDescription (string text, string locale) { desc[locale] = text; } void setSummary (string text, string locale) { summ[locale] = text; } override const(ubyte)[] getFileData (string fname) { if (!archive.isOpen) archive.open (this.getFilename); return archive.readData (fname); } @property override string[] contents () { return contentsL; } @property void contents (string[] c) { contentsL = c; } override void finish () { } } appstream-generator-0.8.6/src/asgen/backends/rpmmd/rpmpkgindex.d000066400000000000000000000163321416066706400247760ustar00rootroot00000000000000/* * Copyright (C) 2016-2017 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.rpmmd.rpmpkgindex; import std.stdio : writeln; import std.path : buildPath; import std.array : appender, empty; import std.string : format; import std.algorithm : canFind, endsWith; import std.conv : to; import std.xml; static import std.file; import asgen.logging; import asgen.backends.interfaces; import asgen.backends.rpmmd.rpmpkg; final class RPMPackageIndex : PackageIndex { private: string rootDir; Package[][string] pkgCache; public: this (string dir) { this.rootDir = dir; if (!std.file.exists (dir)) throw new Exception ("Directory '%s' does not exist.", dir); } void release () { pkgCache = null; } private void setPkgDescription (RPMPackage pkg, string pkgDesc) { if (pkgDesc is null) return; auto desc = "

%s

".format (pkgDesc); pkg.setDescription (desc, "C"); } private RPMPackage[] loadPackages (string suite, string section, string arch) { auto repoRoot = buildPath (rootDir, suite, section, arch, "os"); auto primaryIndexFiles = appender!(string[]); auto filelistFiles = appender!(string[]); auto repoMdIndexContent = cast(string) std.file.read (buildPath (repoRoot, "repodata", "repomd.xml")); auto indexXml = new DocumentParser (repoMdIndexContent); indexXml.onStartTag["data"] = (ElementParser xml) { immutable dataType = xml.tag.attr["type"]; if (dataType == "primary") { xml.onStartTag["location"] = (ElementParser x) { primaryIndexFiles ~= x.tag.attr["href"]; }; xml.parse (); } else if (dataType == "filelists") { xml.onStartTag["location"] = (ElementParser x) { filelistFiles ~= x.tag.attr["href"]; }; xml.parse (); } }; indexXml.parse(); // package-id -> RPMPackage RPMPackage[string] pkgMap; // parse the primary metadata foreach (ref primaryFile; primaryIndexFiles.data) { immutable metaFname = buildPath (repoRoot, primaryFile); string data; if (primaryFile.endsWith (".xml")) { data = cast(string) std.file.read (metaFname); } else { import asgen.zarchive : decompressFile; data = decompressFile (metaFname); } auto pkgXml = new DocumentParser (data); pkgXml.onStartTag["package"] = (ElementParser xml) { // make sure we only check RPM packages if (xml.tag.attr["type"] != "rpm") return; auto pkg = new RPMPackage; pkg.maintainer = "None"; xml.onEndTag["name"] = (in Element e) { pkg.name = e.text; }; xml.onEndTag["arch"] = (in Element e) { pkg.arch = e.text; }; xml.onEndTag["summary"] = (in Element e) { pkg.setSummary (e.text, "C"); }; xml.onEndTag["description"] = (in Element e) { pkg.setDescription (e.text, "C"); }; xml.onEndTag["packager"] = (in Element e) { pkg.maintainer = e.text; }; xml.onStartTag["version"] = (ElementParser x) { immutable epoch = x.tag.attr["epoch"]; immutable upstream_ver = x.tag.attr["ver"]; immutable rel = x.tag.attr["rel"]; if ((epoch == "0") || (epoch.empty)) pkg.ver = "%s-%s".format (upstream_ver, rel); else pkg.ver = "%s:%s-%s".format (epoch, upstream_ver, rel); }; xml.onStartTag["location"] = (ElementParser x) { pkg.filename = buildPath (repoRoot, x.tag.attr["href"]); }; string pkgidCS; xml.onEndTag["checksum"] = (in Element e) { // we are only interested in the package-id here if (e.tag.attr["pkgid"] != "YES") return; pkgidCS = e.text; }; xml.parse (); if (pkgidCS.empty) { logWarning ("Found package '%s' in '%s' without suitable pkgid. Ignoring it.", pkg.name, primaryFile); return; } pkgMap[pkgidCS] = pkg; }; pkgXml.parse(); } pkgMap.rehash; // read the filelists foreach (ref filelistFile; filelistFiles.data) { immutable flistFname = buildPath (repoRoot, filelistFile); string data; if (filelistFile.endsWith (".xml")) { data = cast(string) std.file.read (flistFname); } else { import asgen.zarchive : decompressFile; data = decompressFile (flistFname); } auto flXml = new DocumentParser (data); flXml.onStartTag["package"] = (ElementParser xml) { immutable pkgid = xml.tag.attr["pkgid"]; auto pkgP = pkgid in pkgMap; if (pkgP is null) return; auto pkg = *pkgP; auto contents = appender!(string[]); xml.onEndTag["file"] = (in Element e) { contents ~= e.text; }; xml.parse (); pkg.contents = contents.data; }; flXml.parse(); } return pkgMap.values; } Package[] packagesFor (string suite, string section, string arch, bool withLongDescs = true) { immutable id = "%s-%s-%s".format (suite, section, arch); if (id !in pkgCache) { auto pkgs = loadPackages (suite, section, arch); synchronized (this) pkgCache[id] = to!(Package[]) (pkgs); } return pkgCache[id]; } Package packageForFile (string fname, string suite = null, string section = null) { return null; // FIXME: not implemented } bool hasChanges (DataStore dstore, string suite, string section, string arch) { return true; } } unittest { import std.algorithm.sorting : sort; import asgen.utils : getTestSamplesDir; writeln ("TEST: ", "RpmMDPackageIndex"); auto pi = new RPMPackageIndex (buildPath (getTestSamplesDir (), "rpmmd")); auto pkgs = pi.loadPackages ("26", "Workstation", "x86_64"); assert (pkgs.length == 4); } appstream-generator-0.8.6/src/asgen/backends/ubuntu/000077500000000000000000000000001416066706400224775ustar00rootroot00000000000000appstream-generator-0.8.6/src/asgen/backends/ubuntu/package.d000066400000000000000000000016471416066706400242470ustar00rootroot00000000000000/* * Copyright (C) 2016 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.ubuntu; public import asgen.backends.ubuntu.ubupkg; public import asgen.backends.ubuntu.ubupkgindex; appstream-generator-0.8.6/src/asgen/backends/ubuntu/ubupkg.d000066400000000000000000000163531416066706400241510ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical Ltd * Author: Iain Lane * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.ubuntu.ubupkg; import std.path : buildPath; import std.file : mkdirRecurse; import std.conv : to; import glib.Internationalization; import glib.KeyFile; import appstream.Component; import asgen.logging; import asgen.utils : DESKTOP_GROUP; import asgen.backends.debian.debpkg : DebPackage; import asgen.backends.interfaces; extern (C) char *bindtextdomain (const(char*) domainname, const(char*) dirName) nothrow @nogc; /** * A helper class that provides functions to work with language packs * used in Ubuntu. */ final class LanguagePackProvider { private: UbuntuPackage[] langpacks; string globalTmpDir; string langpackDir; string localeDir; string[] langpackLocales; public: this (string globalTmpDir) { this.globalTmpDir = globalTmpDir; this.langpackDir = buildPath (globalTmpDir, "langpacks"); this.localeDir = buildPath (langpackDir, "locales"); } void addLanguagePacks (UbuntuPackage[] langpacks) { synchronized (this) this.langpacks ~= langpacks; } void clear () { synchronized (this) this.langpacks = []; } private void extractLangpacks () { import std.algorithm : filter, map; import std.array : appender, array, split; import std.file : dirEntries, exists, SpanMode, readText; import std.parallelism : parallel; import std.path : baseName; import std.process : Pid, spawnProcess, wait; import std.string : splitLines, startsWith; import glib.Util : Util; if (!langpackDir.exists) { bool[string] extracted; langpackDir.mkdirRecurse (); foreach (ref pkg; langpacks) { if (pkg.name in extracted) continue; logDebug ("Extracting %s", pkg.name); pkg.extractPackage (langpackDir); extracted[pkg.name] = true; } auto supportedd = buildPath (langpackDir, "var", "lib", "locales", "supported.d"); localeDir.mkdirRecurse (); foreach (locale; parallel (supportedd.dirEntries (SpanMode.shallow), 5)) { foreach (ref line; locale.readText.splitLines) { auto components = line.split (" "); auto localecharset = components[0].split ("."); auto outdir = buildPath (localeDir, components[0]); logDebug ("Generating locale in %s", outdir); auto pid = spawnProcess ([Util.findProgramInPath ("localedef"), "--no-archive", "-i", localecharset[0], "-c", "-f", components[1], outdir]); scope (exit) wait (pid); } } } // we don't need it; we've already extracted the langpacks langpacks = []; if (langpackLocales is null) langpackLocales = localeDir.dirEntries (SpanMode.shallow) .filter!(f => f.isDir) .map!(f => f.name.baseName) .array; } private auto getTranslationsPrivate (const string domain, const string text) { import core.stdc.locale : setlocale, LC_ALL; import core.stdc.string : strdup; import core.sys.posix.stdlib : getenv, setenv, unsetenv; import std.string : toStringz; char *[char *] env; foreach (ref var; ["LC_ALL", "LANG", "LANGUAGE", "LC_MESSAGES"]) { const value = getenv (var.toStringz); if (value !is null) { env[var.toStringz] = getenv (var.toStringz).strdup; unsetenv (var.toStringz); } } scope (exit) { foreach (key, val; env) setenv (key, val, false); } setenv ("LOCPATH", localeDir.toStringz, true); auto initialLocale = setlocale (LC_ALL, ""); scope(exit) setlocale (LC_ALL, initialLocale); auto dir = buildPath (langpackDir, "usr", "share", "locale-langpack"); string[string] ret; foreach (ref locale; langpackLocales) { setlocale (LC_ALL, locale.toStringz); bindtextdomain (domain.toStringz, dir.toStringz); const translatedtext = Internationalization.dgettext (domain, text); if (text != translatedtext) ret[locale] = translatedtext; } return ret; } string[string] getTranslations (const string domain, const string text) { // this functions do nasty things like changing environment variables and // messing with other global state. We therefore need to ensure that nothing // else is running in parallel. synchronized { extractLangpacks (); return getTranslationsPrivate (domain, text); } } } /** * An Ubuntu package. */ final class UbuntuPackage : DebPackage { private: LanguagePackProvider lpack; public: this (string pname, string pver, string parch, LanguagePackProvider lpack) { this.lpack = lpack; super (pname, pver, parch); assert (this.lpack !is null); updateTmpDirPath (); } @property override bool hasDesktopFileTranslations () const { return true; } override string[string] getDesktopFileTranslations (KeyFile desktopFile, const string text) { string langpackdomain; try { langpackdomain = desktopFile.getString (DESKTOP_GROUP, "X-Ubuntu-Gettext-Domain"); } catch (Exception) { try { langpackdomain = desktopFile.getString (DESKTOP_GROUP, "X-GNOME-Gettext-Domain"); } catch (Exception) { return null; } } logDebug ("%s has langpack domain %s", name, langpackdomain); return lpack.getTranslations (langpackdomain, text); } } appstream-generator-0.8.6/src/asgen/backends/ubuntu/ubupkgindex.d000066400000000000000000000062211416066706400251720ustar00rootroot00000000000000/* * Copyright (C) 2016 Canonical Ltd * Author: Iain Lane * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.backends.ubuntu.ubupkgindex; import std.string : format; import std.array : appender; import std.conv : to; import asgen.backends.debian; import asgen.backends.interfaces; import asgen.backends.ubuntu.ubupkg; final class UbuntuPackageIndex : DebianPackageIndex { private: LanguagePackProvider langpacks; // holds the IDs of suite/section/arch combinations where we scanned language packs bool[string] checkedLangPacks; public: this (string dir) { super (dir); /* * UbuntuPackage needs to extract the langpacks, so we give it an array * of langpacks. There is a small overhead when computing this array * which might be unnecessary if no processed packages are using * langpacks, but otherwise we need to keep a reference to all packages * around, which is very expensive. */ langpacks = new LanguagePackProvider (tmpDir); } override void release () { super.release (); checkedLangPacks.clear (); langpacks = new LanguagePackProvider (tmpDir); } override protected DebPackage newPackage (string name, string ver, string arch) { return new UbuntuPackage (name, ver, arch, langpacks); } override Package[] packagesFor (string suite, string section, string arch, bool withLongDescs = true) { import std.string : startsWith; auto pkgs = super.packagesFor (suite, section, arch, withLongDescs); immutable ssaId = "%s/%s/%s".format (suite, section, arch); if (ssaId in checkedLangPacks) return pkgs; // no need to scan for language packs, we already did that // scan for language packs and add them to the data provider synchronized (this) { auto pkgslangpacks = appender!(UbuntuPackage[]); pkgslangpacks.reserve (32); foreach (ref pkg; pkgs) { if (pkg.name.startsWith ("language-pack-")) pkgslangpacks ~= pkg.to!UbuntuPackage; } langpacks.addLanguagePacks (pkgslangpacks.data); checkedLangPacks[ssaId] = true; } return pkgs; } override Package packageForFile (string fname, string suite = null, string section = null) { return null; // FIXME: not implemented } } appstream-generator-0.8.6/src/asgen/bindings/000077500000000000000000000000001416066706400212005ustar00rootroot00000000000000appstream-generator-0.8.6/src/asgen/bindings/libarchive.d000066400000000000000000000076571416066706400234740ustar00rootroot00000000000000/* * Copyright (C) 2016 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ module asgen.bindings.libarchive; import core.stdc.stdio; import std.conv : octal; extern(C): nothrow: @nogc: static if (!is(typeof(usize))) private alias usize = size_t; struct archive {} struct archive_entry {} immutable ARCHIVE_EOF = 1; /* Found end of archive. */ immutable ARCHIVE_OK = 0; /* Operation was successful. */ immutable ARCHIVE_RETRY = -10; /* Retry might succeed. */ immutable ARCHIVE_WARN = -20; /* Partial success. */ immutable ARCHIVE_FAILED = -25; /* Current operation cannot complete. */ immutable ARCHIVE_FATAL = -30; /* No more operations are possible. */ immutable AE_IFMT = octal!170000; immutable AE_IFREG = octal!100000; immutable AE_IFLNK = octal!120000; immutable AE_IFSOCK = octal!140000; immutable AE_IFCHR = octal!20000; immutable AE_IFBLK = octal!60000; immutable AE_IFDIR = octal!40000; immutable AE_IFIFO = octal!10000; const(char) *archive_error_string (archive*); int archive_errno (archive*); archive *archive_read_new (); int archive_read_free (archive*); int archive_read_support_filter_all (archive*); int archive_read_support_filter_gzip (archive*); int archive_read_support_filter_lzma (archive*); int archive_read_support_format_raw (archive*); int archive_read_support_format_empty (archive*); int archive_read_support_format_all (archive*); int archive_read_support_format_ar (archive*); int archive_read_support_format_gnutar (archive*); int archive_write_set_filter_option (archive *a, const(char) *m, const(char) *o, const(char) *v); int archive_read_open_filename (archive*, const(char) *filename, usize block_size); int archive_read_open_FILE (archive*, FILE *file); int archive_read_open_memory (archive*, void *buff, size_t size); ptrdiff_t archive_read_data (archive*, const(void*), usize); int archive_read_next_header (archive*, archive_entry**); int archive_read_data_skip (archive*); int archive_read_data_block (archive *a, const(void*) *buff, size_t *size, long *offset); archive_entry *archive_entry_new (); void archive_entry_free (archive_entry*); const(char) *archive_entry_pathname (archive_entry*); void archive_entry_set_pathname (archive_entry*, const(char) *); uint archive_entry_filetype (archive_entry*); void archive_entry_set_size (archive_entry*, long); void archive_entry_set_filetype (archive_entry*, uint); void archive_entry_set_perm (archive_entry*, uint); void archive_entry_set_mtime (archive_entry*, ulong sec, long nsec); const(char) *archive_entry_symlink (archive_entry*); archive *archive_write_new (); int archive_write_free (archive*); int archive_write_close (archive*); int archive_write_add_filter_gzip (archive*); int archive_write_add_filter_xz (archive*); int archive_write_set_format_pax (archive*); int archive_write_set_format_pax_restricted (archive*); int archive_write_set_format_raw (archive*); //int archive_write_set_format_raw (archive *a); /// Will be available in the next version of libarchive (to be released in 2016) int archive_write_set_format_by_name (archive*, const(char) *name); int archive_write_open_filename (archive*, const(char) *file); int archive_write_header (archive*, archive_entry*); size_t archive_write_data(archive*, const(void)*, size_t); appstream-generator-0.8.6/src/asgen/bindings/lmdb.d000066400000000000000000000156521416066706400222740ustar00rootroot00000000000000/* * @author Howard Chu, Symas Corporation. * * @copyright Copyright 2011-2015 Howard Chu, Symas Corp. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * . * * @par Derived From: * This code is derived from btree.c written by Martin Hedenfalk. * * Copyright (c) 2009, 2010 Martin Hedenfalk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ module asgen.bindings.lmdb; // ////////////////////////////////////////////////////////////////////////// // // C API extern(C): nothrow: @nogc: static if (!is(typeof(usize))) private alias usize = size_t; alias mdb_mode_t = uint; struct mdb_filehandle_ts {} alias mdb_filehandle_t = mdb_filehandle_ts*; enum { MDB_VERSION_MAJOR = 0, MDB_VERSION_MINOR = 9, MDB_VERSION_PATCH = 27, MDB_VERSION_DATE = "October 26, 2020", } struct MDB_env_s {} alias MDB_envp = MDB_env_s*; struct MDB_txn_s {} alias MDB_txnp = MDB_txn_s*; alias MDB_dbi = uint; struct MDB_cursor_s {} alias MDB_cursorp = MDB_cursor_s*; struct MDB_val { usize mv_size; void* mv_data; } enum { MDB_FIXEDMAP = 0x01, MDB_NOSUBDIR = 0x4000, MDB_NOSYNC = 0x10000, MDB_RDONLY = 0x20000, MDB_NOMETASYNC = 0x40000, MDB_WRITEMAP = 0x80000, MDB_MAPASYNC = 0x100000, MDB_NOTLS = 0x200000, MDB_NOLOCK = 0x400000, MDB_NORDAHEAD = 0x800000, MDB_NOMEMINIT = 0x1000000 } enum { MDB_REVERSEKEY = 0x02, MDB_DUPSORT = 0x04, MDB_INTEGERKEY = 0x08, MDB_DUPFIXED = 0x10, MDB_INTEGERDUP = 0x20, MDB_REVERSEDUP = 0x40, MDB_CREATE = 0x40000 } enum { MDB_NOOVERWRITE = 0x10, MDB_NODUPDATA = 0x20, MDB_RESERVE = 0x10000, MDB_APPEND = 0x20000, MDB_APPENDDUP = 0x40000, MDB_MULTIPLE = 0x80000 } enum /*MDB_cursor_op*/ { MDB_FIRST, MDB_FIRST_DUP, MDB_GET_BOTH, MDB_GET_BOTH_RANGE, MDB_GET_CURRENT, MDB_GET_MULTIPLE, MDB_LAST, MDB_LAST_DUP, MDB_NEXT, MDB_NEXT_DUP, MDB_NEXT_MULTIPLE, MDB_NEXT_NODUP, MDB_PREV, MDB_PREV_DUP, MDB_PREV_NODUP, MDB_SET, MDB_SET_KEY, MDB_SET_RANGE, } enum { MDB_SUCCESS = 0, MDB_KEYEXIST = (-30799), MDB_NOTFOUND = (-30798), MDB_PAGE_NOTFOUND = (-30797), MDB_CORRUPTED = (-30796), MDB_PANIC = (-30795), MDB_VERSION_MISMATCH = (-30794), MDB_INVALID = (-30793), MDB_MAP_FULL = (-30792), MDB_DBS_FULL = (-30791), MDB_READERS_FULL = (-30790), MDB_TLS_FULL = (-30789), MDB_TXN_FULL = (-30788), MDB_CURSOR_FULL = (-30787), MDB_PAGE_FULL = (-30786), MDB_MAP_RESIZED = (-30785), MDB_INCOMPATIBLE = (-30784), MDB_BAD_RSLOT = (-30783), MDB_BAD_TXN = (-30782), MDB_BAD_VALSIZE = (-30781), MDB_BAD_DBI = (-30780), MDB_LAST_ERRCODE = MDB_BAD_DBI } struct MDB_stat { uint ms_psize; uint ms_depth; usize ms_branch_pages; usize ms_leaf_pages; usize ms_overflow_pages; usize ms_entries; } struct MDB_envinfo { void* me_mapaddr; usize me_mapsize; usize me_last_pgno; usize me_last_txnid; uint me_maxreaders; uint me_numreaders; } const(char)* mdb_version (int* major, int* minor, int* patch); const(char)* mdb_strerror (int err); int mdb_env_create (MDB_envp* env); int mdb_env_open (MDB_envp env, const(char)* path, uint flags, mdb_mode_t mode); int mdb_env_copy (MDB_envp env, const(char)* path); int mdb_env_copyfd (MDB_envp env, mdb_filehandle_t fd); int mdb_env_stat (MDB_envp env, MDB_stat* stat); int mdb_env_info (MDB_envp env, MDB_envinfo* stat); int mdb_env_sync (MDB_envp env, int force); void mdb_env_close (MDB_envp env); int mdb_env_set_flags (MDB_envp env, uint flags, int onoff); int mdb_env_get_flags (MDB_envp env, uint* flags); int mdb_env_get_path (MDB_envp env, const(char)** path); int mdb_env_get_fd (MDB_envp env, mdb_filehandle_t* fd); int mdb_env_set_mapsize (MDB_envp env, usize size); int mdb_env_set_maxreaders (MDB_envp env, uint readers); int mdb_env_get_maxreaders (MDB_envp env, uint* readers); int mdb_env_set_maxdbs (MDB_envp env, MDB_dbi dbs); int mdb_env_get_maxkeysize (MDB_envp env); int mdb_env_set_userctx (MDB_envp env, void* ctx); void* mdb_env_get_userctx (MDB_envp env); int mdb_env_set_assert (MDB_envp env, void function (MDB_envp env, const(char)* msg) func); int mdb_txn_begin (MDB_envp env, MDB_txnp parent, uint flags, MDB_txnp* txn); MDB_envp mdb_txn_env (MDB_txnp txn); usize mdb_txn_id (MDB_txnp txn); int mdb_txn_commit (MDB_txnp txn); void mdb_txn_abort (MDB_txnp txn); void mdb_txn_reset (MDB_txnp txn); int mdb_txn_renew (MDB_txnp txn); int mdb_dbi_open (MDB_txnp txn, const(char)* name, uint flags, MDB_dbi* dbi); int mdb_stat (MDB_txnp txn, MDB_dbi dbi, MDB_stat* stat); int mdb_dbi_flags (MDB_txnp txn, MDB_dbi dbi, uint* flags); void mdb_dbi_close (MDB_envp env, MDB_dbi dbi); int mdb_drop (MDB_txnp txn, MDB_dbi dbi, int del); int mdb_set_compare (MDB_txnp txn, MDB_dbi dbi, int function (MDB_val* a, MDB_val* b) cmp); int mdb_set_dupsort (MDB_txnp txn, MDB_dbi dbi, int function (MDB_val* a, MDB_val* b) cmp); int mdb_set_relfunc (MDB_txnp txn, MDB_dbi dbi, void function (MDB_val* item, void* oldptr, void* newptr, void* relctx) rel); int mdb_set_relctx (MDB_txnp txn, MDB_dbi dbi, void* ctx); int mdb_get (MDB_txnp txn, MDB_dbi dbi, MDB_val* key, MDB_val* data); int mdb_put (MDB_txnp txn, MDB_dbi dbi, MDB_val* key, MDB_val* data, uint flags); int mdb_del (MDB_txnp txn, MDB_dbi dbi, MDB_val* key, MDB_val* data); int mdb_cursor_open (MDB_txnp txn, MDB_dbi dbi, MDB_cursorp* cursor); void mdb_cursor_close (MDB_cursorp cursor); int mdb_cursor_renew (MDB_txnp txn, MDB_cursorp cursor); MDB_txnp mdb_cursor_txn (MDB_cursorp cursor); MDB_dbi mdb_cursor_dbi (MDB_cursorp cursor); int mdb_cursor_get (MDB_cursorp cursor, MDB_val* key, MDB_val* data, /*MDB_cursor_op*/uint op); int mdb_cursor_put (MDB_cursorp cursor, MDB_val* key, MDB_val* data, uint flags); int mdb_cursor_del (MDB_cursorp cursor, uint flags); int mdb_cursor_count (MDB_cursorp cursor, usize* countp); int mdb_cmp (MDB_txnp txn, MDB_dbi dbi, MDB_val* a, MDB_val* b); int mdb_dcmp (MDB_txnp txn, MDB_dbi dbi, MDB_val* a, MDB_val* b); int mdb_reader_list (MDB_envp env, int function (const(char)* msg, void* ctx) func, void* ctx); int mdb_reader_check (MDB_envp env, int* dead); appstream-generator-0.8.6/src/asgen/config.d000066400000000000000000000570121416066706400210220ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.config; import std.stdio; import std.array; import std.string : format, toLower; import std.path : dirName, buildPath, buildNormalizedPath, isAbsolute, absolutePath; import std.conv : to; import std.json; import std.typecons; import std.file : getcwd, thisExePath, exists; import ascompose.Globals : Globals; public import appstream.c.types : FormatVersion; import asgen.utils : existsAndIsDir, randomString, ImageSize; import asgen.logging; import asgen.defines : DATADIR; /** * Describes a suite in a software repository. **/ struct Suite { string name; int dataPriority = 0; string baseSuite; string iconTheme; string[] sections; string[] architectures; string extraMetainfoDir; bool isImmutable; } /** * The AppStream metadata type we want to generate. **/ enum DataType { XML, YAML } /** * Distribution-specific backends. **/ enum Backend { Unknown, Dummy, Debian, Ubuntu, Archlinux, RpmMd, Alpinelinux } /** * Generator features that can be toggled by the user. */ struct GeneratorFeatures { bool processDesktop; bool validate; bool noDownloads; bool storeScreenshots; bool optipng; bool metadataTimestamps; bool immutableSuites; bool processFonts; bool allowIconUpscale; bool processGStreamer; bool processLocale; bool screenshotVideos; bool propagateMetaInfoArtifacts; } /// Fake package name AppStream Generator uses internally to inject additional metainfo on users' request public immutable EXTRA_METAINFO_FAKE_PKGNAME = "+extra-metainfo"; /// A list of valid icon sizes that we recognize in AppStream public immutable allowedIconSizes = [ImageSize (48), ImageSize (48, 48, 2), ImageSize (64), ImageSize (64, 64, 2), ImageSize (128), ImageSize (128, 128, 2)]; /** * Policy on a single icon size. */ struct IconPolicy { ImageSize iconSize; /// Size of the icon this policy is about bool storeCached; /// True if the icon should be stored in an icon tarball and be cached locally. bool storeRemote; /// True if this icon should be stored remotely and fetched on demand bool storeIcon () @property const { return storeCached || storeRemote; } this (ImageSize size, bool cached, bool remote) { iconSize = size; storeCached = cached; storeRemote = remote; } } /** * The global configuration for the metadata generator. */ final class Config { private: string workspaceDir; string exportDir; string tmpDir; // thread local static bool instantiated_; // thread global __gshared Config instance_; this () { import glib.Util : Util; // our default export format version formatVersion = FormatVersion.V0_14; // find all the external binaries we (may) need // we search for them unconditionally, because the unittests may rely on their absolute // paths being set even if a particular feature flag that requires them isn't. optipngBinary = Globals.optipngBinary; ffprobeBinary = Util.findProgramInPath ("ffprobe"); } public: FormatVersion formatVersion; string projectName; string archiveRoot; string mediaBaseUrl; string htmlBaseUrl; string backendName; Backend backend; Suite[] suites; string[] oldsuites; DataType metadataType; GeneratorFeatures feature; /// Set which features are enabled or disabled string optipngBinary; string ffprobeBinary; bool[string] allowedCustomKeys; // set of allowed keys in tags string dataExportDir; string hintsExportDir; string mediaExportDir; string htmlExportDir; long maxScrFileSize; IconPolicy[] iconSettings; string caInfo; static Config get () { if (!instantiated_) { synchronized (Config.classinfo) { if (!instance_) instance_ = new Config (); instantiated_ = true; } } return instance_; } @property string formatVersionStr () { static import appstream.Utils; alias AsUtils = appstream.Utils.Utils; return AsUtils.formatVersionToString (formatVersion); } @property string databaseDir () const { return buildPath (workspaceDir, "db"); } @property string cacheRootDir () const { return buildPath (workspaceDir, "cache"); } @property string templateDir () { // find a suitable template directory // first check the workspace auto tdir = buildPath (workspaceDir, "templates"); tdir = getVendorTemplateDir (tdir, true); if (tdir.empty) { immutable exeDir = dirName (thisExePath ()); tdir = buildNormalizedPath (exeDir, "..", "..", "..", "data", "templates"); tdir = getVendorTemplateDir (tdir); if (tdir.empty) { tdir = getVendorTemplateDir (buildPath (DATADIR, "templates")); if (tdir.empty) { tdir = buildNormalizedPath (exeDir, "..", "data", "templates"); tdir = getVendorTemplateDir (tdir); } } } return tdir; } /** * Helper function to determine a vendor template directory. */ private string getVendorTemplateDir (const string dir, bool allowRoot = false) @safe { string tdir; if (!projectName.empty) { tdir = buildPath (dir, projectName.toLower); if (existsAndIsDir (tdir)) return tdir; } tdir = buildPath (dir, "default"); if (existsAndIsDir (tdir)) return tdir; if (allowRoot) { if (existsAndIsDir (dir)) return dir; } return null; } void loadFromFile (string fname, string enforcedWorkspaceDir = null, string enforcedExportDir = null) { // read the configuration JSON file auto f = File (fname, "r"); string jsonData; string line; while ((line = f.readln ()) !is null) jsonData ~= line; JSONValue root = parseJSON (jsonData); if ("WorkspaceDir" in root) { workspaceDir = root["WorkspaceDir"].str; } else { workspaceDir = dirName (fname); if (workspaceDir.empty) workspaceDir = getcwd (); } // allow overriding the workspace location if (!enforcedWorkspaceDir.empty) workspaceDir = enforcedWorkspaceDir; if (!workspaceDir.isAbsolute) workspaceDir = workspaceDir.absolutePath; this.projectName = "Unknown"; if ("ProjectName" in root) this.projectName = root["ProjectName"].str; this.archiveRoot = root["ArchiveRoot"].str; this.mediaBaseUrl = ""; if ("MediaBaseUrl" in root) this.mediaBaseUrl = root["MediaBaseUrl"].str; this.htmlBaseUrl = ""; if ("HtmlBaseUrl" in root) this.htmlBaseUrl = root["HtmlBaseUrl"].str; // set root export directory if (enforcedExportDir.empty) { exportDir = buildPath (workspaceDir, "export"); } else { exportDir = enforcedExportDir; logInfo ("Using data export directory root from the command-line: %s", exportDir); } if (!exportDir.isAbsolute) exportDir = exportDir.absolutePath; // set the default export directory locations, allow people to override them in the config // (we convert the relative to absolute paths later) mediaExportDir = "media"; dataExportDir = "data"; hintsExportDir = "hints"; htmlExportDir = "html"; if ("ExportDirs" in root) { auto edirs = root["ExportDirs"].object; foreach (dirId; edirs.byKeyValue) { switch (dirId.key) { case "Media": mediaExportDir = dirId.value.str; break; case "Data": dataExportDir = dirId.value.str; break; case "Hints": hintsExportDir = dirId.value.str; break; case "Html": htmlExportDir = dirId.value.str; break; default: logWarning ("Unknown export directory specifier in config: %s", dirId.key); } } } // convert export directory paths to absolute paths if necessary mediaExportDir = mediaExportDir.isAbsolute? mediaExportDir : buildNormalizedPath (exportDir, mediaExportDir); dataExportDir = dataExportDir.isAbsolute? dataExportDir : buildNormalizedPath (exportDir, dataExportDir); hintsExportDir = hintsExportDir.isAbsolute? hintsExportDir : buildNormalizedPath (exportDir, hintsExportDir); htmlExportDir = htmlExportDir.isAbsolute? htmlExportDir : buildNormalizedPath (exportDir, htmlExportDir); // a place where external metainfo data can be injected auto extraMetainfoDir = buildPath (workspaceDir, "extra-metainfo"); if ("ExtraMetainfoDir" in root) extraMetainfoDir = root["ExtraMetainfoDir"].str; this.metadataType = DataType.XML; if ("MetadataType" in root) if (root["MetadataType"].str.toLower == "yaml") this.metadataType = DataType.YAML; if ("CAInfo" in root) this.caInfo = root["CAInfo"].str; // allow specifying the AppStream format version we build data for. if ("FormatVersion" in root) { immutable versionStr = root["FormatVersion"].str; switch (versionStr) { case "0.8": formatVersion = FormatVersion.V0_8; break; case "0.9": formatVersion = FormatVersion.V0_9; break; case "0.10": formatVersion = FormatVersion.V0_10; break; case "0.11": formatVersion = FormatVersion.V0_11; break; case "0.12": formatVersion = FormatVersion.V0_12; break; default: logWarning ("Configuration tried to set unknown AppStream format version '%s'. Falling back to default version.", versionStr); break; } } // we default to the Debian backend for now auto backendId = "debian"; if ("Backend" in root) backendId = root["Backend"].str.toLower; switch (backendId) { case "dummy": this.backendName = "Dummy"; this.backend = Backend.Dummy; this.metadataType = DataType.YAML; break; case "debian": this.backendName = "Debian"; this.backend = Backend.Debian; this.metadataType = DataType.YAML; break; case "ubuntu": this.backendName = "Ubuntu"; this.backend = Backend.Ubuntu; this.metadataType = DataType.YAML; break; case "arch": case "archlinux": this.backendName = "Arch Linux"; this.backend = Backend.Archlinux; this.metadataType = DataType.XML; break; case "mageia": case "rpmmd": this.backendName = "RpmMd"; this.backend = Backend.RpmMd; this.metadataType = DataType.XML; break; case "alpinelinux": this.backendName = "Alpine Linux"; this.backend = Backend.Alpinelinux; this.metadataType = DataType.XML; break; default: break; } auto hasImmutableSuites = false; foreach (suiteName; root["Suites"].object.byKey) { Suite suite; suite.name = suiteName; // having a suite named "pool" will result in the media pool being copied on // itself if immutableSuites is used. Since 'pool' is a bad suite name anyway, // we error out early on this. if (suiteName == "pool") throw new Exception ("The name 'pool' is forbidden for a suite."); auto sn = root["Suites"][suiteName]; if ("dataPriority" in sn) suite.dataPriority = to!int (sn["dataPriority"].integer); if ("baseSuite" in sn) suite.baseSuite = sn["baseSuite"].str; if ("useIconTheme" in sn) suite.iconTheme = sn["useIconTheme"].str; if ("sections" in sn) foreach (sec; sn["sections"].array) suite.sections ~= sec.str; if ("architectures" in sn) foreach (arch; sn["architectures"].array) suite.architectures ~= arch.str; if ("immutable" in sn) { suite.isImmutable = sn["immutable"].type == JSONType.true_; if (suite.isImmutable) hasImmutableSuites = true; } const suiteExtraMIDir = buildNormalizedPath (extraMetainfoDir, suite.name); if (suiteExtraMIDir.existsAndIsDir) suite.extraMetainfoDir = suiteExtraMIDir; suites ~= suite; } if ("Oldsuites" in root.object) { import std.algorithm.iteration : map; oldsuites = map!"a.str"(root["Oldsuites"].array).array; } // icon policy if ("Icons" in root.object) { import std.algorithm : canFind; iconSettings.reserve (4); auto iconsObj = root["Icons"].object; foreach (iconString; iconsObj.byKey) { auto iconObj = iconsObj[iconString]; IconPolicy ipolicy; ipolicy.iconSize = ImageSize (iconString); if (!allowedIconSizes.canFind (ipolicy.iconSize)) { logError ("Invalid icon size '%s' selected in configuration, icon policy has been ignored.", iconString); continue; } if (ipolicy.iconSize.width < 0) { logError ("Malformed icon size '%s' found in configuration, icon policy has been ignored.", iconString); continue; } if ("remote" in iconObj) ipolicy.storeRemote = iconObj["remote"].type == JSONType.true_; if ("cached" in iconObj) ipolicy.storeCached = iconObj["cached"].type == JSONType.true_; if (ipolicy.storeIcon) iconSettings ~= ipolicy; } // Sanity check bool defaultSizeFound = false; foreach (ref ipolicy; iconSettings) { if (ipolicy.iconSize == ImageSize (64)) { defaultSizeFound = true; if (!ipolicy.storeCached) { logError ("The icon size 64x64 must always be present and be allowed to be cached. Configuration has been adjusted."); ipolicy.storeCached = true; break; } } } if (!defaultSizeFound) { logError ("The icon size 64x64 must always be present and be allowed to be cached. Configuration has been adjusted."); IconPolicy ipolicy; ipolicy.iconSize = ImageSize (64); ipolicy.storeCached = true; iconSettings ~= ipolicy; } } else { // no explicit icon policy was given, so we use a default policy iconSettings.reserve (6); iconSettings ~= IconPolicy (ImageSize (48), true, false); iconSettings ~= IconPolicy (ImageSize (48, 48, 2), true, false); iconSettings ~= IconPolicy (ImageSize (64), true, false); iconSettings ~= IconPolicy (ImageSize (64, 64, 2), true, false); iconSettings ~= IconPolicy (ImageSize (128), true, true); iconSettings ~= IconPolicy (ImageSize (128, 128, 2), true, true); } this.maxScrFileSize = 14; // 14MiB is the default maximum size if ("MaxScreenshotFileSize" in root) this.maxScrFileSize = root["MaxScreenshotFileSize"].integer; if ("AllowedCustomKeys" in root.object) foreach (ref key; root["AllowedCustomKeys"].array) allowedCustomKeys[key.str] = true; // Enable features which are default-enabled feature.processDesktop = true; feature.validate = true; feature.storeScreenshots = true; feature.optipng = true; feature.metadataTimestamps = true; feature.immutableSuites = true; feature.processFonts = true; feature.allowIconUpscale = true; feature.processGStreamer = true; feature.processLocale = true; feature.screenshotVideos = true; // apply vendor feature settings if ("Features" in root.object) { auto featuresObj = root["Features"].object; foreach (featureId; featuresObj.byKey ()) { switch (featureId) { case "validateMetainfo": feature.validate = featuresObj[featureId].type == JSONType.true_; break; case "processDesktop": feature.processDesktop = featuresObj[featureId].type == JSONType.true_; break; case "noDownloads": feature.noDownloads = featuresObj[featureId].type == JSONType.true_; break; case "createScreenshotsStore": feature.storeScreenshots = featuresObj[featureId].type == JSONType.true_; break; case "optimizePNGSize": feature.optipng = featuresObj[featureId].type == JSONType.true_; break; case "metadataTimestamps": feature.metadataTimestamps = featuresObj[featureId].type == JSONType.true_; break; case "immutableSuites": feature.immutableSuites = featuresObj[featureId].type == JSONType.true_; break; case "processFonts": feature.processFonts = featuresObj[featureId].type == JSONType.true_; break; case "allowIconUpscaling": feature.allowIconUpscale = featuresObj[featureId].type == JSONType.true_; break; case "processGStreamer": feature.processGStreamer = featuresObj[featureId].type == JSONType.true_; break; case "processLocale": feature.processLocale = featuresObj[featureId].type == JSONType.true_; break; case "screenshotVideos": feature.screenshotVideos = featuresObj[featureId].type == JSONType.true_; break; case "propagateMetaInfoArtifacts": feature.propagateMetaInfoArtifacts = featuresObj[featureId].type == JSONType.true_; break; default: break; } } } // check if we need to disable features because some prerequisites are not met if (feature.optipng) { if (optipngBinary.empty) { feature.optipng = false; logError ("Disabled feature `optimizePNGSize`: The `optipng` binary was not found."); } else { logDebug ("Using `optipng`: %s", optipngBinary); } } Globals.setUseOptipng (feature.optipng); if (feature.screenshotVideos) { if (ffprobeBinary.empty) { feature.screenshotVideos = false; logError ("Disabled feature `screenshotVideos`: The `ffprobe` binary was not found."); } else { logDebug ("Using `ffprobe`: %s", ffprobeBinary); } } if (feature.noDownloads) { // since disallowing network access might have quite a lot of sideeffects, we print // a message to the logs to make debugging easier. // in general, running with noDownloads is discouraged. logWarning ("Configuration does not permit downloading files. Several features will not be available."); } if (!feature.immutableSuites) { // Immutable suites won't work if the feature is disabled - log this error if (hasImmutableSuites) logError ("Suites are defined as immutable, but the `immutableSuites` feature is disabled. Immutability will not work!"); } if (!feature.validate) logWarning ("MetaInfo validation has been disabled in configuration."); // sanity check to warn if our GdkPixbuf does not support the minimum amount // of image formats we need import ascompose.Image : Image; import std.string : toStringz; auto pbFormatNames = Image.supportedFormatNames (); if (!pbFormatNames.contains(cast(char*) "png".toStringz) || !pbFormatNames.contains(cast(char*) "svg".toStringz) || !pbFormatNames.contains(cast(char*) "jpeg".toStringz)) { logError ("The currently used GdkPixbuf does not seem to support all image formats we require to run normally (png/svg/jpeg). " ~ "This may be a problem with your installation of appstream-generator or gdk-pixbuf."); } } bool isValid () { return this.projectName != null; } /** * Get unique temporary directory to use during one generator run. */ string getTmpDir () { synchronized (this) { if (tmpDir.empty) { string root; if (cacheRootDir.empty) root = "/tmp/"; else root = cacheRootDir; tmpDir = buildPath (root, "tmp", format ("asgen-%s", randomString (8))); // make appstream-compose internal functions aware of the new temp dir Globals.setTmpDir (tmpDir); } } return tmpDir; } } appstream-generator-0.8.6/src/asgen/contentsstore.d000066400000000000000000000275621416066706400224760ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.contentsstore; import std.stdio; import std.string; import std.conv : to, octal; import std.file : mkdirRecurse; import std.array : appender, join, split, empty; import asgen.bindings.lmdb; import asgen.config; import asgen.logging; /** * Contains a cache about available files in packages. * This is useful for finding icons and for re-scanning * packages which become interesting later. **/ final class ContentsStore { private: MDB_envp dbEnv; MDB_dbi dbContents; MDB_dbi dbIcons; MDB_dbi dbLocale; bool opened; public: this () { opened = false; } ~this () { close (); } private void checkError (int rc, string msg) { if (rc != 0) { import std.format; throw new Exception (format ("%s[%s]: %s", msg, rc, mdb_strerror (rc).fromStringz)); } } void open (string dir) { static import std.math; int rc; assert (!opened); logDebug ("Opening contents cache."); // ensure the cache directory exists mkdirRecurse (dir); rc = mdb_env_create (&dbEnv); scope (success) opened = true; scope (failure) dbEnv.mdb_env_close (); checkError (rc, "mdb_env_create"); // We are going to use at max 3 sub-databases: // contents, icons and locale rc = dbEnv.mdb_env_set_maxdbs (3); checkError (rc, "mdb_env_set_maxdbs"); // set a huge map size to be futureproof. // This means we're cruel to non-64bit users, but this // software is supposed to be run on 64bit machines anyway. auto mapsize = cast (size_t) std.math.pow (512L, 4); rc = dbEnv.mdb_env_set_mapsize (mapsize); checkError (rc, "mdb_env_set_mapsize"); // open database rc = dbEnv.mdb_env_open (dir.toStringz (), MDB_NOMETASYNC, octal!755); checkError (rc, "mdb_env_open"); // open sub-databases in the environment MDB_txnp txn; rc = dbEnv.mdb_txn_begin (null, 0, &txn); checkError (rc, "mdb_txn_begin"); scope (failure) txn.mdb_txn_abort (); // contains a full list of all contents rc = txn.mdb_dbi_open ("contents", MDB_CREATE, &dbContents); checkError (rc, "open contents database"); // contains list of icon files and related data // the contents sub-database exists only to allow building instances // of IconHandler much faster. rc = txn.mdb_dbi_open ("icondata", MDB_CREATE, &dbIcons); checkError (rc, "open icon-info database"); // contains list of locale files and related data rc = txn.mdb_dbi_open ("localedata", MDB_CREATE, &dbLocale); checkError (rc, "open locale-info database"); rc = txn.mdb_txn_commit (); checkError (rc, "mdb_txn_commit"); } void open (Config conf) { import std.path : buildPath; this.open (buildPath (conf.databaseDir, "contents")); } void close () { synchronized (this) { if (opened) dbEnv.mdb_env_close (); opened = false; dbEnv = null; } } private MDB_val makeDbValue (const string data) { import core.stdc.string : strlen; MDB_val mval; auto cdata = data.toStringz (); mval.mv_size = char.sizeof * strlen (cdata) + 1; mval.mv_data = cast(void*) cdata; return mval; } private MDB_txnp newTransaction (uint flags = 0) in { assert (opened); } do { int rc; MDB_txnp txn; rc = dbEnv.mdb_txn_begin (null, flags, &txn); checkError (rc, "mdb_txn_begin"); return txn; } private void commitTransaction (MDB_txnp txn) { auto rc = txn.mdb_txn_commit (); checkError (rc, "mdb_txn_commit"); } private void quitTransaction (MDB_txnp txn) { if (txn is null) return; txn.mdb_txn_abort (); } /** * Drop a package-id from the contents cache. */ void removePackage (string pkid) { MDB_val key; key = makeDbValue (pkid); auto txn = newTransaction (); scope (success) commitTransaction (txn); scope (failure) quitTransaction (txn); auto res = txn.mdb_del (dbContents, &key, null); checkError (res, "mdb_del (contents)"); res = txn.mdb_del (dbIcons, &key, null); if (res != MDB_NOTFOUND) checkError (res, "mdb_del (icons)"); res = txn.mdb_del (dbLocale, &key, null); if (res != MDB_NOTFOUND) checkError (res, "mdb_del (locale)"); } bool packageExists (const string pkid) { MDB_val dkey; MDB_cursorp cur; dkey = makeDbValue (pkid); auto txn = newTransaction (MDB_RDONLY); scope (exit) quitTransaction (txn); auto res = txn.mdb_cursor_open (dbContents, &cur); scope (exit) cur.mdb_cursor_close (); checkError (res, "mdb_cursor_open"); res = cur.mdb_cursor_get (&dkey, null, MDB_SET); if (res == MDB_NOTFOUND) return false; checkError (res, "mdb_cursor_get"); return true; } void addContents (string pkid, string[] contents) { // filter out icon filenames and filenames of icon-related stuff (e.g. theme.index), // as well as locale information auto iconInfo = appender!(string[]); auto localeInfo = appender!(string[]); foreach (ref f; contents) { if ((f.startsWith ("/usr/share/icons/")) || (f.startsWith ("/usr/share/pixmaps/"))) { iconInfo ~= f; continue; } // create a huge index of all Gettext and Qt translation filenames if (f.endsWith (".mo") || f.endsWith (".qm")) { localeInfo ~= f; continue; } } immutable contentsStr = contents.join ("\n"); synchronized (this) { MDB_val key, contentsVal; key = makeDbValue (pkid); contentsVal = makeDbValue (contentsStr); auto txn = newTransaction (); scope (success) commitTransaction (txn); scope (failure) quitTransaction (txn); auto res = txn.mdb_put (dbContents, &key, &contentsVal, 0); checkError (res, "mdb_put"); // if we have icon information, store that too if (!iconInfo.data.empty) { MDB_val iconsVal; immutable iconsStr = iconInfo.data.join ("\n"); iconsVal = makeDbValue (iconsStr); res = txn.mdb_put (dbIcons, &key, &iconsVal, 0); checkError (res, "mdb_put (icons)"); } // store locale if (!localeInfo.data.empty) { MDB_val localeVal; immutable localeStr = localeInfo.data.join ("\n"); localeVal = makeDbValue (localeStr); res = txn.mdb_put (dbLocale, &key, &localeVal, 0); checkError (res, "mdb_put (locale)"); } } } private string[string] getFilesMap (string[] pkids, MDB_dbi dbi, bool useBaseName = false) { import std.path : baseName; MDB_cursorp cur; auto txn = newTransaction (MDB_RDONLY); scope (exit) quitTransaction (txn); auto res = txn.mdb_cursor_open (dbi, &cur); scope (exit) cur.mdb_cursor_close (); checkError (res, "mdb_cursor_open"); string[string] pkgCMap; foreach (ref pkid; pkids) { MDB_val pkey = makeDbValue (pkid); MDB_val cval; res = cur.mdb_cursor_get (&pkey, &cval, MDB_SET); if (res == MDB_NOTFOUND) continue; checkError (res, "mdb_cursor_get"); auto data = fromStringz (cast(char*) cval.mv_data); auto contents = to!string (data); foreach (const ref c; contents.split ("\n")) { if (useBaseName) pkgCMap[c.baseName] = pkid; else pkgCMap[c] = pkid; } } return pkgCMap; } auto getContentsMap (string[] pkids) { return getFilesMap (pkids, dbContents); } auto getIconFilesMap (string[] pkids) { return getFilesMap (pkids, dbIcons); } auto getLocaleMap (string[] pkids) { // we make the assumption here that all locale for a given domain are in one package. // otherwise this global search will get even more insane. return getFilesMap (pkids, dbLocale); } private string[] getContentsList (string pkid, MDB_dbi dbi) { MDB_val pkey, cval; MDB_cursorp cur; pkey = makeDbValue (pkid); auto txn = newTransaction (MDB_RDONLY); scope (exit) quitTransaction (txn); auto res = txn.mdb_cursor_open (dbi, &cur); scope (exit) cur.mdb_cursor_close (); checkError (res, "mdb_cursor_open"); res = cur.mdb_cursor_get (&pkey, &cval, MDB_SET); if (res == MDB_NOTFOUND) return null; checkError (res, "mdb_cursor_get"); auto data = fromStringz (cast(char*) cval.mv_data); auto contentsStr = to!string (data); return contentsStr.split ("\n"); } string[] getContents (string pkid) { return getContentsList (pkid, dbContents); } string[] getIcons (string pkid) { return getContentsList (pkid, dbIcons); } string[] getLocaleFiles (string pkid) { return getContentsList (pkid, dbLocale); } bool[immutable string] getPackageIdSet () { MDB_cursorp cur; auto txn = newTransaction (); scope (exit) quitTransaction (txn); auto res = txn.mdb_cursor_open (dbContents, &cur); scope (exit) cur.mdb_cursor_close (); checkError (res, "mdb_cursor_open (getPackageIdSet)"); bool[immutable string] pkgSet; MDB_val pkey; while (cur.mdb_cursor_get (&pkey, null, MDB_NEXT) == 0) { immutable pkid = to!string (fromStringz (cast(char*) pkey.mv_data)); pkgSet[pkid] = true; } return pkgSet; } void removePackages (ref bool[immutable string] pkidSet) { auto txn = newTransaction (); scope (success) commitTransaction (txn); scope (failure) quitTransaction (txn); foreach (ref pkid; pkidSet.byKey) { auto key = makeDbValue (pkid); auto res = txn.mdb_del (dbContents, &key, null); checkError (res, "mdb_del (contents)"); res = txn.mdb_del (dbIcons, &key, null); if (res != MDB_NOTFOUND) checkError (res, "mdb_del (icons)"); res = txn.mdb_del (dbLocale, &key, null); if (res != MDB_NOTFOUND) checkError (res, "mdb_del (locale)"); } } void sync () in { assert (opened); } do { dbEnv.mdb_env_sync (1); } } appstream-generator-0.8.6/src/asgen/datainjectpkg.d000066400000000000000000000107471416066706400223710ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ import std.stdio : File; import std.array : empty; import std.path : buildPath, buildNormalizedPath, relativePath, baseName; import asgen.backends.interfaces; import asgen.utils : GENERIC_BUFFER_SIZE, existsAndIsDir; import asgen.logging; /** * Fake package which has the sole purpose of allowing easy injection of local * data that does not reside in packages. */ final class DataInjectPackage : Package { private: string pkgname; string pkgarch; string pkgmaintainer; string[string] desc; string[string] _contents; string _dataLocation; string _archDataLocation; public: @property override string name () const { return pkgname; } @property override string ver () const { return "0~0"; } @property override string arch () const { return pkgarch; } @property override PackageKind kind () @safe pure { return PackageKind.FAKE; } @property override const(string[string]) description () const { return desc; } override @property string getFilename () const { return "_local_"; } override @property string maintainer () const { return pkgmaintainer; } @property void maintainer (string maint) { pkgmaintainer = maint; } @property string dataLocation () const { return _dataLocation; } @property void dataLocation (string value) { _dataLocation = value; } @property string archDataLocation () const { return _archDataLocation; } @property void archDataLocation (string value) { _archDataLocation = value; } this (string pname, string parch) { pkgname = pname; pkgarch = parch; } override ubyte[] getFileData (string fname) { immutable localPath = _contents.get (fname, null); if (localPath.empty) return []; ubyte[] data; auto f = File (localPath, "r"); while (!f.eof) { char[GENERIC_BUFFER_SIZE] buf; data ~= f.rawRead (buf); } return data; } @property override string[] contents () { import std.file : dirEntries, SpanMode; import std.array : array; if (!_contents.empty) return array(_contents.byKey); if (_dataLocation.empty || !_dataLocation.existsAndIsDir) return []; // find all icons immutable iconLocation = buildNormalizedPath (_dataLocation, "icons"); foreach (iconFname; iconLocation.dirEntries ("*.{svg,svgz,png}", SpanMode.breadth, true)) { immutable iconBasePath = relativePath (iconFname, iconLocation); _contents[buildPath ("/usr/share/icons/hicolor", iconBasePath)] = iconFname; } // find metainfo files foreach (miFname; _dataLocation.dirEntries ("*.xml", SpanMode.shallow, false)) { immutable miBasename = miFname.baseName; logDebug ("Found injected metainfo [%s]: %s", "all", miBasename); _contents[buildPath ("/usr/share/metainfo", miBasename)] = miFname; } if (!archDataLocation.existsAndIsDir) return array(_contents.byKey); // load arch-specific override metainfo files foreach (miFname; archDataLocation.dirEntries ("*.xml", SpanMode.shallow, false)) { immutable miBasename = miFname.baseName; immutable fakePath = buildPath ("/usr/share/metainfo", miBasename); if (fakePath in _contents) logDebug ("Found injected metainfo [%s]: %s (replacing generic one)", arch, miBasename); else logDebug ("Found injected metainfo [%s]: %s", arch, miBasename); _contents[fakePath] = miFname; } return array(_contents.byKey); } override void finish () { } } appstream-generator-0.8.6/src/asgen/datastore.d000066400000000000000000000543731416066706400215520ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.datastore; import std.stdio; import std.string; import std.conv : to, octal; import std.file : mkdirRecurse; import std.path : buildPath, buildNormalizedPath, pathSplitter; import std.array : appender; import std.typecons : Tuple, scoped; import std.json; import asgen.bindings.lmdb; import appstream.Metadata; import appstream.Component; import asgen.config; import asgen.logging; import asgen.config : DataType; import asgen.result; /** * Main database containing information about scanned packages, * the components they provide, the component metadata itself, * issues found as well as statistics about the metadata evolution * over time. **/ final class DataStore { private: MDB_envp dbEnv; MDB_dbi dbRepoInfo; MDB_dbi dbPackages; MDB_dbi dbDataXml; MDB_dbi dbDataYaml; MDB_dbi dbHints; MDB_dbi dbStats; bool opened; Metadata mdata; string mediaDir; public: struct StatisticsEntry { size_t time; JSONValue data; } this () { opened = false; mdata = new Metadata (); mdata.setLocale ("ALL"); mdata.setFormatVersion (Config.get ().formatVersion); mdata.setWriteHeader(false); } ~this () { close (); } @property string mediaExportPoolDir () { return mediaDir; } private void checkError (int rc, string msg) { if (rc != 0) { import std.format; throw new Exception (format ("%s[%s]: %s", msg, rc, mdb_strerror (rc).fromStringz)); } } private void printVersionDbg () { import std.stdio : writeln; int major, minor, patch; auto ver = mdb_version (&major, &minor, &patch); logDebug ("Using %s major=%s minor=%s patch=%s", ver.fromStringz, major, minor, patch); } void open (string dir, string mediaBaseDir) { static import std.math; int rc; assert (!opened); // add LMDB version we are using to the debug output printVersionDbg (); // ensure the cache directory exists mkdirRecurse (dir); rc = mdb_env_create (&dbEnv); scope (success) opened = true; scope (failure) dbEnv.mdb_env_close (); checkError (rc, "mdb_env_create"); // We are going to use at max 6 sub-databases: // packages, hints, metadata_xml, metadata_yaml, statistics rc = dbEnv.mdb_env_set_maxdbs (6); checkError (rc, "mdb_env_set_maxdbs"); // set a huge map size to be futureproof. // This means we're cruel to non-64bit users, but this // software is supposed to be run on 64bit machines anyway. auto mapsize = cast (size_t) std.math.pow (512L, 4); rc = dbEnv.mdb_env_set_mapsize (mapsize); checkError (rc, "mdb_env_set_mapsize"); // open database rc = dbEnv.mdb_env_open (dir.toStringz (), MDB_NOMETASYNC, octal!755); checkError (rc, "mdb_env_open"); // open sub-databases in the environment MDB_txnp txn; rc = dbEnv.mdb_txn_begin (null, 0, &txn); checkError (rc, "mdb_txn_begin"); scope (failure) txn.mdb_txn_abort (); rc = txn.mdb_dbi_open ("packages", MDB_CREATE, &dbPackages); checkError (rc, "open packages database"); rc = txn.mdb_dbi_open ("repository", MDB_CREATE, &dbRepoInfo); checkError (rc, "open repository database"); rc = txn.mdb_dbi_open ("metadata_xml", MDB_CREATE, &dbDataXml); checkError (rc, "open metadata (xml) database"); rc = txn.mdb_dbi_open ("metadata_yaml", MDB_CREATE, &dbDataYaml); checkError (rc, "open metadata (yaml) database"); rc = txn.mdb_dbi_open ("hints", MDB_CREATE, &dbHints); checkError (rc, "open hints database"); rc = txn.mdb_dbi_open ("statistics", MDB_CREATE | MDB_INTEGERKEY, &dbStats); checkError (rc, "open statistics database"); rc = txn.mdb_txn_commit (); checkError (rc, "mdb_txn_commit"); this.mediaDir = buildPath (mediaBaseDir, "pool"); mkdirRecurse (this.mediaDir); } void close () { synchronized (this) { if (opened) dbEnv.mdb_env_close (); opened = false; dbEnv = null; } } void open (Config conf) { this.open (buildPath (conf.databaseDir, "main"), conf.mediaExportDir); } private MDB_val makeDbValue (string data) { import core.stdc.string : strlen; MDB_val mval; auto cdata = data.toStringz (); mval.mv_size = char.sizeof * strlen (cdata) + 1; mval.mv_data = cast(void*) cdata; return mval; } private MDB_txnp newTransaction (uint flags = 0) in { assert (opened); } do { int rc; MDB_txnp txn; rc = dbEnv.mdb_txn_begin (null, flags, &txn); checkError (rc, "mdb_txn_begin"); return txn; } private void commitTransaction (MDB_txnp txn) { auto rc = txn.mdb_txn_commit (); checkError (rc, "mdb_txn_commit"); } private void quitTransaction (MDB_txnp txn) { if (txn is null) return; txn.mdb_txn_abort (); } private void putKeyValue (MDB_dbi dbi, string key, string value) { MDB_val dbkey, dbvalue; dbkey = makeDbValue (key); dbvalue = makeDbValue (value); auto txn = newTransaction (); scope (success) commitTransaction (txn); scope (failure) quitTransaction (txn); auto res = txn.mdb_put (dbi, &dbkey, &dbvalue, 0); checkError (res, "mdb_put"); } private string getValue (MDB_dbi dbi, MDB_val dkey) { import std.conv; MDB_val dval; MDB_cursorp cur; auto txn = newTransaction (MDB_RDONLY); scope (exit) quitTransaction (txn); auto res = txn.mdb_cursor_open (dbi, &cur); scope (exit) cur.mdb_cursor_close (); checkError (res, "mdb_cursor_open"); res = cur.mdb_cursor_get (&dkey, &dval, MDB_SET); if (res == MDB_NOTFOUND) return null; checkError (res, "mdb_cursor_get"); auto data = fromStringz (cast(char*) dval.mv_data); return to!string (data); } private string getValue (MDB_dbi dbi, string key) { MDB_val dkey; dkey = makeDbValue (key); return getValue (dbi, dkey); } bool metadataExists (DataType dtype, string gcid) { return getMetadata (dtype, gcid) !is null; } void setMetadata (DataType dtype, string gcid, string asdata) { if (dtype == DataType.XML) putKeyValue (dbDataXml, gcid, asdata); else putKeyValue (dbDataYaml, gcid, asdata); } string getMetadata (DataType dtype, string gcid) { string data; if (dtype == DataType.XML) data = getValue (dbDataXml, gcid); else data = getValue (dbDataYaml, gcid); return data; } bool hasHints (string pkid) { return getValue (dbHints, pkid) !is null; } void setHints (string pkid, string hintsYaml) { putKeyValue (dbHints, pkid, hintsYaml); } string getHints (string pkid) { return getValue (dbHints, pkid); } string getPackageValue (string pkid) { return getValue (dbPackages, pkid); } void setPackageIgnore (string pkid) { putKeyValue (dbPackages, pkid, "ignore"); } bool isIgnored (string pkid) { auto val = getValue (dbPackages, pkid); return val == "ignore"; } bool packageExists (string pkid) { auto val = getValue (dbPackages, pkid); return val !is null; } void addGeneratorResult (DataType dtype, ref GeneratorResult gres, const bool alwaysRegenerate = false) { // if the package has no components or hints, // mark it as always-ignore if (gres.unitIgnored) { setPackageIgnore (gres.pkid); return; } auto cptsPtrArray = gres.fetchComponents (); for (uint i = 0; i < cptsPtrArray.len; i++) { auto cpt = new Component (cast (AsComponent*) cptsPtrArray.index (i)); auto gcid = gres.gcidForComponent (cpt); if (metadataExists (dtype, gcid) && !alwaysRegenerate) { // we already have seen this exact metadata - only adjust the reference, // and don't regenerate it. continue; } mdata.clearComponents (); mdata.addComponent (cpt); // convert out compoent into metadata string data; try { if (dtype == DataType.XML) { data = mdata.componentsToCollection (FormatKind.XML); } else { data = mdata.componentsToCollection (FormatKind.YAML); } } catch (Exception e) { gres.addHint (cpt, "metadata-serialization-failed", e.msg); continue; } // remove trailing whitespaces and linebreaks data = data.stripRight (); // store metadata if (!empty (data)) setMetadata (dtype, gcid, data); } if (gres.hintsCount () > 0) { auto hintsJson = gres.hintsToJson (); if (!hintsJson.empty) setHints (gres.pkid, hintsJson); } auto gcids = gres.getComponentGcids (); if (gcids.empty) { // no global components, and we're not ignoring this component. // this means we likely have hints stored for this one. Mark it // as "seen" so we don't reprocess it again. putKeyValue (dbPackages, gres.pkid, "seen"); } else { import std.array : join; // store global component IDs for this package as newline-separated list auto gcidVal = join (gcids, "\n"); putKeyValue (dbPackages, gres.pkid, gcidVal); } } string[] getGCIDsForPackage (string pkid) { auto pkval = getPackageValue (pkid); if (pkval == "ignore") return null; if (pkval == "seen") return null; auto validCids = appender!(string[]); auto cids = pkval.split ("\n"); foreach (cid; cids) { if (cid.empty) continue; validCids ~= cid; } return validCids.data; } string[] getMetadataForPackage (DataType dtype, string pkid) { auto gcids = getGCIDsForPackage (pkid); if (gcids is null) return null; auto res = appender!(string[]); res.reserve (gcids.length); foreach (cid; gcids) { auto data = getMetadata (dtype, cid); if (!data.empty) res ~= data; } return res.data; } /** * Drop a package from the database. This process might leave cruft behind, * which can be collected using the cleanupCruft() method. */ void removePackage (string pkid) { MDB_val dbkey; dbkey = makeDbValue (pkid); auto txn = newTransaction (); scope (success) commitTransaction (txn); scope (failure) quitTransaction (txn); auto res = txn.mdb_del (dbPackages, &dbkey, null); if (res != MDB_NOTFOUND) checkError (res, "mdb_del"); res = txn.mdb_del (dbHints, &dbkey, null); if (res != MDB_NOTFOUND) checkError (res, "mdb_del"); } private auto getActiveGCIDs () { MDB_val dkey, dval; MDB_cursorp cur; auto txn = newTransaction (MDB_RDONLY); scope (exit) quitTransaction (txn); auto res = txn.mdb_cursor_open (dbPackages, &cur); scope (exit) cur.mdb_cursor_close (); checkError (res, "mdb_cursor_open (gcids)"); bool[string] gcids; while (cur.mdb_cursor_get (&dkey, &dval, MDB_NEXT) == 0) { immutable pkval = to!string (fromStringz (cast(char*) dval.mv_data)); if ((pkval == "ignore") || (pkval == "seen")) continue; foreach (gcid; pkval.split ("\n")) gcids[gcid] = true; } return gcids; } void cleanupCruft () { import std.file; import std.array : array; if (mediaDir is null) { logError ("Can not clean up cruft: No media directory is set."); return; } auto activeGCIDs = getActiveGCIDs (); bool gcidReferenced (string gcid) { // we use an associative array as a set here return (gcid in activeGCIDs) !is null; } void dropOrphanedData (MDB_dbi dbi) { MDB_cursorp cur; auto txn = newTransaction (); scope (success) commitTransaction (txn); scope (failure) quitTransaction (txn); auto res = txn.mdb_cursor_open (dbi, &cur); scope (exit) cur.mdb_cursor_close (); checkError (res, "mdb_cursor_open (stats)"); MDB_val ckey; while (cur.mdb_cursor_get (&ckey, null, MDB_NEXT) == 0) { immutable gcid = to!string (fromStringz (cast(char*) ckey.mv_data)); if (gcidReferenced (gcid)) continue; // if we got here, the component is cruft and can be removed res = cur.mdb_cursor_del (0); checkError (res, "mdb_del"); logInfo ("Marked %s as cruft.", gcid); } } bool dirEmpty (string dir) { bool empty = true; foreach (ref e; dirEntries (dir, SpanMode.shallow, false)) { empty = false; break; } return empty; } void cleanupDirs (string rootPath) { auto pdir = buildNormalizedPath (rootPath, ".."); if (!std.file.exists (pdir)) return; if (dirEmpty (pdir)) rmdir (pdir); pdir = buildNormalizedPath (pdir, ".."); if (dirEmpty (pdir)) rmdir (pdir); } // drop orphaned metadata dropOrphanedData (dbDataXml); dropOrphanedData (dbDataYaml); // we need the global Config instance here auto conf = Config.get (); auto mdirLen = mediaDir.length; foreach (ref path; dirEntries (mediaDir, SpanMode.depth, false)) { if (path.length <= mdirLen) continue; immutable relPath = path[mdirLen+1..$]; auto split = array (pathSplitter (relPath)); if (split.length != 4) continue; immutable gcid = relPath; if (gcidReferenced (gcid)) continue; // if we are here, the component is removed and we can drop its media if (std.file.exists (path)) rmdirRecurse (path); // remove possibly empty directories cleanupDirs (path); // expire data in suite-specific media directories, // if suite is not marked as immutable if (conf.feature.immutableSuites) { foreach (ref suite; conf.suites) { if (suite.isImmutable) continue; immutable suiteGCIDMediaDir = buildNormalizedPath (mediaDir, "..", suite.name, gcid); if (std.file.exists (suiteGCIDMediaDir)) rmdirRecurse (suiteGCIDMediaDir); // remove possibly empty directories cleanupDirs (suiteGCIDMediaDir); } } logInfo ("Expired media for '%s'", gcid); } } bool[immutable string] getPackageIdSet () { MDB_cursorp cur; auto txn = newTransaction (); scope (exit) quitTransaction (txn); bool[immutable string] pkgSet; auto res = txn.mdb_cursor_open (dbPackages, &cur); scope (exit) cur.mdb_cursor_close (); checkError (res, "mdb_cursor_open (getPackageIdSet)"); MDB_val pkey; while (cur.mdb_cursor_get (&pkey, null, MDB_NEXT) == 0) { immutable pkid = to!string (fromStringz (cast(char*) pkey.mv_data)); pkgSet[pkid] = true; } return pkgSet; } void removePackages (ref bool[immutable string] pkidSet) { auto txn = newTransaction (); scope (success) commitTransaction (txn); scope (failure) quitTransaction (txn); foreach (ref pkid; pkidSet.byKey) { auto dbkey = makeDbValue (pkid); auto res = txn.mdb_del (dbPackages, &dbkey, null); if (res != MDB_NOTFOUND) checkError (res, "mdb_del (metadata)"); res = txn.mdb_del (dbHints, &dbkey, null); if (res != MDB_NOTFOUND) checkError (res, "mdb_del (hints)"); logInfo ("Dropped package %s", pkid); } } StatisticsEntry[] getStatistics () { MDB_val dkey, dval; MDB_cursorp cur; auto txn = newTransaction (MDB_RDONLY); scope (exit) quitTransaction (txn); auto res = txn.mdb_cursor_open (dbStats, &cur); scope (exit) cur.mdb_cursor_close (); checkError (res, "mdb_cursor_open (stats)"); auto stats = appender!(StatisticsEntry[]); stats.reserve (128); while (cur.mdb_cursor_get (&dkey, &dval, MDB_NEXT) == 0) { auto jsonData = to!string (fromStringz (cast(char*) dval.mv_data)); auto timestamp = *(cast(size_t*) dkey.mv_data); auto sentry = StatisticsEntry (timestamp, parseJSON (jsonData)); stats ~= sentry; } return stats.data; } void removeStatistics (size_t time) { MDB_val dbkey; dbkey.mv_size = size_t.sizeof; dbkey.mv_data = &time; auto txn = newTransaction (); scope (success) commitTransaction (txn); scope (failure) quitTransaction (txn); auto res = txn.mdb_del (dbStats, &dbkey, null); if (res != MDB_NOTFOUND) checkError (res, "mdb_del"); } void addStatistics (JSONValue stats) { import core.stdc.time : time; MDB_val dbkey, dbvalue; size_t unixTime = time (null); auto statsJsonStr = stats.toString (); dbkey.mv_size = size_t.sizeof; dbkey.mv_data = &unixTime; dbvalue = makeDbValue (statsJsonStr); auto txn = newTransaction (); scope (success) commitTransaction (txn); scope (failure) quitTransaction (txn); auto res = txn.mdb_put (dbStats, &dbkey, &dbvalue, MDB_APPEND); if (res == MDB_KEYEXIST) { // this point in time already exists, so we need to extend it with additional data // retrieve the old statistics data auto existingJsonData = getValue (dbStats, dbkey); auto existingJson = parseJSON (existingJsonData); // make the new JSON a list of the old and the new data, if it isn't one already JSONValue newJson; if (existingJson.type == JSONType.array) { newJson = existingJson; newJson.array ~= stats; } else { newJson = JSONValue ([existingJson, stats]); } // build new database value and add it to the db, overriding the old one statsJsonStr = newJson.toJSON (); dbvalue = makeDbValue (statsJsonStr); res = txn.mdb_put (dbStats, &dbkey, &dbvalue, 0); } checkError (res, "mdb_put (stats)"); } JSONValue getRepoInfo (string suite, string section, string arch) { auto repoid = "%s-%s-%s".format (suite, section, arch); auto jsonData = getValue (dbRepoInfo, repoid); if (jsonData is null) { JSONValue[string] dummy; return JSONValue (dummy); } return parseJSON (jsonData); } void setRepoInfo (string suite, string section, string arch, JSONValue repoInfo) { auto repoid = "%s-%s-%s".format (suite, section, arch); auto jsonData = repoInfo.toJSON (); putKeyValue (dbRepoInfo, repoid, jsonData); } void removeRepoInfo (string suite, string section, string arch) { auto repoid = "%s-%s-%s".format (suite, section, arch); auto dbkey = makeDbValue (repoid); auto txn = newTransaction (); scope (success) commitTransaction (txn); scope (failure) quitTransaction (txn); auto res = txn.mdb_del (dbRepoInfo, &dbkey, null); if (res != MDB_NOTFOUND) checkError (res, "mdb_del"); } /** * Get a list of package-ids which match a prefix. */ string[] getPkidsMatching (string prefix) { MDB_val dkey; MDB_cursorp cur; auto txn = newTransaction (MDB_RDONLY); scope (exit) quitTransaction (txn); auto res = txn.mdb_cursor_open (dbPackages, &cur); scope (exit) cur.mdb_cursor_close (); checkError (res, "mdb_cursor_open (pkid-match)"); auto pkids = appender!(string[]); prefix ~= "/"; while (cur.mdb_cursor_get (&dkey, null, MDB_NEXT) == 0) { auto pkid = to!string (fromStringz (cast(char*) dkey.mv_data)); if (pkid.startsWith (prefix)) pkids ~= pkid; } return pkids.data; } } appstream-generator-0.8.6/src/asgen/defines.d.in000066400000000000000000000017121416066706400215730ustar00rootroot00000000000000/* * Copyright (C) 2017 Canonical Ltd * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.defines; @safe: // location of arch-indep data for asgen public enum DATADIR = "@datadir@"; // current version of the tool public enum ASGEN_VERSION = "@asgen_version@"; appstream-generator-0.8.6/src/asgen/downloader.d000066400000000000000000000234041416066706400217110ustar00rootroot00000000000000/* * Copyright (C) 2019-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.downloader; @safe: import std.stdio : File; import std.typecons : Nullable; import std.datetime : SysTime, Clock, parseRFC822DateTime, DateTimeException; import std.array : appender, empty; import std.path : buildPath, dirName, buildNormalizedPath; import std.algorithm : startsWith; import std.format : format; import std.conv : to; import asgen.logging; static import std.file; import asgen.config : Config; import asgen.defines : ASGEN_VERSION; import asgen.utils : isRemote, randomString; class DownloadException : Exception { @safe pure nothrow this(const string msg, const string file = __FILE__, size_t line = __LINE__, Throwable next = null) { super (msg, file, line, next); } } /** * Download data via HTTP. Based on cURL. */ final class Downloader { private: immutable string caInfo; immutable string userAgent; // thread local instance static Downloader instance_; public: static Downloader get () @trusted { if (instance_ is null) instance_ = new Downloader; return instance_; } this () @trusted { userAgent = "appstream-generator/" ~ ASGEN_VERSION; // set custom SSL CA file, if we have one caInfo = Config.get.caInfo; } private immutable(Nullable!SysTime) downloadInternal (const string url, ref File dest, const uint maxTryCount = 5) @trusted in { assert (url.isRemote); } do { import core.time : dur; import std.string : toLower; import std.net.curl : HTTP, FTP; import std.typecons : Yes; Nullable!SysTime ret; size_t onReceiveCb (File f, ubyte[] data) { f.rawWrite (data); return data.length; } /* the curl library is stupid; you can't make an AutoProtocol set timeouts */ logDebug ("Downloading %s", url); try { if (url.startsWith ("http")) { immutable httpsUrl = url.startsWith ("https"); auto curlHttp = HTTP (url); if (!caInfo.empty) curlHttp.caInfo = caInfo; curlHttp.setUserAgent (userAgent); HTTP.StatusLine statusLine; curlHttp.connectTimeout = dur!"seconds" (30); curlHttp.dataTimeout = dur!"seconds" (30); curlHttp.onReceive = (data) => onReceiveCb (dest, data); curlHttp.onReceiveStatusLine = (HTTP.StatusLine l) { statusLine = l; }; curlHttp.onReceiveHeader = (in char[] key, in char[] value) { // we will not allow a HTTPS --> HTTP downgrade if (!httpsUrl) return; if (key == "location" && value.toLower.startsWith ("http:")) throw new DownloadException ("HTTPS URL tried to redirect to a less secure HTTP URL."); }; curlHttp.perform(Yes.throwOnError); if ("last-modified" in curlHttp.responseHeaders) { auto lastmodified = curlHttp.responseHeaders["last-modified"]; ret = parseRFC822DateTime(lastmodified); } if (statusLine.code != 200 && statusLine.code != 301 && statusLine.code != 302) { if (statusLine.code == 0) { // with some recent update of the D runtime or Curl, the status line isn't set anymore // just to be safe, check whether we received data before assuming everything went fine if (dest.size == 0) throw new DownloadException ("No data was received from the remote end (Code: %d).".format (statusLine.code)); } else { throw new DownloadException ("HTTP request returned status code %d (%s)".format (statusLine.code, statusLine.reason)); } } } else { auto curlFtp = FTP (url); curlFtp.connectTimeout = dur!"seconds" (30); curlFtp.dataTimeout = dur!"seconds" (30); curlFtp.onReceive = (data) => onReceiveCb (dest, data); curlFtp.perform(Yes.throwOnError); } logDebug ("Downloaded %s", url); } catch (Exception e) { if (maxTryCount > 0) { logDebug ("Failed to download %s, will retry %d more %s", url, maxTryCount, maxTryCount > 1 ? "times" : "time"); download (url, dest, maxTryCount - 1); } else { throw new DownloadException (e.message.to!string); } } return ret; } immutable(Nullable!SysTime) download (const string url, ref File dFile, const uint maxTryCount = 3) @trusted { return downloadInternal (url, dFile, maxTryCount); } immutable(ubyte[]) download (const string url, const uint maxTryCount = 3) @trusted { import core.stdc.stdlib : free; import core.sys.posix.stdio : fclose, open_memstream; char* ptr = null; scope (exit) free (ptr); size_t sz = 0; { auto f = open_memstream (&ptr, &sz); scope (exit) fclose (f); auto file = File.wrapFile (f); downloadInternal (url, file, maxTryCount); } return cast(immutable ubyte[]) ptr[0..sz].idup; } /** * Download `url` to `dest`. * * Params: * url = The URL to download. * dest = The location for the downloaded file. * maxTryCount = Number of times to attempt the download. */ void downloadFile (const string url, const string dest, const uint maxTryCount = 3) @trusted in { assert (url.isRemote); } out { assert (std.file.exists (dest)); } do { import std.file : exists, mkdirRecurse, setTimes, remove; static import std.file; if (dest.exists) { logDebug ("File '%s' already exists, re-download of '%s' skipped.", dest, url); return; } mkdirRecurse (dest.dirName); auto f = File (dest, "wb"); scope (failure) remove (dest); auto time = downloadInternal (url, f, maxTryCount); f.close (); if (!time.isNull) setTimes (dest, Clock.currTime, time.get); } /** * Download `url` and return a string with its contents. * * Params: * url = The URL to download. * maxTryCount = Number of times to retry on timeout. */ string downloadText (const string url, const uint maxTryCount = 3) @trusted { const data = download (url, maxTryCount); return (cast(char[])data).to!string; } /** * Download `url` and return a string array of lines. * * Params: * url = The URL to download. * maxTryCount = Number of times to retry on timeout. */ string[] downloadTextLines (const string url, const uint maxTryCount = 3) @trusted { import std.string : splitLines; return downloadText (url, maxTryCount).splitLines; } } @trusted unittest { import std.stdio : writeln; import std.exception : assertThrown; import std.file : remove, readText; import std.process : environment; asgen.logging.setVerbose (true); writeln ("TEST: ", "Downloader"); if (environment.get("ASGEN_TESTS_NO_NET", "no") != "no") { writeln ("I: NETWORK DEPENDENT TESTS SKIPPED. (explicitly disabled via `ASGEN_TESTS_NO_NET`)"); return; } immutable urlFirefoxDetectportal = "https://detectportal.firefox.com/"; auto dl = new Downloader; string detectPortalRes; try { detectPortalRes = dl.downloadText (urlFirefoxDetectportal); } catch (DownloadException e) { writeln ("W: NETWORK DEPENDENT TESTS SKIPPED. (automatically, no network detected: ", e.msg, ")"); return; } writeln ("I: Running network-dependent tests."); assert (detectPortalRes == "success\n"); // check if a downloaded file contains the right contents immutable firefoxDetectportalFname = "/tmp/asgen-test.ffdp" ~ randomString (4); scope(exit) firefoxDetectportalFname.remove (); dl.downloadFile (urlFirefoxDetectportal, firefoxDetectportalFname); assert (readText (firefoxDetectportalFname) == "success\n"); // download a bigger chunk of data without error immutable debianOrgFname = "/tmp/asgen-test.do" ~ randomString (4); scope(exit) debianOrgFname.remove (); dl.downloadFile ("https://debian.org", debianOrgFname); // fail when attempting to download a nonexistent file assertThrown!DownloadException (dl.downloadFile ("https://appstream.debian.org/nonexistent", "/tmp/asgen-dltest" ~ randomString (4), 2)); // check if HTTP --> HTTPS redirects, like done on mozilla.org, work dl.downloadFile ("http://mozilla.org", "/tmp/asgen-test.mozilla" ~ randomString (4), 1); } appstream-generator-0.8.6/src/asgen/engine.d000066400000000000000000001247251416066706400210300ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.engine; import std.parallelism : parallel; import std.string : format, count, toLower, startsWith; import std.array : Appender, appender, empty; import std.path : buildPath, buildNormalizedPath; import std.file : mkdirRecurse, rmdirRecurse; import std.algorithm : canFind, sort, SwapStrategy; import std.typecons : scoped, Nullable, Tuple; import std.conv : to; static import std.file; import appstream.Component; import asgen.config; import asgen.logging; import asgen.extractor; import asgen.datastore; import asgen.contentsstore; import asgen.result; import asgen.hint; import asgen.reportgenerator; import asgen.localeunit : LocaleUnit; import asgen.utils : copyDir, stringArrayToByteArray, getCidFromGlobalID; import asgen.backends.interfaces; import asgen.backends.dummy; import asgen.backends.debian; import asgen.backends.ubuntu; import asgen.backends.archlinux; import asgen.backends.rpmmd; import asgen.backends.alpinelinux; import asgen.iconhandler : IconHandler; /** * Class orchestrating the whole metadata extraction * and publication process. */ final class Engine { private: Config conf; PackageIndex pkgIndex; DataStore dstore; ContentsStore cstore; bool m_forced; public: this () { this.conf = Config.get (); switch (conf.backend) { case Backend.Dummy: pkgIndex = new DummyPackageIndex (conf.archiveRoot); break; case Backend.Debian: pkgIndex = new DebianPackageIndex (conf.archiveRoot); break; case Backend.Ubuntu: pkgIndex = new UbuntuPackageIndex (conf.archiveRoot); break; case Backend.Archlinux: pkgIndex = new ArchPackageIndex (conf.archiveRoot); break; case Backend.RpmMd: pkgIndex = new RPMPackageIndex (conf.archiveRoot); break; case Backend.Alpinelinux: pkgIndex = new AlpinePackageIndex (conf.archiveRoot); break; default: throw new Exception ("No backend specified, can not continue!"); } // load global registry of issue hint templates loadHintsRegistry (); // create cache in cache directory on workspace dstore = new DataStore (); dstore.open (conf); // open package contents cache cstore = new ContentsStore (); cstore.open (conf); } @property bool forced () { return m_forced; } @property void forced (bool v) { m_forced = v; } private void gcCollect () { static import core.memory; logDebug ("GC collection cycle triggered explicitly."); core.memory.GC.collect (); } private void logVersionInfo () { import asgen.defines : ASGEN_VERSION; static import appstream.Utils; alias AsUtils = appstream.Utils.Utils; string backendInfo = ""; if (!conf.backendName.empty) backendInfo = " [%s]".format (conf.backendName); logInfo ("AppStream Generator %s, AS: %s%s", ASGEN_VERSION, AsUtils.appstreamVersion, backendInfo); } /** * Extract metadata from a software container (usually a distro package). * The result is automatically stored in the database. */ private void processPackages (ref Package[] pkgs, IconHandler iconh) { import std.range : chunks; import glib.Thread : Thread; auto localeUnit = new LocaleUnit (cstore, pkgs); ulong chunkSize = pkgs.length / Thread.getNumProcessors () / 10; if (chunkSize > 100) chunkSize = 100; if (chunkSize <= 10) chunkSize = 10; logDebug ("Analyzing %s packages in batches of %s", pkgs.length, chunkSize); foreach (pkgsChunk; parallel (pkgs.chunks (chunkSize), 1)) { auto mde = new DataExtractor (dstore, iconh, localeUnit); foreach (ref pkg; pkgsChunk) { immutable pkid = pkg.id; if (dstore.packageExists (pkid)) continue; auto res = mde.processPackage (pkg); synchronized (dstore) { // write resulting data into the database dstore.addGeneratorResult (this.conf.metadataType, res); logInfo ("Processed %s, components: %s, hints: %s", res.pkid, res.componentsCount (), res.hintsCount ()); } // we don't need content data from this package anymore pkg.finish (); } } } /** * Populate the contents index with new contents data. While we are at it, we can also mark * some uninteresting packages as to-be-ignored, so we don't waste time on them * during the following metadata extraction. * * Returns: True in case we have new interesting packages, false otherwise. **/ private bool seedContentsData (Suite suite, string section, string arch, Package[] pkgs = []) { import glib.Thread : Thread; bool packageInteresting (Package pkg) { auto contents = pkg.contents; foreach (ref c; contents) { if (c.startsWith ("/usr/share/applications/")) return true; if (c.startsWith ("/usr/share/metainfo/")) return true; } if (pkg.gst.isNull) return false; return pkg.gst.get.isNotEmpty; } ulong workUnitSize = Thread.getNumProcessors * 2; if (workUnitSize >= pkgs.length) workUnitSize = 4; if (workUnitSize > 30) workUnitSize = 30; logDebug ("Scanning %s packages, work unit size %s", pkgs.length, workUnitSize); // check if the index has changed data, skip the update if there's nothing new if ((pkgs.empty) && (!pkgIndex.hasChanges (dstore, suite.name, section, arch)) && (!this.forced)) { logDebug ("Skipping contents cache update for %s/%s [%s], index has not changed.", suite.name, section, arch); return false; } logInfo ("Scanning new packages for %s/%s [%s]", suite.name, section, arch); if (pkgs.empty) pkgs = pkgIndex.packagesFor (suite.name, section, arch); // get contents information for packages and add them to the database auto interestingFound = false; // First get the contents (only) of all packages in the base suite if (!suite.baseSuite.empty) { logInfo ("Scanning new packages for base suite %s/%s [%s]", suite.baseSuite, section, arch); auto baseSuitePkgs = pkgIndex.packagesFor (suite.baseSuite, section, arch); foreach (ref pkg; parallel (baseSuitePkgs, workUnitSize)) { immutable pkid = pkg.id; if (!cstore.packageExists (pkid)) { cstore.addContents (pkid, pkg.contents); logInfo ("Scanned %s for base suite.", pkid); } // chances are that we might never want to extract data from these packages, // so remove their temporary data for now - we can reopen the packages later if we actually need them. pkg.cleanupTemp (); } } // And then scan the suite itself - here packages can be 'interesting' // in that they might end up in the output. foreach (ref pkg; parallel (pkgs, workUnitSize)) { immutable pkid = pkg.id; string[] contents; if (cstore.packageExists (pkid)) { if (dstore.packageExists (pkid)) { // TODO: Unfortunately, packages can move between suites without changing their ID. // This means as soon as we have an interesting package, even if we already processed it, // we need to regenerate the output metadata. // For that to happen, we set interestingFound to true here. Later, a more elegent solution // would be desirable here, ideally one which doesn't force us to track which package is // in which suite as well. if (!dstore.isIgnored (pkid)) interestingFound = true; continue; } // we will complement the main database with ignore data, in case it // went missing. contents = cstore.getContents (pkid); } else { // add contents to the index contents = pkg.contents; cstore.addContents (pkid, contents); } // check if we can already mark this package as ignored, and print some log messages if (!packageInteresting (pkg)) { dstore.setPackageIgnore (pkid); logInfo ("Scanned %s, no interesting files found.", pkid); // we won't use this anymore pkg.finish (); } else { logInfo ("Scanned %s, could be interesting.", pkid); interestingFound = true; } } // ensure the contents store is in a consistent state on disk, // since it might be accessed from other threads after this function // is run. cstore.sync (); return interestingFound; } private string getMetadataHead (Suite suite, string section) { import std.datetime : Clock; import core.time : Duration; string head; immutable origin = "%s-%s-%s".format (conf.projectName.toLower, suite.name.toLower, section.toLower); auto time = Clock.currTime (); time.fracSecs = Duration.zero; // we don't want fractional seconds immutable timeStr = time.toISOString (); string mediaPoolUrl = buildPath (conf.mediaBaseUrl, "pool"); if (conf.feature.immutableSuites) { mediaPoolUrl = buildPath (conf.mediaBaseUrl, suite.name); } if (conf.metadataType == DataType.XML) { head = "\n"; head ~= format ("//"); return false; } immutable pkid = identifier; writeln ("== ", pkid, " =="); writeln ("Contents:"); auto pkgContents = cstore.getContents (pkid); if (pkgContents.empty) { writeln ("~ No contents found."); } else { foreach (ref s; pkgContents) writeln (" ", s); } writeln (); writeln ("Icons:"); auto pkgIcons = cstore.getIcons (pkid); if (pkgIcons.empty) { writeln ("~ No icons found."); } else { foreach (ref s; pkgIcons) writeln (" ", s); } writeln (); if (dstore.isIgnored (pkid)) { writeln ("Ignored: yes"); writeln (); } else { writeln ("Global Component IDs:"); foreach (ref s; dstore.getGCIDsForPackage (pkid)) writeln ("- ", s); writeln (); writeln ("Generated Data:"); foreach (ref s; dstore.getMetadataForPackage (conf.metadataType, pkid)) writeln (s); writeln (); } if (dstore.hasHints (pkid)) { writeln ("Hints:"); writeln (dstore.getHints (pkid)); } else { writeln ("Hints: None"); } writeln (); return true; } } // End of Engine class appstream-generator-0.8.6/src/asgen/extractor.d000066400000000000000000000336241416066706400215730ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.extractor; import std.array : appender; import std.stdio; import std.string; import std.path : baseName; import std.algorithm : canFind; import std.typecons : scoped; import appstream.Component; import appstream.Metadata; import ascompose.Hint : Hint; import ascompose.Compose : Compose, IconPolicy; import ascompose.Unit : Unit; import ascompose.c.types : ComposeFlags, AscResult, AscUnit; import glib.Bytes : Bytes; import glib.c.types : GPtrArray; import asgen.config : Config, DataType; import asgen.logging; import asgen.hintregistry; import asgen.result; import asgen.backends.interfaces; import asgen.datastore; import asgen.iconhandler : IconHandler; import asgen.utils : componentGetRawIcon, toStaticGBytes; import asgen.packageunit : PackageUnit; import asgen.localeunit : LocaleUnit; final class DataExtractor { private: Compose compose; package DataType dtype; package DataStore dstore; package Config conf; IconHandler iconh; LocaleUnit l10nUnit; public: this (DataStore db, IconHandler iconHandler, LocaleUnit localeUnit) { dstore = db; iconh = iconHandler; conf = Config.get (); dtype = conf.metadataType; l10nUnit = localeUnit; compose = new Compose; //compose.setPrefix ("/usr"); compose.setMediaResultDir (db.mediaExportPoolDir); compose.setMediaBaseurl (""); compose.setCheckMetadataEarlyFunc (&checkMetadataIntermediate, cast(void*) this); compose.addFlags (ComposeFlags.IGNORE_ICONS | // we do custom icon processing ComposeFlags.PROCESS_UNPAIRED_DESKTOP | // handle desktop-entry files without metainfo data ComposeFlags.NO_FINAL_CHECK // we trigger the final check manually ); // we handle all threading, so the compose process doesn't also have to be threaded compose.removeFlags (ComposeFlags.USE_THREADS); if (l10nUnit !is null) compose.setLocaleUnit (l10nUnit); // set max screenshot size in bytes, if size is limited if (conf.maxScrFileSize != 0) compose.setMaxScreenshotSize (conf.maxScrFileSize * 1024 * 1024); // enable or disable user-defined features if (conf.feature.validate) compose.addFlags (ComposeFlags.VALIDATE); else compose.removeFlags (ComposeFlags.VALIDATE); if (conf.feature.noDownloads) compose.removeFlags (ComposeFlags.ALLOW_NET); else compose.addFlags (ComposeFlags.ALLOW_NET); if (conf.feature.processLocale) compose.addFlags (ComposeFlags.PROCESS_TRANSLATIONS); else compose.removeFlags (ComposeFlags.PROCESS_TRANSLATIONS); if (conf.feature.processFonts) compose.addFlags (ComposeFlags.PROCESS_FONTS); else compose.removeFlags (ComposeFlags.PROCESS_FONTS); if (conf.feature.screenshotVideos) compose.addFlags (ComposeFlags.ALLOW_SCREENCASTS); else compose.removeFlags (ComposeFlags.ALLOW_SCREENCASTS); // guess an icon policy that matches the user settings best bool hasIconsRemote = false; bool hasIconsCached = false; foreach (const ref policy; conf.iconSettings) { if (policy.storeCached) hasIconsCached = true; if (policy.storeRemote) hasIconsRemote = true; if (hasIconsRemote && hasIconsCached) break; } compose.setIconPolicy (IconPolicy.BALANCED); if (!hasIconsRemote && hasIconsCached) compose.setIconPolicy (IconPolicy.ONLY_CACHED); if (!hasIconsCached && hasIconsRemote) compose.setIconPolicy (IconPolicy.ONLY_REMOTE); // register allowed custom keys with the composer foreach (const ref key; conf.allowedCustomKeys.byKey) compose.addCustomAllowed (key); } /** * Helper function for DataExtractor.parseDesktopFile */ extern(C) static void checkMetadataIntermediate (AscResult *cres, AscUnit *cunit, void *userData) { import ascompose.Result : Result; import asgen.config : EXTRA_METAINFO_FAKE_PKGNAME; auto self = cast(DataExtractor) userData; auto result = new Result (cres); auto cptsPtrArray = result.fetchComponents (); for (uint i = 0; i < cptsPtrArray.len; i++) { auto cpt = new Component (cast (AsComponent*) cptsPtrArray.index (i)); auto gcid = result.gcidForComponent (cpt); // don't run expensive operations if the metadata already exists auto existingMData = self.dstore.getMetadata (self.dtype, gcid); if (existingMData is null) continue; // To account for packages which change their package name, we // also need to check if the package this component is associated // with matches ours. // If it doesn't, we can't just link the package to the component. bool samePkg = false; immutable bundleId = result.getBundleId; if (bundleId == EXTRA_METAINFO_FAKE_PKGNAME) { // the fake package is exempt from this check samePkg = true; } else { if (self.dtype == DataType.YAML) { if (existingMData.canFind (format ("Package: %s\n", bundleId))) samePkg = true; } else { if (existingMData.canFind (format ("%s", bundleId))) samePkg = true; } } if ((!samePkg) && (cpt.getKind != ComponentKind.WEB_APP)) { // The exact same metadata exists in a different package already, we emit an error hint. // ATTENTION: This does not cover the case where *different* metadata (as in, different summary etc.) // but with the *same ID* exists. // We only catch that kind of problem later. auto cdata = new Metadata (); cdata.setFormatStyle (FormatStyle.COLLECTION); cdata.setFormatVersion (self.conf.formatVersion); if (self.dtype == DataType.YAML) cdata.parse (existingMData, FormatKind.YAML); else cdata.parse (existingMData, FormatKind.XML); auto ecpt = cdata.getComponent (); const pkgNames = ecpt.getPkgnames; string pkgName = "(none)"; if (!pkgNames.empty) pkgName = pkgNames[0]; result.addHint (cpt, "metainfo-duplicate-id", ["cid", cpt.getId, "pkgname", pkgName]); } // drop the component as we already have processed it, but keep its // global ID so we can still register the ID with this package. result.removeComponentFull(cpt, false); } } /** * Helper function for DataExtractor.processPackage */ extern(C) static GPtrArray *translateDesktopTextCallback (GKeyFile *dePtr, const(char) *text, void *userData) { import glib.KeyFile : KeyFile; import glib.c.functions; import std.string : fromStringz, toStringz; auto pkg = *cast(Package*) userData; auto de = new KeyFile (dePtr, false); auto res = g_ptr_array_new_with_free_func (&g_free); auto translations = pkg.getDesktopFileTranslations (de, cast(string) text.fromStringz); foreach (ref key, ref value; translations) { g_ptr_array_add (res, g_strdup (key.toStringz)); g_ptr_array_add (res, g_strdup (value.toStringz)); } return res; } GeneratorResult processPackage (Package pkg) { import ascompose.Result : Result; // reset compose instance to clear data from any previous invocation compose.reset (); // set external desktop-entry translation function, if needed immutable externalL10n = pkg.hasDesktopFileTranslations; compose.setDesktopEntryL10nFunc (externalL10n? &translateDesktopTextCallback : null, externalL10n? &pkg : null); // wrap package into unit, so AppStream Compose can work with it auto unit = new PackageUnit (pkg); compose.addUnit (unit); // process all data compose.run (null); auto resultsArray = compose.getResults (); // we processed one unit, so should always generate one result if (resultsArray.len != 1) { logError ("Expected %s result for data extraction, but retrieved %s.", 1, resultsArray.len); assert (resultsArray.len == 1); } // create result wrapper auto gres = GeneratorResult (new Result (cast (AscResult*) resultsArray.index (0)), pkg); // process icons and perform additional refinements auto cptsPtrArray = gres.fetchComponents (); for (uint i = 0; i < cptsPtrArray.len; i++) { auto cpt = new Component (cast (AsComponent*) cptsPtrArray.index (i)); immutable ckind = cpt.getKind; // find & store icons iconh.process (gres, cpt); if (gres.isIgnored (cpt)) continue; // add fallback long descriptions only for desktop apps, console apps and web apps if (cpt.getMergeKind != MergeKind.NONE) continue; if (ckind != ComponentKind.DESKTOP_APP && ckind != ComponentKind.CONSOLE_APP && ckind != ComponentKind.WEB_APP) continue; // inject package descriptions, if needed auto flags = cpt.getValueFlags; cpt.setValueFlags (flags | AsValueFlags.NO_TRANSLATION_FALLBACK); cpt.setActiveLocale ("C"); if (!cpt.getDescription.empty) continue; // component doesn't have a long description, add one from the packaging. auto desc_added = false; foreach (ref lang, ref desc; pkg.description) { cpt.setDescription (desc, lang); desc_added = true; } if (desc_added) { // we only add the "description-from-package" tag if we haven't alreaey // emitted a "no-metainfo" tag, to avoid two hints explaining the same thing if (!gres.hasHint (cpt, "no-metainfo")) { if (!gres.addHint (cpt, "description-from-package")) continue; } } else { if (!gres.addHint (cpt, "description-missing", ["kind": AsUtils.componentKindToString (ckind)])) continue; } } // handle GStreamer integration (usually for Ubuntu) if (conf.feature.processGStreamer && !pkg.gst.isNull && pkg.gst.get.isNotEmpty) { auto data = appender!string; data.reserve(200); auto cpt = new Component (); cpt.setId (pkg.name); cpt.setKind (ComponentKind.CODEC); cpt.setName ("GStreamer Multimedia Codecs", "C"); foreach (ref lang, ref desc; pkg.summary) { cpt.setSummary (desc, lang); data ~= desc; } gres.addComponent (cpt, data.data.toStaticGBytes); } // perform final checks compose.finalizeResults (); // do our own final validation cptsPtrArray = gres.fetchComponents (); for (uint i = 0; i < cptsPtrArray.len; i++) { auto cpt = new Component (cast (AsComponent*) cptsPtrArray.index (i)); immutable ckind = cpt.getKind; if (cpt.getMergeKind != MergeKind.NONE) continue; if (cpt.getPkgnames.empty) { // no packages are associated with this component if (ckind != ComponentKind.WEB_APP && ckind != ComponentKind.OPERATING_SYSTEM && ckind != ComponentKind.REPOSITORY) { // this component is not allowed to have no installation candidate if (!cpt.hasBundle) { if (!gres.addHint (cpt, "no-install-candidate")) continue; } } } else { // packages are associated with this component if (pkg.kind == PackageKind.FAKE) { import std.array : array; import asgen.config : EXTRA_METAINFO_FAKE_PKGNAME; import std.algorithm.iteration : filter; // drop any association with the dummy package auto pkgnames = cpt.getPkgnames; cpt.setPkgnames (array(pkgnames.filter!(a => a != EXTRA_METAINFO_FAKE_PKGNAME))); } } } // clean up and return result pkg.finish (); return gres; } } appstream-generator-0.8.6/src/asgen/hintregistry.d000066400000000000000000000134541416066706400223120ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.hint; @safe: import std.stdio; import std.string; import std.json; import std.conv : to; import appstream.Validator : Validator; import appstream.c.types : IssueSeverity; import ascompose.Hint : Hint; import ascompose.Globals : Globals; static import appstream.Utils; alias AsUtils = appstream.Utils.Utils; import asgen.logging; import asgen.utils; /** * Each issue hint type has a severity assigned to it: * ERROR: A fatal error which resulted in the component being excluded from the final metadata. * WARNING: An issue which did not prevent generating meaningful data, but which is still serious * and should be fixed (warning of this kind usually result in less data). * INFO: Information, no immediate action needed (but will likely be an issue later). * PEDANTIC: Information which may improve the data, but could also be ignored. */ /** * Definition of a issue hint. */ struct HintDefinition { string tag; /// Unique issue tag IssueSeverity severity; /// Issue severity string explanation; /// Explanation template } /** * Load all issue hints from file and register them globally. */ void loadHintsRegistry () @trusted { import std.path; static import std.file; // find the hint definition file auto hintsDefFile = getDataPath ("asgen-hints.json"); if (!std.file.exists (hintsDefFile)) { logError ("Hints definition file '%s' was not found! This means we can not determine severity of issue tags and not render report pages.", hintsDefFile); return; } // read the hints definition JSON file auto f = File (hintsDefFile, "r"); string jsonData; string line; while ((line = f.readln ()) !is null) jsonData ~= line; auto hintDefsJSON = parseJSON (jsonData); bool checkAlreadyLoaded = true; foreach (ref tag; hintDefsJSON.object.byKey) { auto j = hintDefsJSON[tag]; immutable severity = AsUtils.severityFromString (j["severity"].str); bool overrideExisting = false; if ((tag == "icon-not-found") || (tag == "internal-unknown-tag") || (tag == "internal-error") || (tag == "no-metainfo")) overrideExisting = true; if (checkAlreadyLoaded) { if (!overrideExisting && Globals.hintTagSeverity (tag) != IssueSeverity.UNKNOWN) { logDebug ("Global hints registry already loaded."); break; } checkAlreadyLoaded = false; } string explanation = ""; if (j["text"].type == JSONType.array) { foreach (l; j["text"].array) explanation ~= l.str ~ "\n"; } else { explanation = j["text"].str; } if (!Globals.addHintTag (tag, severity, explanation, overrideExisting)) logError ("Unable to override existing hint tag %s.", tag); } } /** * Save information about all hint templates we know about to a JSON file. */ void saveHintsRegistryToJsonFile (const string fname) @trusted { // FIXME: is this really the only way you can set a type for JSONValue? auto map = JSONValue (["null": 0]); map.object.remove ("null"); foreach (const htag; Globals.getHintTags) { const hdef = retrieveHintDef (htag); auto jval = JSONValue (["text": JSONValue (hdef.explanation), "severity": JSONValue (AsUtils.severityToString (hdef.severity))]); map.object[hdef.tag] = jval; } File file = File(fname, "w"); file.writeln (map.toJSON (true)); file.close (); } HintDefinition retrieveHintDef (string tag) @trusted { HintDefinition hdef; hdef.tag = tag; hdef.severity = Globals.hintTagSeverity (tag); if (hdef.severity == IssueSeverity.UNKNOWN) return HintDefinition (); hdef.explanation = Globals.hintTagExplanation (tag); return hdef; } auto toJsonValue (Hint hint) @trusted { auto hintList = hint.getExplanationVarsList; string[string] vars; for (uint i = 0; i < hintList.len; i++) { if (i % 2 != 0) continue; const auto key = fromStringz (cast(char*) hintList.index (i)).to!string; const auto value = fromStringz (cast(char*) hintList.index (i + 1)).to!string; vars[key] = value; } return JSONValue(["tag": JSONValue (hint.getTag), "vars": JSONValue (vars)]); } @trusted unittest { import std.exception : assertThrown; import glib.GException : GException; writeln ("TEST: ", "Issue Hints"); assertThrown!GException (new Hint ("description-from-package")); loadHintsRegistry (); auto hint = new Hint ("description-from-package"); foreach (k, v; ["rainbows": "yes", "unicorns": "no", "storage": "towel"]) hint.addExplanationVar (k, v); auto root = hint.toJsonValue (); writeln (root.toJSON (true)); assert (retrieveHintDef ("asv-relation-item-invalid-vercmp").severity != IssueSeverity.UNKNOWN); saveHintsRegistryToJsonFile ("/tmp/testsuite-asgen-hints.json"); } appstream-generator-0.8.6/src/asgen/iconhandler.d000066400000000000000000001107111416066706400220370ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.iconhandler; import std.stdio : File, writeln; import std.string : endsWith, startsWith, format; import std.array : replace, array, empty; import std.path : baseName, buildPath; import std.uni : toLower; import std.file : mkdirRecurse; import std.algorithm : canFind, map; import std.variant : Algebraic; import std.typecons : scoped; import std.parallelism : parallel; import std.concurrency : Generator, yield; import glib.KeyFile : KeyFile; import glib.GException : GException; import appstream.Component; import appstream.Icon; static import ascompose.Utils; alias AscUtils = ascompose.Utils.Utils; import ascompose.Image : Image; import ascompose.Canvas : Canvas; import ascompose.c.types : ImageFormat, ImageLoadFlags, ImageSaveFlags; static import std.file; import asgen.utils; import asgen.logging; import asgen.result; import asgen.backends.interfaces; import asgen.contentsstore; import asgen.config : Config, IconPolicy; // all image extensions that we recognize as possible for icons. // the most favorable file extension needs to come first to prefer it private immutable possibleIconExts = [".png", ".jpg", ".svgz", ".svg", ".gif", ".ico", ".xpm"]; // the image extensions that we will actually allow software to have. private immutable allowedIconExts = [".png", ".jpg", ".svgz", ".svg", ".xpm"]; /** * Describes an icon theme as specified in the XDG theme spec. */ private final class Theme { private: string _name; Algebraic!(int, string)[string][] _directories; public: this (const string name, const(ubyte)[] indexData) { this._name = name; auto index = new KeyFile (); auto indexText = cast(string) indexData; index.loadFromData (indexText, -1, GKeyFileFlags.NONE); size_t dummy; foreach (section; index.getGroups (dummy)) { string type; string context; int scale; int threshold; int size; int minSize; int maxSize; // we ignore symbolic icons if (section.startsWith("symbolic/")) continue; try { size = index.getInteger (section, "Size"); context = index.getString (section, "Context"); } catch (GException) { continue; } try { threshold = index.getInteger (section, "Threshold"); } catch (GException) { threshold = 2; } try { type = index.getString (section, "Type"); } catch (GException) { type = "Threshold"; } try { minSize = index.getInteger (section, "MinSize"); } catch (GException) { minSize = size; } try { maxSize = index.getInteger (section, "MaxSize"); } catch (GException) { maxSize = size; } try { scale = index.getInteger (section, "Scale"); } catch (GException) { scale = 1; } if (size == 0) continue; auto themedir = [ "path": Algebraic!(int, string) (section), "type": Algebraic!(int, string) (type), "size": Algebraic!(int, string) (size), "minsize": Algebraic!(int, string) (minSize), "maxsize": Algebraic!(int, string) (maxSize), "threshold": Algebraic!(int, string) (threshold), "scale": Algebraic!(int, string) (scale) ]; _directories ~= themedir; } // sort our directory list, so the smallest size is at the top import std.algorithm : sort; _directories.sort!("a[\"size\"].get!int < b[\"size\"].get!int"); } this (const string name, Package pkg) { auto indexData = pkg.getFileData (buildPath ("/usr/share/icons", name, "index.theme")); this (name, indexData); } @property string name () const { return this._name; } @property auto directories () const { return this._directories; } /** * Check if a directory is suitable for the selected size. * If @assumeThresholdScalable is set to true, we will allow * downscaling of any higher-than-requested icon size, even if the * section is of "Threshold" type and would usually prohibit the scaling. */ package bool directoryMatchesSize (const Algebraic!(int, string)[string] themedir, ImageSize size, bool assumeThresholdScalable = false) { immutable scale = themedir["scale"].get!int; if (scale != size.scale) return false; immutable type = themedir["type"].get!string; if (type == "Fixed") return size.toInt () == themedir["size"].get!int; if (type == "Scalable") { if ((themedir["minsize"].get!int <= size.toInt) && (size.toInt <= themedir["maxsize"].get!int)) return true; return false; } if (type == "Threshold") { immutable themeSize = themedir["size"].get!int; immutable th = themedir["threshold"].get!int; if (assumeThresholdScalable) { // we treat this "Threshold" as if we were allowed to downscale its icons if they // have a higher size. // This can lead to "wrong" scaling, but allows us to retrieve more icons. return themeSize >= size.toInt; } else { // follow the proper algorithm as defined by the XDG spec if (((themeSize - th) <= size.toInt) && (size.toInt <= (themeSize + th))) return true; } return false; } return false; } /** * Returns an iteratable of possible icon filenames that match @iname and @size. * If @relaxedScalingRules is set to true, we scale down any bigger icon seize, even * if the theme definition would usually prohibit that. **/ auto matchingIconFilenames (string iname, ImageSize size, bool relaxedScalingRules = false) { struct IconFilenamesRange { // state int idxThemeDir = 0; int idxExt = 0; bool isEmpty = false; // extensions to check for immutable extensions = ["png", "svgz", "svg", "xpm"]; // reference to the theme Theme theme; this (Theme theme) { this.theme = theme; // get initial matching icon, as // front() is called before popFront() idxExt = -1; popFront (); } bool empty () @property { return idxThemeDir >= theme.directories.length; } string front () const @property { return "/usr/share/icons/%s/%s/%s.%s".format (theme.name, theme.directories[idxThemeDir]["path"].get!(string), iname, extensions[idxExt]); } void popFront () { do { idxExt++; if (idxExt >= extensions.length) { idxExt = 0; idxThemeDir++; } if (idxThemeDir >= theme.directories.length) return; // end of range reached if (theme.directoryMatchesSize (theme.directories[idxThemeDir], size, relaxedScalingRules)) return; } while (true); } } return IconFilenamesRange(this); } } /** * Finds icons in a software archive and stores them in the * correct sizes for a given AppStream component. */ final class IconHandler { private: string mediaExportPath; Theme[] themes; Package[string] iconFiles; string[] themeNames; IconPolicy[] iconPolicy; IconPolicy defaultIconPolicy; bool allowIconUpscaling; bool allowRemoteIcons; public: this (ContentsStore ccache, string mediaPath, IconPolicy[] iconPolicy, Package[string] pkgMap, string iconTheme = null) { logDebug ("Creating new IconHandler"); iconFiles.clear (); mediaExportPath = mediaPath; this.iconPolicy = iconPolicy; foreach (ref policy; iconPolicy) { if (policy.iconSize == ImageSize (64)) { defaultIconPolicy = policy; break; } } // Sanity checks if (defaultIconPolicy.iconSize != ImageSize (64)) throw new Exception ("Could not find default icon site '64x64' in icon policy list. This is a bug in the generator or configuration file."); assert (defaultIconPolicy.storeCached == true); auto conf = Config.get; allowIconUpscaling = conf.feature.allowIconUpscale; // we assume that when screenshot storage is permitted, remote icons are also okay allowRemoteIcons = conf.feature.storeScreenshots && !conf.mediaBaseUrl.empty; // Preseeded theme names. // * prioritize hicolor, because that's where apps often install their upstream icon // * then look at the theme given in the config file // * allow Breeze icon theme, needed to support KDE apps (they have no icon at all, otherwise...) // * in rare events, GNOME needs the same treatment, so special-case Adwaita as well // * We need at least one icon theme to provide the default XDG icon spec stock icons. // A fair take would be to select them between KDE and GNOME at random, but for consistency and // because everyone hates unpredictable behavior, we sort alphabetically and prefer Adwaita over Breeze. themeNames = ["hicolor"]; if (iconTheme !is null) themeNames ~= iconTheme; themeNames ~= "Adwaita"; // GNOME themeNames ~= "breeze"; // KDE Package getPackage (string pkid) { if (pkid is null) return null; return pkgMap.get (pkid, null); } // load data from the contents index. // we don't show mercy to memory here, we just want the icon lookup to be fast, // so we have to cache the data. Theme[string] tmpThemes; auto filesPkids = ccache.getIconFilesMap (array(pkgMap.byKey)); foreach (info; parallel (filesPkids.byKeyValue, 100)) { immutable fname = info.key; immutable pkgid = info.value; if (fname.startsWith ("/usr/share/pixmaps/")) { auto pkg = getPackage (pkgid); if (pkg is null) continue; synchronized (this) iconFiles[fname] = pkg; continue; } // optimization: check if we actually have an interesting path before // entering the foreach loop. if (!fname.startsWith ("/usr/share/icons/")) continue; auto pkg = getPackage (filesPkids[fname]); if (pkg is null) continue; foreach (name; themeNames) { if (fname == "/usr/share/icons/%s/index.theme".format (name)) { synchronized (this) tmpThemes[name] = new Theme (name, pkg); } else if (fname.startsWith ("/usr/share/icons/%s".format (name))) { synchronized (this) iconFiles[fname] = pkg; } } } // when running on partial repos (e.g. PPAs) we might not have a package containing the // hicolor theme definition. Since we always need it to be there to properly process icons, // we inject our own copy here. if ("hicolor" !in tmpThemes) { logInfo ("No packaged hicolor icon theme found, using built-in one."); auto hicolorThemeIndex = getDataPath ("hicolor-theme-index.theme"); if (!std.file.exists (hicolorThemeIndex)) { logError ("Hicolor icon theme index at '%s' was not found! We will not be able to handle icons in this theme.", hicolorThemeIndex); } else { ubyte[] indexData; auto f = File (hicolorThemeIndex, "r"); while (!f.eof) { char[GENERIC_BUFFER_SIZE] buf; indexData ~= f.rawRead (buf); } tmpThemes["hicolor"] = new Theme ("hicolor", indexData); } } // this is necessary to keep the ordering (and therefore priority) of themes. // we don't know the order in which we find index.theme files in the code above, // therefore this sorting is necessary. foreach (tname; themeNames) { if (tname in tmpThemes) themes ~= tmpThemes[tname]; } logDebug ("Created new IconHandler."); } private string getIconNameAndClear (Component cpt) { string name = null; // a not-processed icon name is stored as "1x1px" icon, so we can // quickly identify it here. auto icon = componentGetRawIcon (cpt); if (!icon.isNull) { if (icon.get.getKind == IconKind.LOCAL) name = icon.get.getFilename (); else name = icon.get.getName (); } // clear the list of icons in this component auto iconsArray = cpt.getIcons (); if (iconsArray.len > 0) iconsArray.removeRange (0, iconsArray.len); return name; } static private bool iconAllowed (string iconName) { foreach (ref ext; allowedIconExts) if (iconName.endsWith (ext)) return true; return false; } /** * Generates potential filenames of the icon that is searched for in the * given size. **/ private auto possibleIconFilenames (string iconName, ImageSize size, bool relaxedScalingRules = false) { auto gen = new Generator!string ( { foreach (theme; this.themes) { foreach (fname; theme.matchingIconFilenames (iconName, size, relaxedScalingRules)) yield (fname); } if (size.scale == 1 && size.width == 64) { // Check icons root directory for icon files // this is "wrong", but we support it for compatibility reasons. // However, we only ever use it to satisfy the 64x64px requirement foreach (extension; possibleIconExts) yield ("/usr/share/icons/%s%s".format (iconName, extension)); // check pixmaps directory for icons // we only ever use the pixmap directory contents to satisfy the minimum 64x64px icon // requirement. Otherwise we get weird upscaling to higher sizes or HiDPI sizes happening, // as later code tries to downscale "bigger" sizes. foreach (extension; possibleIconExts) yield ("/usr/share/pixmaps/%s%s".format (iconName, extension)); } }); return gen; } /** * Helper structure for the findIcons * method. **/ private struct IconFindResult { Package pkg; string fname; this (Package pkg, string fname) { this.pkg = pkg; this.fname = fname; } } /** * Looks up 'icon' with 'size' in popular icon themes according to the XDG * icon theme spec. **/ private auto findIcons (string iconName, const ImageSize[] sizes, Package pkg = null) { IconFindResult[ImageSize] sizeMap; foreach (size; sizes) { // search for possible icon filenames, using relaxed scaling rules by default foreach (fname; possibleIconFilenames (iconName, size, true)) { if (pkg !is null) { // we are supposed to search in one particular package if (pkg.contents.canFind (fname)) { sizeMap[size] = IconFindResult (pkg, fname); break; } } else { // global search in all packages pkg = iconFiles.get (fname, null); // continue if filename is not in map if (pkg is null) continue; sizeMap[size] = IconFindResult (pkg, fname); break; } } } return sizeMap; } /** * Strip file extension from icon. */ string stripIconExt (ref string iconName) { if (iconName.endsWith (".png")) return iconName[0..$-4]; if (iconName.endsWith (".svg")) return iconName[0..$-4]; if (iconName.endsWith (".xpm")) return iconName[0..$-4]; if (iconName.endsWith (".svgz")) return iconName[0..$-5]; return iconName; } /** * Extracts the icon from the package and stores it in the cache. * Ensures the stored icon always has the size given in "size", and renders * scalable vectorgraphics if necessary. * * Params: * cpt = The component this icon belongs to. * res = The result the component belongs to. * cptExportPath = The data export directory of the component. * sourcePkg = The package the to-be-extracted icon is located in. * iconPath = The (absolute) path to the icon. * size = The size the icon should be stored in. **/ private bool storeIcon (Component cpt, ref GeneratorResult gres, string cptExportPath, Package sourcePkg, string iconPath, IconPolicy policy) { immutable size = policy.iconSize; auto iformat = AscUtils.imageFormatFromFilename (iconPath); if (iformat == ImageFormat.UNKNOWN) { gres.addHint (cpt.getId (), "icon-format-unsupported", ["icon_fname": baseName (iconPath)]); return false; } auto path = buildPath (cptExportPath, "icons", size.toString); auto iconName = (gres.pkg.kind == PackageKind.FAKE)? baseName (iconPath) : "%s_%s".format (gres.pkg.name, baseName (iconPath)); if (iconName.endsWith (".svgz")) iconName = iconName.replace (".svgz", ".png"); else if (iconName.endsWith (".svg")) iconName = iconName.replace (".svg", ".png"); else if (iconName.endsWith (".xpm")) iconName = iconName.replace (".xpm", ".png"); auto iconStoreLocation = buildPath (path, iconName); if (std.file.exists (iconStoreLocation)) { // we already extracted that icon, skip the extraction step // and just add the new icon. if (policy.storeCached) { auto icon = new Icon (); icon.setKind (IconKind.CACHED); icon.setWidth (size.width); icon.setHeight (size.height); icon.setScale (size.scale); icon.setName (iconName); cpt.addIcon (icon); } if (policy.storeRemote && allowRemoteIcons) { immutable gcid = gres.gcidForComponent (cpt); if (gcid is null) { gres.addHint (cpt, "internal-error", "No global ID could be found for the component, could not add remote icon."); return true; } immutable remoteIconUrl = buildPath (gcid, "icons", size.toString, iconName); auto icon = new Icon (); icon.setKind (IconKind.REMOTE); icon.setWidth (size.width); icon.setHeight (size.height); icon.setScale (size.scale); icon.setUrl (remoteIconUrl); cpt.addIcon (icon); } return true; } // filepath is checked because icon can reside in another binary // eg amarok's icon is in amarok-data const(ubyte)[] iconData = null; try { iconData = sourcePkg.getFileData (iconPath); } catch (Exception e) { gres.addHint(cpt.getId (), "pkg-extract-error", ["fname": baseName (iconPath), "pkg_fname": baseName (sourcePkg.getFilename), "error": e.msg]); return false; } if (iconData.empty ()) { gres.addHint (cpt.getId (), "pkg-empty-file", ["fname": baseName (iconPath), "pkg_fname": baseName (sourcePkg.getFilename)]); return false; } auto scaled_width = size.width * size.scale; auto scaled_height = size.height * size.scale; if ((iformat == ImageFormat.SVG) || (iformat == ImageFormat.SVGZ)) { // create target directory mkdirRecurse (path); try { import gio.MemoryInputStream : MemoryInputStream; auto stream = new MemoryInputStream (cast(ubyte[]) iconData, null); auto cv = scoped!Canvas (scaled_width, scaled_height); cv.renderSvg (stream); cv.savePng (iconStoreLocation); } catch (Exception e) { gres.addHint(cpt.getId (), "image-write-error", ["fname": baseName (iconPath), "pkg_fname": baseName (sourcePkg.getFilename), "error": e.msg]); return false; } } else { Image img; try { img = new Image ((cast(ubyte[]) iconData).ptr, cast(ptrdiff_t)iconData.length, 0, iconPath.endsWith (".svgz"), ImageLoadFlags.NONE); } catch (Exception e) { gres.addHint(cpt.getId (), "image-write-error", ["fname": baseName (iconPath), "pkg_fname": baseName (sourcePkg.getFilename), "error": e.msg]); return false; } if (iformat == ImageFormat.XPM) { // we use XPM images only if they are large enough if (allowIconUpscaling) { // we only try upscaling for the default 64x64px size and only if // the icon is not too small if (size != ImageSize (64)) return false; if ((img.getWidth < 48) || (img.getHeight < 48)) return false; } else { if ((img.getWidth < scaled_width) || (img.getHeight < scaled_height)) return false; } } // ensure that we don't try to make an application visible that has a really tiny icon // by upscaling it to a blurry mess if (size.scale == 1 && size.width == 64) { if ((img.getWidth < 48) || (img.getHeight < 48)) { gres.addHint (cpt, "icon-too-small", ["icon_name": iconName, "icon_size": "%ux%u".format (img.getWidth, img.getHeight)]); return false; } } // warn about icon upscaling, it looks ugly if (scaled_width > img.getWidth) { gres.addHint (cpt, "icon-scaled-up", ["icon_name": iconName, "icon_size": "%ux%u".format (img.getWidth, img.getHeight), "scale_size": size.toString]); } // create target directory mkdirRecurse (path); try { img.scale (scaled_width, scaled_height); img.saveFilename (iconStoreLocation, 0, 0, ImageSaveFlags.OPTIMIZE); } catch (Exception e) { gres.addHint (cpt, "image-write-error", ["fname": baseName (iconPath), "pkg_fname": baseName (sourcePkg.getFilename), "error": e.msg]); return false; } } if (policy.storeCached) { auto icon = new Icon (); icon.setKind (IconKind.CACHED); icon.setWidth (size.width); icon.setHeight (size.height); icon.setScale (size.scale); icon.setName (iconName); cpt.addIcon (icon); } if (policy.storeRemote && allowRemoteIcons) { immutable gcid = gres.gcidForComponent (cpt); if (gcid is null) { gres.addHint (cpt, "internal-error", "No global ID could be found for the component, could not add remote icon."); return true; } immutable remoteIconUrl = buildPath (gcid, "icons", size.toString, iconName); auto icon = new Icon (); icon.setKind (IconKind.REMOTE); icon.setWidth (size.width); icon.setHeight (size.height); icon.setScale (size.scale); icon.setUrl (remoteIconUrl); cpt.addIcon (icon); } return true; } /** * Helper function to try to find an icon that we can up- or downscale to the desired size. */ private auto findIconScalableToSize (ref IconFindResult[ImageSize] possibleIcons, const ref ImageSize size) { IconFindResult info; info.pkg = null; // on principle, never attempt to up- or downscale an icon to something below // AppStream's default icon size. // The clients can do that just as well, without us wasting disk space // and network bandwidth. if (size.scale == 1 && size.width < 64) return info; // the size we want wasn't found, can we downscale a larger one? foreach (ref asize; possibleIcons.byKey) { auto data = possibleIcons[asize]; if (asize.scale != size.scale) continue; if (asize < size) continue; info = data; break; } if ((info.pkg is null) && (allowIconUpscaling) && (size == defaultIconPolicy.iconSize)) { // no icon was found to downscale, but we allow upscaling, so try one last time // to find a suitable icon for at least the default AppStream icon size. foreach (ref asize; possibleIcons.byKey) { auto data = possibleIcons[asize]; // we never allow icons smaller than 48x48px if (asize.width < 48) continue; if (asize.scale != size.scale) continue; info = data; break; } } return info; } /** * Try to find & store icons for a selected component. */ bool process (ref GeneratorResult gres, Component cpt) { // we don't touch fonts unless those didn't have their icon // rendered from the font itself already if (cpt.getKind == ComponentKind.FONT) { auto iconsArr = cpt.getIcons (); for (uint i = 0; i < iconsArr.len; i++) { auto icon = new Icon (cast(AsIcon*) iconsArr.index (i)); // nothing to do for us if cached and remote icons are already there if (icon.getKind == IconKind.CACHED || icon.getKind == IconKind.REMOTE) return true; } } auto iconName = getIconNameAndClear (cpt); // nothing to do if there is no icon if (iconName is null) return true; auto gcid = gres.gcidForComponent (cpt); if (gcid is null) { auto cid = cpt.getId (); if (cid is null) cid = "general"; gres.addHint (cid, "internal-error", "No global ID could be found for the component."); return false; } auto cptMediaPath = buildPath (mediaExportPath, gcid); if (iconName.startsWith ("/")) { logDebug ("Looking for icon '%s' for '%s::%s' (path)", iconName, gres.pkid, cpt.getId); if (gres.pkg.contents.canFind (iconName)) return storeIcon (cpt, gres, cptMediaPath, gres.pkg, iconName, defaultIconPolicy); // we couldn't find the absolute icon path gres.addHint (cpt.getId, "icon-not-found", ["icon_fname": iconName]); return false; } else { logDebug ("Looking for icon '%s' for '%s::%s' (XDG)", iconName, gres.pkid, cpt.getId); iconName = baseName (iconName); // Small hack: Strip .png and other extensions from icon files to make the XDG and Pixmap finder // work properly, which add their own icon extensions and find the most suitable icon. iconName = stripIconExt (iconName); string lastIconName = null; /// Search for an icon in XDG icon directories. /// Returns true on success and sets lastIconName to the /// last icon name that has been handled. bool findAndStoreXdgIcon (Package epkg = null) { auto iconRes = findIcons (iconName, array(iconPolicy.map!(a => a.iconSize)), epkg); if (iconRes.empty) return false; IconFindResult[ImageSize] iconsStored; foreach (ref policy; iconPolicy) { immutable size = policy.iconSize; auto infoP = (size in iconRes); IconFindResult info; if (infoP is null) info.pkg = null; else info = *infoP; // check if we can scale another size to the desired one if (info.pkg is null) info = findIconScalableToSize (iconRes, size); // give up if we still haven't found an icon (in which case `info.pkg` would be set) if (info.pkg is null) continue; lastIconName = info.fname; if (iconAllowed (lastIconName)) { if (storeIcon (cpt, gres, cptMediaPath, info.pkg, lastIconName, policy)) iconsStored[size] = info; } else { // the found icon is not suitable, but maybe we can scale a differently sized icon to the right one? info = findIconScalableToSize (iconRes, size); if (info.pkg is null) continue; if (iconAllowed (info.fname)) { if (storeIcon (cpt, gres, cptMediaPath, info.pkg, lastIconName, policy)) iconsStored[size] = info; lastIconName = info.fname; } } if (gres.isIgnored (cpt)) { // running storeIcon() in this loop may lead to rejection // of this component, in case icons can't be saved. // we just give up in that case. return false; } } // ensure we have stored a 64x64px icon, since this is mandated // by the AppStream spec by downscaling a larger icon that we // might have found. if (ImageSize(64) in iconsStored) { logDebug ("Found icon %s - %s in XDG directories, 64x64px size is present", gres.pkid, iconName); return true; } else { foreach (size; iconPolicy.map!(a => a.iconSize)) { if (size !in iconsStored) continue; if (size < ImageSize(64)) continue; logInfo ("Downscaling icon %s - %s from %s to %s", gres.pkid, iconName, size, defaultIconPolicy.iconSize); auto info = iconsStored[size]; lastIconName = info.fname; if (storeIcon (cpt, gres, cptMediaPath, info.pkg, lastIconName, defaultIconPolicy)) return true; } } // if we are here, we either didn't find an icon, or no icon is present // in the default 64x64px size return false; } // search for the right icon iside the current package auto success = findAndStoreXdgIcon (gres.pkg); if (!success && !gres.isIgnored (cpt)) { // search in all packages success = findAndStoreXdgIcon (); } if (success) { logDebug ("Icon %s - %s found in XDG dirs", gres.pkid, iconName); // we found a valid stock icon, so set that additionally to the cached one auto icon = new Icon; icon.setKind (IconKind.STOCK); icon.setName (iconName); cpt.addIcon (icon); return true; } else { logDebug ("Icon %s - %s not found in required size(s) in XDG dirs", gres.pkid, iconName); if ((lastIconName !is null) && (!iconAllowed (lastIconName))) { gres.addHint (cpt.getId, "icon-format-unsupported", ["icon_fname": baseName (lastIconName)]); return false; } gres.addHint (cpt.getId, "icon-not-found", ["icon_fname": iconName]); return false; } } } } // end of IconHandler class unittest { writeln ("TEST: ", "IconHandler"); auto hicolorThemeIndex = getDataPath ("hicolor-theme-index.theme"); ubyte[] indexData; auto f = File (hicolorThemeIndex, "r"); while (!f.eof) { char[GENERIC_BUFFER_SIZE] buf; indexData ~= f.rawRead (buf); } auto theme = new Theme ("hicolor", indexData); foreach (fname; theme.matchingIconFilenames ("accessories-calculator", ImageSize (48))) { bool valid = false; if (fname.startsWith ("/usr/share/icons/hicolor/48x48/")) valid = true; if (fname.startsWith ("/usr/share/icons/hicolor/scalable/")) valid = true; assert (valid); if ((valid) && (IconHandler.iconAllowed (fname))) valid = true; else valid = false; if (fname.endsWith (".ico")) assert (!valid); else assert (valid); } foreach (fname; theme.matchingIconFilenames ("accessories-text-editor", ImageSize (192))) { if (fname.startsWith ("/usr/share/icons/hicolor/192x192/")) continue; if (fname.startsWith ("/usr/share/icons/hicolor/256x256/")) continue; if (fname.startsWith ("/usr/share/icons/hicolor/512x512/")) continue; if (fname.startsWith ("/usr/share/icons/hicolor/scalable/")) continue; assert (0); } } appstream-generator-0.8.6/src/asgen/localeunit.d000066400000000000000000000211021416066706400217030ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ import std.conv : to; import std.string : format; import glib.PtrArray : PtrArray; import glib.c.types : GQuark, GError, GBytes; import ascompose.Unit : Unit; import ascompose.c.types; import asgen.config : Config; import asgen.contentsstore : ContentsStore; import asgen.backends.interfaces : Package; private __gshared extern(C) GQuark asc_compose_error_quark (); /** * Special unit that wraps the global locale fetching code. */ public final class LocaleUnit : Unit { package Package[string] localeFilePkgMap; import gobject.Type : Type; import gobject.c.functions : g_object_get_data; import ascompose.c.functions: asc_unit_get_type; import ascompose.c.functions : asc_unit_get_user_data; struct AGLocaleUnit { AscUnit parentInstance; } struct AGLocaleUnitClass { AscUnitClass parentClass; } protected AGLocaleUnit *agLocaleUnit; protected override void* getStruct() { return cast(void*)gObject; } public synchronized static GType getType() { import std.algorithm : startsWith; GType agLocaleUnitType = Type.fromName("AGLocaleUnit"); if (agLocaleUnitType == GType.INVALID) { agLocaleUnitType = Type.registerStaticSimple( asc_unit_get_type(), "AGLocaleUnit", cast(uint)AGLocaleUnitClass.sizeof, cast(GClassInitFunc) &agLocaleUnitClassInit, cast(uint)AGLocaleUnit.sizeof, null, cast(GTypeFlags)0); foreach (member; __traits(derivedMembers, LocaleUnit)) { static if (member.startsWith("_implementInterface")) __traits(getMember, LocaleUnit, member)(agLocaleUnitType); } } return agLocaleUnitType; } public this (ContentsStore cstore, Package[] pkgList) { import std.array : array; import std.typecons : scoped; import std.string : toStringz; import glib.c.functions : g_strdup, g_free; import gobject.c.functions : g_object_newv; super (cast(AscUnit*) g_object_newv (getType (), 0, null), true); // helper for function override callbacks setUserData (cast(void*) this); // convert the list into an associative array for faster lookups Package[string] pkgMap; foreach (ref pkg; pkgList) { immutable pkid = pkg.id; pkgMap[pkid] = pkg; } pkgMap.rehash (); auto conf = Config.get; if (!conf.feature.processLocale) return; // don't load the expensive locale<->package mapping if we don't need it // we make the assumption here that all locale for a given domain are in one package. // otherwise this global search will get even more insane. // the key of the map returned by getLocaleMap will therefore contain only the locale // file basename instead of a full path auto dbLocaleMap = cstore.getLocaleMap (array(pkgMap.byKey)); foreach (ref info; dbLocaleMap.byKeyValue) { immutable id = info.key; immutable pkgid = info.value; // check if we already have a package - lookups in this HashMap are faster // due to its smaller size and (most of the time) outweight the following additional // lookup for the right package entity. if (localeFilePkgMap.get (id, null) !is null) continue; Package pkg; if (pkgid !is null) pkg = pkgMap.get (pkgid, null); if (pkg !is null) localeFilePkgMap[id] = pkg; } auto contents = new PtrArray (&g_free); foreach (ref fname; localeFilePkgMap.byKey) contents.add (cast(void*) g_strdup (fname.toStringz)); setContents (contents); } extern(C) { static void agLocaleUnitClassInit (void* klass) { AscUnitClass *ascUnitClass = cast(AscUnitClass*) klass; gobject.c.types.GObjectClass *gobjectCTypesGObjectClass = cast(gobject.c.types.GObjectClass*)klass; ascUnitClass.open = &agLocaleUnitOpen; ascUnitClass.close = &agLocaleUnitClose; ascUnitClass.fileExists = &agLocaleUnitFileExists; ascUnitClass.dirExists = &agLocaleUnitDirExists; ascUnitClass.readData = &agLocaleUnitReadData; } static int agLocaleUnitOpen (AscUnit *iface, GError** err) { auto impl = cast(LocaleUnit) g_object_get_data (cast(GObject*)iface, "GObject".ptr); if (impl is null) impl = cast(LocaleUnit) asc_unit_get_user_data (iface); return impl.open ()? 1 : 0; } static void agLocaleUnitClose (AscUnit* iface) { auto impl = cast(LocaleUnit) g_object_get_data (cast(GObject*)iface, "GObject".ptr); if (impl is null) impl = cast(LocaleUnit) asc_unit_get_user_data (iface); impl.close(); } static int agLocaleUnitFileExists (AscUnit* iface, const(char)* dirname) { import std.string : fromStringz; auto impl = cast(LocaleUnit) g_object_get_data (cast(GObject*)iface, "GObject".ptr); if (impl is null) impl = cast(LocaleUnit) asc_unit_get_user_data (iface); return impl.fileExists (dirname.fromStringz.to!string)? 1 : 0; } static int agLocaleUnitDirExists (AscUnit* iface, const(char)* dirname) { import std.string : fromStringz; auto impl = cast(LocaleUnit) g_object_get_data (cast(GObject*)iface, "GObject".ptr); if (impl is null) impl = cast(LocaleUnit) asc_unit_get_user_data (iface); return impl.dirExists (dirname.fromStringz.to!string)? 1 : 0; } static GBytes *agLocaleUnitReadData (AscUnit* iface, const(char)* filename, GError** err) { auto impl = cast(LocaleUnit) g_object_get_data (cast(GObject*)iface, "GObject".ptr); if (impl is null) impl = cast(LocaleUnit) asc_unit_get_user_data (iface); return impl.c_readData (filename, err); } } public override bool open () { // we already opened everything in the constructor // (this enables us to reuse this fake unit multiple times) return true; } public override void close () { // noop } public override bool fileExists (string filename) { return ((filename in localeFilePkgMap) is null)? false : true; } public override bool dirExists (string dirname) { // not implemented yet, as it's not needed for locale finding (yet?) return false; } package GBytes *c_readData (const(char)* filename, GError** err) { import glib.c.functions : g_bytes_new_take, g_memdup, g_set_error_literal; import ascompose.c.types : ComposeError; import ascompose.c.functions : asc_unit_get_user_data; import std.string : toStringz, fromStringz; const fname = filename.fromStringz.to!string; auto pkgP = fname in localeFilePkgMap; if (pkgP is null) { g_set_error_literal (err, asc_compose_error_quark, ComposeError.FAILED, toStringz ("File '%s' does not exist in a known package!".format(fname))); return null; } const data = cast(ubyte[]) (*pkgP).getFileData (fname); // FIXME: We should use g_memdup2 here, once we can bump the GLib version! void *ncCopy = g_memdup (cast(void*) data.ptr, cast(uint) data.length); return g_bytes_new_take (ncCopy, cast(size_t) data.length); } } appstream-generator-0.8.6/src/asgen/logging.d000066400000000000000000000050611416066706400212000ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.logging; import std.stdio; import std.string : format; import std.datetime; import glib.c.types : LogLevelFlags; private __gshared bool __verbose = false; enum LogSeverity : string { DEBUG = "DEBUG", INFO = "INFO", WARNING = "WARNING", ERROR = "ERROR" } @trusted void logMessage (LogSeverity, string, Args...) (const LogSeverity severity, const string tmpl, const Args args) { auto time = Clock.currTime (); auto timeStr = "%d-%02d-%02d %02d:%02d:%02d".format (time.year, time.month, time.day, time.hour,time.minute, time.second); writeln (timeStr, " - ", severity, ": ", format (tmpl, args)); } @trusted void logDebug (string, Args...) (const string tmpl, const Args args) { if (__verbose) logMessage (LogSeverity.DEBUG, tmpl, args); } @safe void logInfo (string, Args...) (const string tmpl, const Args args) { logMessage (LogSeverity.INFO, tmpl, args); } @safe void logWarning (string, Args...) (const string tmpl, const Args args) { logMessage (LogSeverity.WARNING, tmpl, args); } @safe void logError (string, Args...) (const string tmpl, const Args args) { logMessage (LogSeverity.ERROR, tmpl, args); } private extern(C) void logGExternal (const(char)* logDomain, LogLevelFlags logLevel, const(char)* message, void* userData) { import std.string : fromStringz; logDebug (message.fromStringz); } @trusted void setVerbose (const bool enabled) { import glib.MessageLog : MessageLog; __verbose = enabled; if (__verbose) { MessageLog.logSetHandler (null, LogLevelFlags.LEVEL_DEBUG, &logGExternal, null); } } appstream-generator-0.8.6/src/asgen/meson.build000066400000000000000000000051361416066706400215520ustar00rootroot00000000000000 conf_data = configuration_data() conf_data.set('datadir', join_paths(get_option('prefix'), get_option('datadir'), 'appstream')) conf_data.set('asgen_version', asgen_version) configure_file(input : 'defines.d.in', output: 'defines.d', configuration: conf_data ) data_import_dirs = include_directories('../../data') # # Sources # asgen_sources = [ '../app.d', 'bindings/libarchive.d', 'bindings/lmdb.d', 'config.d', 'contentsstore.d', 'datainjectpkg.d', 'datastore.d', 'downloader.d', 'engine.d', 'extractor.d', 'hintregistry.d', 'iconhandler.d', 'localeunit.d', 'logging.d', 'mustache.d', 'packageunit.d', 'reportgenerator.d', 'result.d', 'utils.d', 'zarchive.d', join_paths(build_root, 'src', 'asgen', 'defines.d'), ] backend_sources = [ 'backends/interfaces.d', 'backends/dummy/package.d', 'backends/dummy/dummypkg.d', 'backends/dummy/pkgindex.d', 'backends/alpinelinux/package.d', 'backends/alpinelinux/apkindexutils.d', 'backends/alpinelinux/apkpkg.d', 'backends/alpinelinux/apkpkgindex.d', 'backends/archlinux/package.d', 'backends/archlinux/alpkg.d', 'backends/archlinux/alpkgindex.d', 'backends/archlinux/listfile.d', 'backends/debian/package.d', 'backends/debian/debpkg.d', 'backends/debian/debpkgindex.d', 'backends/debian/tagfile.d', 'backends/debian/debutils.d', 'backends/ubuntu/package.d', 'backends/ubuntu/ubupkg.d', 'backends/ubuntu/ubupkgindex.d', 'backends/rpmmd/package.d', 'backends/rpmmd/rpmpkg.d', 'backends/rpmmd/rpmpkgindex.d', ] # # Build and test # asgen_exe = executable('appstream-generator', [asgen_sources, backend_sources], include_directories: [src_dir, gir_bind_dir], dependencies: [glibd_dep, appstream_dep, ascompose_dep, lmdb_dep, archive_dep, curl_dep], link_with: [girbind_lib], d_import_dirs: [data_import_dirs], install: true ) asgen_test_exe = executable('asgen_test', [asgen_sources, backend_sources], include_directories: [src_dir, gir_bind_dir], dependencies: [glibd_dep, appstream_dep, ascompose_dep, lmdb_dep, archive_dep, curl_dep], link_with: [girbind_lib], d_import_dirs: [data_import_dirs], d_unittest: true ) test('asgen_tests', asgen_test_exe, workdir: meson.source_root()) appstream-generator-0.8.6/src/asgen/mustache.d000066400000000000000000001275661416066706400214020ustar00rootroot00000000000000/* * Copyright (C) 2011 Masahiro Nakagawa * * Licensed under the GNU Lesser General Public License Version 3 * or Boost Software License 1.0. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . * * Mustache template engine for D * * Implemented according to [mustach(5)](http://mustache.github.com/mustache.5.html) * BSL-1.0 licensed source from https://github.com/repeatedly/mustache-d */ module mustache; import std.algorithm : all; import std.array; // empty, back, popBack, appender import std.conv; // to import std.datetime; // SysTime (I think std.file should import std.datetime as public) import std.file; // read, timeLastModified import std.path; // buildPath import std.range; // isOutputRange import std.string; // strip, chomp, stripLeft import std.traits; // isSomeString, isAssociativeArray static import std.ascii; // isWhite; version(unittest) import core.thread; /** * Exception for Mustache */ class MustacheException : Exception { this(string messaage) { super(messaage); } } /** * Core implementation of Mustache * * $(D_PARAM String) parameter means a string type to render. * * Example: * ----- * alias MustacheEngine!(string) Mustache; * * Mustache mustache; * auto context = new Mustache.Context; * * context["name"] = "Chris"; * context["value"] = 10000; * context["taxed_value"] = 10000 - (10000 * 0.4); * context.useSection("in_ca"); * * write(mustache.render("sample", context)); * ----- * sample.mustache: * ----- * Hello {{name}} * You have just won ${{value}}! * {{#in_ca}} * Well, ${{taxed_value}}, after taxes. * {{/in_ca}} * ----- * Output: * ----- * Hello Chris * You have just won $10000! * Well, $6000, after taxes. * ----- */ struct MustacheEngine(String = string) if (isSomeString!(String)) { static assert(!is(String == wstring), "wstring is unsupported. It's a buggy!"); public: alias Handler = String delegate(String); alias FindPath = string delegate(string); /** * Cache level for compile result */ static enum CacheLevel { no, /// No caching check, /// Caches compiled result and checks the freshness of template once /// Caches compiled result but not check the freshness of template } /** * Options for rendering */ static struct Option { string ext = "mustache"; /// template file extenstion string path = "."; /// root path for template file searching FindPath findPath; /// dynamically finds the path for a name CacheLevel level = CacheLevel.check; /// See CacheLevel Handler handler; /// Callback handler for unknown name } /** * Mustache context for setting values * * Variable: * ----- * //{{name}} to "Chris" * context["name"] = "Chirs" * ----- * * Lists section("addSubContext" name is drived from ctemplate's API): * ----- * //{{#repo}} * //{{name}} * //{{/repo}} * // to * //resque * //hub * //rip * foreach (name; ["resque", "hub", "rip"]) { * auto sub = context.addSubContext("repo"); * sub["name"] = name; * } * ----- * * Variable section: * ----- * //{{#person?}}Hi {{name}}{{/person?}} to "Hi Jon" * context["person?"] = ["name" : "Jon"]; * ----- * * Lambdas section: * ----- * //{{#wrapped}}awesome{{/wrapped}} to "awesome" * context["Wrapped"] = (string str) { return "" ~ str ~ ""; }; * ----- * * Inverted section: * ----- * //{{#repo}}{{name}}{{/repo}} * //{{^repo}}No repos :({{/repo}} * // to * //No repos :( * context["foo"] = "bar"; // not set to "repo" * ----- */ static final class Context { private: enum SectionType { nil, use, var, func, list } struct Section { SectionType type; union { String[String] var; String delegate(String) func; // func type is String delegate(String) delegate()? Context[] list; } @trusted this(bool u) nothrow { type = SectionType.use; } @trusted this(String[String] v) nothrow { type = SectionType.var; var = v; } @trusted this(String delegate(String) f) nothrow { type = SectionType.func; func = f; } @trusted this(Context c) nothrow { type = SectionType.list; list = [c]; } @trusted this(Context[] c) nothrow { type = SectionType.list; list = c; } /* nothrow : AA's length is not nothrow */ @trusted @property bool empty() const { final switch (type) { case SectionType.nil: return true; case SectionType.use: return false; case SectionType.var: return !var.length; // Why? case SectionType.func: return func is null; case SectionType.list: return !list.length; } } /* Convenience function */ @safe @property static Section nil() nothrow { Section result; result.type = SectionType.nil; return result; } } const Context parent; String[String] variables; Section[String] sections; public: @safe this(in Context context = null) nothrow { parent = context; } /** * Gets $(D_PARAM key)'s value. This method does not search Section. * * Params: * key = key string to search * * Returns: * a $(D_PARAM key) associated value. * * Throws: * a RangeError if $(D_PARAM key) does not exist. */ @safe String opIndex(in String key) const nothrow { return variables[key]; } /** * Assigns $(D_PARAM value)(automatically convert to String) to $(D_PARAM key) field. * * If you try to assign associative array or delegate, * This method assigns $(D_PARAM value) as Section. * * Arrays of Contexts are accepted, too. * * Params: * value = some type value to assign * key = key string to assign */ @trusted void opIndexAssign(T)(T value, in String key) { static if (isAssociativeArray!(T)) { static if (is(T V : V[K], K : String)) { String[String] aa; static if (is(V == String)) aa = value; else foreach (k, v; value) aa[k] = to!String(v); sections[key] = Section(aa); } else static assert(false, "Non-supported Associative Array type"); } else static if (isCallable!T) { import std.functional : toDelegate; auto v = toDelegate(value); static if (is(typeof(v) D == S delegate(S), S : String)) sections[key] = Section(v); else static assert(false, "Non-supported delegate type"); } else static if (isArray!T && !isSomeString!T) { static if (is(T : Context[])) sections[key] = Section(value); else static assert(false, "Non-supported array type"); } else { variables[key] = to!String(value); } } /** * Enable $(D_PARAM key)'s section. * * Params: * key = key string to enable * * NOTE: * I don't like this method, but D's typing can't well-handle Ruby's typing. */ @safe void useSection(in String key) { sections[key] = Section(true); } /** * Adds new context to $(D_PARAM key)'s section. This method overwrites with * list type if you already assigned other type to $(D_PARAM key)'s section. * * Params: * key = key string to add * size = reserve size for avoiding reallocation * * Returns: * new Context object that added to $(D_PARAM key) section list. */ @trusted Context addSubContext(in String key, lazy size_t size = 1) { auto c = new Context(this); auto p = key in sections; if (!p || p.type != SectionType.list) { sections[key] = Section(c); sections[key].list.reserve(size); } else { sections[key].list ~= c; } return c; } private: /* * Fetches $(D_PARAM)'s value. This method follows parent context. * * Params: * key = key string to fetch * * Returns: * a $(D_PARAM key) associated value. null if key does not exist. */ @trusted String fetch(in String[] key, lazy Handler handler = null) const { assert(key.length > 0); if (key.length == 1) { auto result = key[0] in variables; if (result !is null) return *result; if (parent !is null) return parent.fetch(key, handler); } else { auto contexts = fetchList(key[0..$-1]); foreach (c; contexts) { auto result = key[$-1] in c.variables; if (result !is null) return *result; } } return handler is null ? null : handler()(keyToString(key)); } @trusted const(Section) fetchSection()(in String[] key) const /* nothrow */ { assert(key.length > 0); // Ascend context tree to find the key's beginning auto currentSection = key[0] in sections; if (currentSection is null) { if (parent is null) return Section.nil; return parent.fetchSection(key); } // Decend context tree to match the rest of the key size_t keyIndex = 0; while (currentSection) { // Matched the entire key? if (keyIndex == key.length.to!long - 1) return currentSection.empty ? Section.nil : *currentSection; if (currentSection.type != SectionType.list) return Section.nil; // Can't decend any further // Find next part of key keyIndex++; foreach (c; currentSection.list) { currentSection = key[keyIndex] in c.sections; if (currentSection) break; } } return Section.nil; } @trusted const(Result) fetchSection(Result, SectionType type, string name)(in String[] key) const /* nothrow */ { auto result = fetchSection(key); if (result.type == type) return result.empty ? null : mixin("result." ~ to!string(type)); return null; } alias fetchVar = fetchSection!(String[String], SectionType.var, "Var"); alias fetchList = fetchSection!(Context[], SectionType.list, "List"); alias fetchFunc = fetchSection!(String delegate(String), SectionType.func, "Func"); } unittest { Context context = new Context(); context["name"] = "Red Bull"; assert(context["name"] == "Red Bull"); context["price"] = 275; assert(context["price"] == "275"); { // list foreach (i; 100..105) { auto sub = context.addSubContext("sub"); sub["num"] = i; foreach (b; [true, false]) { auto subsub = sub.addSubContext("subsub"); subsub["To be or not to be"] = b; } } foreach (i, sub; context.fetchList(["sub"])) { assert(sub.fetch(["name"]) == "Red Bull"); assert(sub["num"] == to!String(i + 100)); foreach (j, subsub; sub.fetchList(["subsub"])) { assert(subsub.fetch(["price"]) == to!String(275)); assert(subsub["To be or not to be"] == to!String(j == 0)); } } } { // variable String[String] aa = ["name" : "Ritsu"]; context["Value"] = aa; assert(context.fetchVar(["Value"]) == cast(const)aa); } { // func auto func = function (String str) { return "" ~ str ~ ""; }; context["Wrapped"] = func; assert(context.fetchFunc(["Wrapped"])("Ritsu") == func("Ritsu")); } { // handler Handler fixme = delegate String(String s) { assert(s=="unknown"); return "FIXME"; }; Handler error = delegate String(String s) { assert(s=="unknown"); throw new MustacheException("Unknow"); }; assert(context.fetch(["unknown"]) == ""); assert(context.fetch(["unknown"], fixme) == "FIXME"); try { assert(context.fetch(["unknown"], error) == ""); assert(false); } catch (MustacheException e) { } } { // subcontext auto sub = new Context(); sub["num"] = 42; context["a"] = [sub]; auto list = context.fetchList(["a"]); assert(list.length == 1); foreach (i, s; list) assert(s["num"] == to!String(42)); } } private: // Internal cache struct Cache { Node[] compiled; SysTime modified; } Option option_; Cache[string] caches_; public: @safe this(Option option) nothrow { option_ = option; } @property @safe nothrow { /** * Property for template extenstion */ const(string) ext() const { return option_.ext; } /// ditto void ext(string ext) { option_.ext = ext; } /** * Property for template searche path */ const(string) path() const { return option_.path; } /// ditto void path(string path) { option_.path = path; } /** * Property for callback to dynamically search path. * The result of the delegate should return the full path for * the given name. */ FindPath findPath() const { return option_.findPath; } /// ditto void findPath(FindPath findPath) { option_.findPath = findPath; } /** * Property for cache level */ const(CacheLevel) level() const { return option_.level; } /// ditto void level(CacheLevel level) { option_.level = level; } /** * Property for callback handler */ const(Handler) handler() const { return option_.handler; } /// ditto void handler(Handler handler) { option_.handler = handler; } } /** * Clears the intenal cache. * Useful for forcing reloads when using CacheLevel.once. */ @safe void clearCache() { caches_ = null; } /** * Renders $(D_PARAM name) template with $(D_PARAM context). * * This method stores compile result in memory if you set check or once CacheLevel. * * Params: * name = template name without extenstion * context = Mustache context for rendering * * Returns: * rendered result. * * Throws: * object.Exception if String alignment is mismatched from template file. */ String render()(in string name, in Context context) { auto sink = appender!String(); render(name, context, sink); return sink.data; } /** * OutputRange version of $(D render). */ void render(Sink)(in string name, in Context context, ref Sink sink) if(isOutputRange!(Sink, String)) { /* * Helper for file reading * * Throws: * object.Exception if alignment is mismatched. */ @trusted static String readFile(string file) { // cast checks character encoding alignment. return cast(String)read(file); } string file; if (option_.findPath) { file = option_.findPath(name); } else { file = buildPath(option_.path, name ~ "." ~ option_.ext); } Node[] nodes; final switch (option_.level) { case CacheLevel.no: nodes = compile(readFile(file)); break; case CacheLevel.check: auto t = timeLastModified(file); auto p = file in caches_; if (!p || t > p.modified) caches_[file] = Cache(compile(readFile(file)), t); nodes = caches_[file].compiled; break; case CacheLevel.once: if (file !in caches_) caches_[file] = Cache(compile(readFile(file)), SysTime.min); nodes = caches_[file].compiled; break; } renderImpl(nodes, context, sink); } /** * string version of $(D render). */ String renderString()(in String src, in Context context) { auto sink = appender!String(); renderString(src, context, sink); return sink.data; } /** * string/OutputRange version of $(D render). */ void renderString(Sink)(in String src, in Context context, ref Sink sink) if(isOutputRange!(Sink, String)) { renderImpl(compile(src), context, sink); } private: /* * Implemention of render function. */ void renderImpl(Sink)(in Node[] nodes, in Context context, ref Sink sink) if(isOutputRange!(Sink, String)) { // helper for HTML escape(original function from std.xml.encode) static void encode(in String text, ref Sink sink) { size_t index; foreach (i, c; text) { String temp; switch (c) { case '&': temp = "&"; break; case '"': temp = """; break; case '<': temp = "<"; break; case '>': temp = ">"; break; default: continue; } sink.put(text[index .. i]); sink.put(temp); index = i + 1; } sink.put(text[index .. $]); } foreach (ref node; nodes) { final switch (node.type) { case NodeType.text: sink.put(node.text); break; case NodeType.var: auto value = context.fetch(node.key, option_.handler); if (value) { if(node.flag) sink.put(value); else encode(value, sink); } break; case NodeType.section: auto section = context.fetchSection(node.key); final switch (section.type) { case Context.SectionType.nil: if (node.flag) renderImpl(node.childs, context, sink); break; case Context.SectionType.use: if (!node.flag) renderImpl(node.childs, context, sink); break; case Context.SectionType.var: auto var = section.var; auto sub = new Context(context); foreach (k, v; var) sub[k] = v; renderImpl(node.childs, sub, sink); break; case Context.SectionType.func: auto func = section.func; renderImpl(compile(func(node.source)), context, sink); break; case Context.SectionType.list: auto list = section.list; if (!node.flag) { foreach (sub; list) renderImpl(node.childs, sub, sink); } break; } break; case NodeType.partial: render(to!string(node.key.front), context, sink); break; } } } unittest { MustacheEngine!(String) m; auto render = (String str, Context c) => m.renderString(str, c); { // var auto context = new Context; context["name"] = "Ritsu & Mio"; assert(render("Hello {{name}}", context) == "Hello Ritsu & Mio"); assert(render("Hello {{&name}}", context) == "Hello Ritsu & Mio"); assert(render("Hello {{{name}}}", context) == "Hello Ritsu & Mio"); } { // var with handler auto context = new Context; context["name"] = "Ritsu & Mio"; m.handler = delegate String(String s) { assert(s=="unknown"); return "FIXME"; }; assert(render("Hello {{unknown}}", context) == "Hello FIXME"); m.handler = delegate String(String s) { assert(s=="unknown"); throw new MustacheException("Unknow"); }; try { assert(render("Hello {{&unknown}}", context) == "Hello Ritsu & Mio"); assert(false); } catch (MustacheException e) {} m.handler = null; } { // list section auto context = new Context; foreach (name; ["resque", "hub", "rip"]) { auto sub = context.addSubContext("repo"); sub["name"] = name; } assert(render("{{#repo}}\n {{name}}\n{{/repo}}", context) == " resque\n hub\n rip\n"); } { // var section auto context = new Context; String[String] aa = ["name" : "Ritsu"]; context["person?"] = aa; assert(render("{{#person?}} Hi {{name}}!\n{{/person?}}", context) == " Hi Ritsu!\n"); } { // inverted section { String temp = "{{#repo}}\n{{name}}\n{{/repo}}\n{{^repo}}\nNo repos :(\n{{/repo}}\n"; auto context = new Context; assert(render(temp, context) == "\nNo repos :(\n"); String[String] aa; context["person?"] = aa; assert(render(temp, context) == "\nNo repos :(\n"); } { auto temp = "{{^section}}This shouldn't be seen.{{/section}}"; auto context = new Context; context.addSubContext("section")["foo"] = "bar"; assert(render(temp, context).empty); } } { // comment auto context = new Context; assert(render("

Today{{! ignore me }}.

", context) == "

Today.

"); } { // partial std.file.write("user.mustache", to!String("{{name}}")); scope(exit) std.file.remove("user.mustache"); auto context = new Context; foreach (name; ["Ritsu", "Mio"]) { auto sub = context.addSubContext("names"); sub["name"] = name; } assert(render("

Names

\n{{#names}}\n {{> user}}\n{{/names}}\n", context) == "

Names

\n Ritsu\n Mio\n"); } { // dotted names auto context = new Context; context .addSubContext("a") .addSubContext("b") .addSubContext("c") .addSubContext("person")["name"] = "Ritsu"; context .addSubContext("b") .addSubContext("c") .addSubContext("person")["name"] = "Wrong"; assert(render("Hello {{a.b.c.person.name}}", context) == "Hello Ritsu"); assert(render("Hello {{#a}}{{b.c.person.name}}{{/a}}", context) == "Hello Ritsu"); assert(render("Hello {{# a . b }}{{c.person.name}}{{/a.b}}", context) == "Hello Ritsu"); } { // dotted names - context precedence auto context = new Context; context.addSubContext("a").addSubContext("b")["X"] = "Y"; context.addSubContext("b")["c"] = "ERROR"; assert(render("-{{#a}}{{b.c}}{{/a}}-", context) == "--"); } { // dotted names - broken chains auto context = new Context; context.addSubContext("a")["X"] = "Y"; assert(render("-{{a.b.c}}-", context) == "--"); } { // dotted names - broken chain resolution auto context = new Context; context.addSubContext("a").addSubContext("b")["X"] = "Y"; context.addSubContext("c")["name"] = "ERROR"; assert(render("-{{a.b.c.name}}-", context) == "--"); } } /* * Compiles $(D_PARAM src) into Intermediate Representation. */ static Node[] compile(String src) { bool beforeNewline = true; // strip previous whitespace bool fixWS(ref Node node) { // TODO: refactor and optimize with enum if (node.type == NodeType.text) { if (beforeNewline) { if (all!(std.ascii.isWhite)(node.text)) { node.text = ""; return true; } } auto i = node.text.lastIndexOf('\n'); if (i != -1) { if (all!(std.ascii.isWhite)(node.text[i + 1..$])) { node.text = node.text[0..i + 1]; return true; } } } return false; } String sTag = "{{"; String eTag = "}}"; void setDelimiter(String src) { auto i = src.indexOf(" "); if (i == -1) throw new MustacheException("Delimiter tag needs whitespace"); sTag = src[0..i]; eTag = src[i + 1..$].stripLeft(); } size_t getEnd(String src) { auto end = src.indexOf(eTag); if (end == -1) throw new MustacheException("Mustache tag is not closed"); return end; } // State capturing for section struct Memo { String[] key; Node[] nodes; String source; bool opEquals()(auto ref const Memo m) inout { // Don't compare source because the internal // whitespace might be different return key == m.key && nodes == m.nodes; } @trusted size_t toHash() const nothrow { return typeid(key).getHash(&key) + typeid(nodes).getHash(&nodes); } } Node[] result; Memo[] stack; // for nested section bool singleLineSection; while (true) { if (singleLineSection) { src = chompPrefix(src, "\n"); singleLineSection = false; } auto hit = src.indexOf(sTag); if (hit == -1) { // rest template does not have tags if (src.length > 0) result ~= Node(src); break; } else { if (hit > 0) result ~= Node(src[0..hit]); src = src[hit + sTag.length..$]; } size_t end; immutable type = src[0]; switch (type) { case '#': case '^': src = src[1..$]; auto key = parseKey(src, eTag, end); if (result.length == 0) { // for start of template singleLineSection = true; } else if (result.length > 0) { if (src[end + eTag.length] == '\n') { singleLineSection = fixWS(result[$ - 1]); beforeNewline = false; } } result ~= Node(NodeType.section, key, type == '^'); stack ~= Memo(key, result, src[end + eTag.length..$]); result = null; break; case '/': src = src[1..$]; auto key = parseKey(src, eTag, end); if (stack.empty) throw new MustacheException(to!string(key) ~ " is unopened"); auto memo = stack.back; stack.popBack(); stack.assumeSafeAppend(); if (key != memo.key) throw new MustacheException(to!string(key) ~ " is different from expected " ~ to!string(memo.key)); if (src.length == (end + eTag.length)) // for end of template fixWS(result[$ - 1]); if ((src.length > (end + eTag.length)) && (src[end + eTag.length] == '\n')) { singleLineSection = fixWS(result[$ - 1]); beforeNewline = false; } auto temp = result; result = memo.nodes; result[$ - 1].childs = temp; result[$ - 1].source = memo.source[0..src.ptr - memo.source.ptr - 1 - eTag.length]; break; case '>': // TODO: If option argument exists, this function can read and compile partial file. end = getEnd(src); result ~= Node(NodeType.partial, [src[1..end].strip()]); break; case '=': end = getEnd(src); setDelimiter(src[1..end - 1]); break; case '!': end = getEnd(src); break; case '{': src = src[1..$]; auto key = parseKey(src, "}", end); end += 1; if (end >= src.length || !src[end..$].startsWith(eTag)) throw new MustacheException("Unescaped tag is not closed"); result ~= Node(NodeType.var, key, true); break; case '&': src = src[1..$]; auto key = parseKey(src, eTag, end); result ~= Node(NodeType.var, key, true); break; default: auto key = parseKey(src, eTag, end); result ~= Node(NodeType.var, key); break; } src = src[end + eTag.length..$]; } return result; } unittest { { // text and unescape auto nodes = compile("Hello {{{name}}}"); assert(nodes[0].type == NodeType.text); assert(nodes[0].text == "Hello "); assert(nodes[1].type == NodeType.var); assert(nodes[1].key == ["name"]); assert(nodes[1].flag == true); } { // section and escape auto nodes = compile("{{#in_ca}}\nWell, ${{taxed_value}}, after taxes.\n{{/in_ca}}\n"); assert(nodes[0].type == NodeType.section); assert(nodes[0].key == ["in_ca"]); assert(nodes[0].flag == false); assert(nodes[0].source == "\nWell, ${{taxed_value}}, after taxes.\n"); auto childs = nodes[0].childs; assert(childs[0].type == NodeType.text); assert(childs[0].text == "Well, $"); assert(childs[1].type == NodeType.var); assert(childs[1].key == ["taxed_value"]); assert(childs[1].flag == false); assert(childs[2].type == NodeType.text); assert(childs[2].text == ", after taxes.\n"); } { // inverted section auto nodes = compile("{{^repo}}\n No repos :(\n{{/repo}}\n"); assert(nodes[0].type == NodeType.section); assert(nodes[0].key == ["repo"]); assert(nodes[0].flag == true); auto childs = nodes[0].childs; assert(childs[0].type == NodeType.text); assert(childs[0].text == " No repos :(\n"); } { // partial and set delimiter auto nodes = compile("{{=<% %>=}}<%> erb_style %>"); assert(nodes[0].type == NodeType.partial); assert(nodes[0].key == ["erb_style"]); } } static String[] parseKey(String src, String eTag, out size_t end) { String[] key; size_t index = 0; size_t keySegmentStart = 0; // Index from before eating whitespace, so stripRight // doesn't need to be called on each segment of the key. size_t beforeEatWSIndex = 0; void advance(size_t length) { if (index + length >= src.length) throw new MustacheException("Mustache tag is not closed"); index += length; beforeEatWSIndex = index; } void eatWhitespace() { beforeEatWSIndex = index; index = src.length - src[index..$].stripLeft().length; } void acceptKeySegment() { if (keySegmentStart >= beforeEatWSIndex) throw new MustacheException("Missing tag name"); key ~= src[keySegmentStart .. beforeEatWSIndex]; } eatWhitespace(); keySegmentStart = index; enum String dot = "."; while (true) { if (src[index..$].startsWith(eTag)) { acceptKeySegment(); break; } else if (src[index..$].startsWith(dot)) { acceptKeySegment(); advance(dot.length); eatWhitespace(); keySegmentStart = index; } else { advance(1); eatWhitespace(); } } end = index; return key; } unittest { { // single char, single segment, no whitespace size_t end; String src = "a}}"; auto key = parseKey(src, "}}", end); assert(key.length == 1); assert(key[0] == "a"); assert(src[end..$] == "}}"); } { // multiple chars, single segment, no whitespace size_t end; String src = "Mio}}"; auto key = parseKey(src, "}}", end); assert(key.length == 1); assert(key[0] == "Mio"); assert(src[end..$] == "}}"); } { // single char, multiple segments, no whitespace size_t end; String src = "a.b.c}}"; auto key = parseKey(src, "}}", end); assert(key.length == 3); assert(key[0] == "a"); assert(key[1] == "b"); assert(key[2] == "c"); assert(src[end..$] == "}}"); } { // multiple chars, multiple segments, no whitespace size_t end; String src = "Mio.Ritsu.Yui}}"; auto key = parseKey(src, "}}", end); assert(key.length == 3); assert(key[0] == "Mio"); assert(key[1] == "Ritsu"); assert(key[2] == "Yui"); assert(src[end..$] == "}}"); } { // whitespace size_t end; String src = " Mio . Ritsu }}"; auto key = parseKey(src, "}}", end); assert(key.length == 2); assert(key[0] == "Mio"); assert(key[1] == "Ritsu"); assert(src[end..$] == "}}"); } { // single char custom end delimiter size_t end; String src = "Ritsu-"; auto key = parseKey(src, "-", end); assert(key.length == 1); assert(key[0] == "Ritsu"); assert(src[end..$] == "-"); } { // extra chars at end size_t end; String src = "Ritsu}}abc"; auto key = parseKey(src, "}}", end); assert(key.length == 1); assert(key[0] == "Ritsu"); assert(src[end..$] == "}}abc"); } { // error: no end delimiter size_t end; String src = "a.b.c"; try { parseKey(src, "}}", end); assert(false); } catch (MustacheException e) { } } { // error: missing tag name size_t end; String src = " }}"; try { parseKey(src, "}}", end); assert(false); } catch (MustacheException e) { } } { // error: missing ending tag name size_t end; String src = "Mio.}}"; try { parseKey(src, "}}", end); assert(false); } catch (MustacheException e) { } } { // error: missing middle tag name size_t end; String src = "Mio. .Ritsu}}"; try { parseKey(src, "}}", end); assert(false); } catch (MustacheException e) { } } } @trusted static String keyToString(in String[] key) { if (key.length == 0) return null; if (key.length == 1) return key[0]; Appender!String buf; foreach (index, segment; key) { if (index != 0) buf.put('.'); buf.put(segment); } return buf.data; } /* * Mustache's node types */ static enum NodeType { text, /// outside tag var, /// {{}} or {{{}}} or {{&}} section, /// {{#}} or {{^}} partial /// {{>}} } /* * Intermediate Representation of Mustache */ static struct Node { NodeType type; union { String text; struct { String[] key; bool flag; // true is inverted or unescape Node[] childs; // for list section String source; // for lambda section } } /** * Constructs with arguments. * * Params: * t = raw text */ @trusted this(String t) nothrow { type = NodeType.text; text = t; } /** * ditto * * Params: * t = Mustache's node type * k = key string of tag * f = invert? or escape? */ @trusted this(NodeType t, String[] k, bool f = false) nothrow { type = t; key = k; flag = f; } /** * Represents the internal status as a string. * * Returns: * stringized node representation. */ string toString() const { string result; final switch (type) { case NodeType.text: result = "[T : \"" ~ to!string(text) ~ "\"]"; break; case NodeType.var: result = "[" ~ (flag ? "E" : "V") ~ " : \"" ~ keyToString(key) ~ "\"]"; break; case NodeType.section: result = "[" ~ (flag ? "I" : "S") ~ " : \"" ~ keyToString(key) ~ "\", [ "; foreach (ref node; childs) result ~= node.toString() ~ " "; result ~= "], \"" ~ to!string(source) ~ "\"]"; break; case NodeType.partial: result = "[P : \"" ~ keyToString(key) ~ "\"]"; break; } return result; } } unittest { Node section; Node[] nodes, childs; nodes ~= Node("Hi "); nodes ~= Node(NodeType.var, ["name"]); nodes ~= Node(NodeType.partial, ["redbull"]); { childs ~= Node("Ritsu is "); childs ~= Node(NodeType.var, ["attr"], true); section = Node(NodeType.section, ["ritsu"], false); section.childs = childs; nodes ~= section; } assert(to!string(nodes) == `[[T : "Hi "], [V : "name"], [P : "redbull"], ` ~ `[S : "ritsu", [ [T : "Ritsu is "] [E : "attr"] ], ""]]`); } } unittest { alias Mustache = MustacheEngine!(string); std.file.write("unittest.mustache", "Level: {{lvl}}"); scope(exit) std.file.remove("unittest.mustache"); Mustache mustache; auto context = new Mustache.Context; { // no mustache.level = Mustache.CacheLevel.no; context["lvl"] = "no"; assert(mustache.render("unittest", context) == "Level: no"); assert(mustache.caches_.length == 0); } { // check mustache.level = Mustache.CacheLevel.check; context["lvl"] = "check"; assert(mustache.render("unittest", context) == "Level: check"); assert(mustache.caches_.length > 0); core.thread.Thread.sleep(dur!"seconds"(1)); std.file.write("unittest.mustache", "Modified"); assert(mustache.render("unittest", context) == "Modified"); } mustache.caches_.remove("./unittest.mustache"); // remove previous cache { // once mustache.level = Mustache.CacheLevel.once; context["lvl"] = "once"; assert(mustache.render("unittest", context) == "Modified"); assert(mustache.caches_.length > 0); core.thread.Thread.sleep(dur!"seconds"(1)); std.file.write("unittest.mustache", "Level: {{lvl}}"); assert(mustache.render("unittest", context) == "Modified"); } } unittest { alias Mustache = MustacheEngine!(string); std.file.write("unittest.mustache", "{{>name}}"); scope(exit) std.file.remove("unittest.mustache"); std.file.write("other.mustache", "Ok"); scope(exit) std.file.remove("other.mustache"); Mustache mustache; auto context = new Mustache.Context; mustache.findPath((path) { if (path == "name") { return "other." ~ mustache.ext; } else { return path ~ "." ~ mustache.ext; } }); assert(mustache.render("unittest", context) == "Ok"); } appstream-generator-0.8.6/src/asgen/packageunit.d000066400000000000000000000143331416066706400220470ustar00rootroot00000000000000/* * Copyright (C) 2018-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ import std.conv : to; import glib.PtrArray : PtrArray; import glib.c.types : GQuark, GError, GBytes; import appstream.c.types : BundleKind; import ascompose.Unit : Unit; import ascompose.c.types; import asgen.backends.interfaces : Package; private __gshared extern(C) GQuark asc_compose_error_quark (); /** * Unit implementation that wraps a package. */ public final class PackageUnit : Unit { package Package pkg; import gobject.Type : Type; import gobject.c.functions : g_object_get_data; import ascompose.c.functions: asc_unit_get_type; import ascompose.c.functions : asc_unit_get_user_data; struct AGPackageUnit { AscUnit parentInstance; } struct AGPackageUnitClass { AscUnitClass parentClass; } protected AGPackageUnit *agPackageUnit; protected override void* getStruct() { return cast(void*)gObject; } public synchronized static GType getType() { import std.algorithm : startsWith; GType agPackageUnitType = Type.fromName("AGPackageUnit"); if (agPackageUnitType == GType.INVALID) { agPackageUnitType = Type.registerStaticSimple( asc_unit_get_type (), "AGPackageUnit", cast(uint)AGPackageUnitClass.sizeof, cast(GClassInitFunc) &agPackageUnitClassInit, cast(uint)AGPackageUnit.sizeof, null, cast(GTypeFlags)0); foreach (member; __traits(derivedMembers, PackageUnit)) { static if (member.startsWith("_implementInterface")) __traits(getMember, PackageUnit, member)(agPackageUnitType); } } return agPackageUnitType; } public this (Package pack) { import std.array : array; import std.typecons : scoped; import std.string : toStringz; import glib.c.functions : g_strdup, g_free; import gobject.c.functions : g_object_newv; super (cast(AscUnit*) g_object_newv (getType (), 0, null), true); this.pkg = pack; // helper for function override callbacks setUserData (cast(void*) this); // set identity setBundleId (pkg.name); setBundleKind (BundleKind.PACKAGE); auto contents = new PtrArray (pkg.contents.length.to!uint, &g_free); foreach (const ref fname; pkg.contents) contents.add (cast(void*) g_strdup (fname.toStringz)); setContents (contents); } extern(C) { static void agPackageUnitClassInit (void* klass) { AscUnitClass *ascUnitClass = cast(AscUnitClass*) klass; gobject.c.types.GObjectClass *gobjectCTypesGObjectClass = cast(gobject.c.types.GObjectClass*)klass; ascUnitClass.open = &agPackageUnitOpen; ascUnitClass.close = &agPackageUnitClose; ascUnitClass.dirExists = &agPackageUnitDirExists; ascUnitClass.readData = &agPackageUnitReadData; } static int agPackageUnitOpen (AscUnit *iface, GError** err) { auto impl = cast(PackageUnit) g_object_get_data (cast(GObject*)iface, "GObject".ptr); if (impl is null) impl = cast(PackageUnit) asc_unit_get_user_data (iface); return impl.open ()? 1 : 0; } static void agPackageUnitClose (AscUnit* iface) { auto impl = cast(PackageUnit) g_object_get_data (cast(GObject*)iface, "GObject".ptr); if (impl is null) impl = cast(PackageUnit) asc_unit_get_user_data (iface); impl.close(); } static int agPackageUnitDirExists (AscUnit* iface, const(char)* dirname) { import std.string : fromStringz; auto impl = cast(PackageUnit) g_object_get_data (cast(GObject*)iface, "GObject".ptr); if (impl is null) impl = cast(PackageUnit) asc_unit_get_user_data (iface); return impl.dirExists (dirname.fromStringz.to!string)? 1 : 0; } static GBytes *agPackageUnitReadData (AscUnit* iface, const(char)* filename, GError** err) { auto impl = cast(PackageUnit) g_object_get_data (cast(GObject*)iface, "GObject".ptr); if (impl is null) impl = cast(PackageUnit) asc_unit_get_user_data (iface); return impl.c_readData (filename, err); } } public override bool open () { // we already opened everything in the constructor // (this enables us to reuse this fake unit multiple times) return true; } public override void close () { pkg.finish (); } public override bool dirExists (string dirname) { // not implemented yet, as it's not needed (yet?) return false; } package GBytes *c_readData (const(char)* filename, GError** err) { import glib.c.functions : g_bytes_new_take, g_memdup, g_set_error_literal; import ascompose.c.types : ComposeError; import std.string : toStringz, fromStringz; const data = pkg.getFileData (filename.fromStringz.to!string); // FIXME: We should use g_memdup2 here, once we can bump the GLib version! void *ncCopy = g_memdup (cast(void*) data.ptr, cast(uint) data.length); return g_bytes_new_take (ncCopy, cast(size_t) data.length); } } appstream-generator-0.8.6/src/asgen/reportgenerator.d000066400000000000000000000717471416066706400230120ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.reportgenerator; import std.stdio : File, writeln; import std.string : format, strip, startsWith, indexOf; import std.path : buildPath, buildNormalizedPath, dirName; import std.file : mkdirRecurse, rmdirRecurse; import std.algorithm : canFind; import std.array : empty; import std.json; import std.conv : to; import std.typecons : scoped; static import std.file; import mustache; import appstream.Metadata; import appstream.c.types : IssueSeverity; import ascompose.Hint : Hint; static import appstream.Utils; alias AsUtils = appstream.Utils.Utils; import asgen.defines : ASGEN_VERSION; import asgen.utils; import asgen.config; import asgen.logging; import asgen.hintregistry; import asgen.backends.interfaces; import asgen.datastore; private alias Mustache = MustacheEngine!(string); final class ReportGenerator { private: Config conf; PackageIndex pkgIndex; DataStore dstore; string htmlExportDir; string templateDir; string defaultTemplateDir; string mediaPoolDir; string mediaPoolUrl; string versionInfo; Mustache mustache; struct HintTag { string tag; string message; } struct HintEntry { string identifier; string[] archs; HintTag[] errors; HintTag[] warnings; HintTag[] infos; } struct MetadataEntry { ComponentKind kind; string identifier; string[] archs; string data; string iconName; } struct PkgSummary { string pkgname; string[] cpts; int infoCount; int warningCount; int errorCount; } struct DataSummary { PkgSummary[string][string] pkgSummaries; HintEntry[string][string] hintEntries; MetadataEntry[string][string][string] mdataEntries; // package -> version -> gcid -> entry long totalMetadata; long totalInfos; long totalWarnings; long totalErrors; } public: this (DataStore db) { this.conf = Config.get (); // we need the data store to get hint and metainfo data dstore = db; htmlExportDir = conf.htmlExportDir; mediaPoolDir = dstore.mediaExportPoolDir; mediaPoolUrl = buildPath (conf.mediaBaseUrl, "pool"); // get template directory templateDir = conf.templateDir; defaultTemplateDir = buildNormalizedPath (templateDir, "..", "default"); mustache.path = templateDir; mustache.ext = "html"; // create version information to display on every page versionInfo = "%s, AS: %s".format (ASGEN_VERSION, AsUtils.appstreamVersion); } private string[] splitBlockData (string str, string blockType) { auto content = str.strip (); string blockName; if (content.startsWith ("{")) { auto li = content.indexOf("}"); if (li <= 0) throw new Exception ("Invalid %s: Closing '}' missing.", blockType); blockName = content[1..li].strip (); if (li+1 >= content.length) content = ""; else content = content[li+1..$]; } if (blockName is null) throw new Exception ("Invalid %s: Does not have a name.", blockType); return [blockName, content]; } private void setupMustacheContext (Mustache.Context context) { import std.datetime : Clock; string[string] partials; // this implements a very cheap way to get template inheritance // would obviously be better if our template system supported this natively. context["partial"] = (string str) { auto split = splitBlockData (str, "partial"); partials[split[0]] = split[1]; return ""; }; context["block"] = (string str) { auto split = splitBlockData (str, "block"); auto blockName = split[0]; str = split[1] ~ "\n"; auto partialCP = (blockName in partials); if (partialCP is null) return str; else return *partialCP; }; auto time = Clock.currTime (); auto timeStr = "%d-%02d-%02d %02d:%02d [%s]".format (time.year, time.month, time.day, time.hour,time.minute, time.timezone.stdName); context["time"] = timeStr; context["generator_version"] = versionInfo; context["project_name"] = conf.projectName; context["root_url"] = conf.htmlBaseUrl; } private void renderPage (string pageID, string exportName, Mustache.Context context) { setupMustacheContext (context); auto fname = buildPath (htmlExportDir, exportName) ~ ".html"; std.file.mkdirRecurse (dirName (fname)); if (!std.file.exists (buildPath (templateDir, pageID ~ ".html"))) { if (std.file.exists (buildPath (defaultTemplateDir, pageID ~ ".html"))) mustache.path = defaultTemplateDir; } logDebug ("Rendering HTML page: %s", exportName); auto data = mustache.render (pageID, context).strip (); auto f = File (fname, "w"); f.writeln (data); // reset default template path, we might have changed it mustache.path = templateDir; } private void renderPagesFor (string suiteName, string section, DataSummary dsum) { static import std.regex; if (templateDir is null) { logError ("Can not render HTML: No page templates found."); return; } logInfo ("Rendering HTML for %s/%s", suiteName, section); auto maintRE = std.regex.ctRegex!(`[àáèéëêòöøîìùñ~/\\(\\)" ']`, "g"); // write issue hint pages foreach (ref pkgname; dsum.hintEntries.byKey ()) { auto pkgHEntries = dsum.hintEntries[pkgname]; auto exportName = format ("%s/%s/issues/%s", suiteName, section, pkgname); auto context = new Mustache.Context; context["suite"] = suiteName; context["package_name"] = pkgname; context["section"] = section; context["entries"] = (string content) { string res; foreach (ref cid; pkgHEntries.byKey ()) { auto hentry = pkgHEntries[cid]; auto intCtx = new Mustache.Context; intCtx["component_id"] = cid; foreach (arch; hentry.archs) { auto archSub = intCtx.addSubContext("architectures"); archSub["arch"] = arch; } if (!hentry.errors.empty) intCtx["has_errors"] = ["has_errors": "yes"]; foreach (error; hentry.errors) { auto errSub = intCtx.addSubContext("errors"); errSub["error_tag"] = error.tag; errSub["error_description"] = error.message; } if (!hentry.warnings.empty) intCtx["has_warnings"] = ["has_warnings": "yes"]; foreach (warning; hentry.warnings) { auto warnSub = intCtx.addSubContext("warnings"); warnSub["warning_tag"] = warning.tag; warnSub["warning_description"] = warning.message; } if (!hentry.infos.empty) intCtx["has_infos"] = ["has_infos": "yes"]; foreach (info; hentry.infos) { auto infoSub = intCtx.addSubContext("infos"); infoSub["info_tag"] = info.tag; infoSub["info_description"] = info.message; } res ~= mustache.renderString (content, intCtx); } return res; }; renderPage ("issues_page", exportName, context); } // write metadata info pages foreach (ref pkgname; dsum.mdataEntries.byKey ()) { auto pkgMVerEntries = dsum.mdataEntries[pkgname]; auto exportName = format ("%s/%s/metainfo/%s", suiteName, section, pkgname); auto context = new Mustache.Context; context["suite"] = suiteName; context["package_name"] = pkgname; context["section"] = section; context["cpts"] = (string content) { string res; foreach (ver; pkgMVerEntries.byKey ()) { auto mEntries = pkgMVerEntries[ver]; foreach (gcid; mEntries.byKey ()) { auto mentry = mEntries[gcid]; auto intCtx = new Mustache.Context; intCtx["component_id"] = format ("%s - %s", mentry.identifier, ver); foreach (arch; mentry.archs) { auto archSub = intCtx.addSubContext("architectures"); archSub["arch"] = arch; } intCtx["metadata"] = mentry.data; auto cptMediaPath = buildPath (mediaPoolDir, gcid); auto cptMediaUrl = buildPath (mediaPoolUrl, gcid); string iconUrl; switch (mentry.kind) { case ComponentKind.UNKNOWN: iconUrl = buildPath (conf.htmlBaseUrl, "static", "img", "no-image.png"); break; case ComponentKind.DESKTOP_APP: case ComponentKind.WEB_APP: case ComponentKind.FONT: case ComponentKind.OPERATING_SYSTEM: if (std.file.exists (buildPath (cptMediaPath, "icons", "64x64", mentry.iconName))) iconUrl = buildPath (cptMediaUrl, "icons", "64x64", mentry.iconName); else iconUrl = buildPath (conf.htmlBaseUrl, "static", "img", "no-image.png"); break; default: iconUrl = buildPath (conf.htmlBaseUrl, "static", "img", "cpt-nogui.png"); break; } intCtx["icon_url"] = iconUrl; res ~= mustache.renderString (content, intCtx); } } return res; }; renderPage ("metainfo_page", exportName, context); } // write hint overview page auto hindexExportName = format ("%s/%s/issues/index", suiteName, section); auto hsummaryCtx = new Mustache.Context; hsummaryCtx["suite"] = suiteName; hsummaryCtx["section"] = section; hsummaryCtx["summaries"] = (string content) { string res; foreach (maintainer; dsum.pkgSummaries.byKey ()) { auto summaries = dsum.pkgSummaries[maintainer]; auto intCtx = new Mustache.Context; intCtx["maintainer"] = maintainer; intCtx["maintainer_anchor"] = std.regex.replaceAll (maintainer, maintRE, "_"); bool interesting = false; foreach (summary; summaries.byValue ()) { if ((summary.infoCount == 0) && (summary.warningCount == 0) && (summary.errorCount == 0)) continue; interesting = true; auto maintSub = intCtx.addSubContext("packages"); maintSub["pkgname"] = summary.pkgname; // again, we use this dumb hack to allow conditionals in the Mustache // template. if (summary.infoCount > 0) maintSub["has_info_count"] =["has_count": "yes"]; if (summary.warningCount > 0) maintSub["has_warning_count"] =["has_count": "yes"]; if (summary.errorCount > 0) maintSub["has_error_count"] =["has_count": "yes"]; maintSub["info_count"] = summary.infoCount; maintSub["warning_count"] = summary.warningCount; maintSub["error_count"] = summary.errorCount; } if (interesting) res ~= mustache.renderString (content, intCtx); } return res; }; renderPage ("issues_index", hindexExportName, hsummaryCtx); // write metainfo overview page auto mindexExportName = format ("%s/%s/metainfo/index", suiteName, section); auto msummaryCtx = new Mustache.Context; msummaryCtx["suite"] = suiteName; msummaryCtx["section"] = section; msummaryCtx["summaries"] = (string content) { string res; foreach (maintainer; dsum.pkgSummaries.byKey ()) { auto summaries = dsum.pkgSummaries[maintainer]; auto intCtx = new Mustache.Context; intCtx["maintainer"] = maintainer; intCtx["maintainer_anchor"] = std.regex.replaceAll (maintainer, maintRE, "_"); intCtx["packages"] = (string content) { string pRes; foreach (summary; summaries) { if (summary.cpts.length == 0) continue; auto subCtx = new Mustache.Context; subCtx["pkgname"] = summary.pkgname; foreach (cid; summary.cpts) { auto cptsSub = subCtx.addSubContext("components"); cptsSub["cid"] = cid; } pRes ~= mustache.renderString (content, subCtx); } return pRes; }; res ~= mustache.renderString (content, intCtx); } return res; }; renderPage ("metainfo_index", mindexExportName, msummaryCtx); // render section index page auto secIndexExportName = format ("%s/%s/index", suiteName, section); auto secIndexCtx = new Mustache.Context; secIndexCtx["suite"] = suiteName; secIndexCtx["section"] = section; float percOne = 100.0 / cast(float) (dsum.totalMetadata + dsum.totalInfos + dsum.totalWarnings + dsum.totalErrors); secIndexCtx["valid_percentage"] = dsum.totalMetadata * percOne; secIndexCtx["info_percentage"] = dsum.totalInfos * percOne; secIndexCtx["warning_percentage"] = dsum.totalWarnings * percOne; secIndexCtx["error_percentage"] = dsum.totalErrors * percOne; secIndexCtx["metainfo_count"] = dsum.totalMetadata; secIndexCtx["error_count"] = dsum.totalErrors; secIndexCtx["warning_count"] = dsum.totalWarnings; secIndexCtx["info_count"] = dsum.totalInfos; renderPage ("section_page", secIndexExportName, secIndexCtx); } private DataSummary preprocessInformation (string suiteName, string section, Package[] pkgs) { DataSummary dsum; logInfo ("Collecting data about hints and available metainfo for %s/%s", suiteName, section); auto dtype = conf.metadataType; auto mdata = scoped!Metadata (); mdata.setFormatStyle (FormatStyle.COLLECTION); mdata.setFormatVersion (conf.formatVersion); foreach (ref pkg; pkgs) { immutable pkid = pkg.id; auto gcids = dstore.getGCIDsForPackage (pkid); auto hintsData = dstore.getHints (pkid); if ((hintsData is null) && (gcids is null)) continue; PkgSummary pkgsummary; bool newInfo = false; pkgsummary.pkgname = pkg.name; if (pkg.maintainer in dsum.pkgSummaries) { auto pkgSumP = pkg.name in dsum.pkgSummaries[pkg.maintainer]; if (pkgSumP !is null) pkgsummary = *pkgSumP; else newInfo = true; } // process component metadata for this package if there are any if (gcids !is null) { foreach (gcid; gcids) { auto cid = getCidFromGlobalID (gcid); // don't add the same entry multiple times for multiple versions if (pkg.name in dsum.mdataEntries) { if (pkg.ver in dsum.mdataEntries[pkg.name]) { auto meP = gcid in dsum.mdataEntries[pkg.name][pkg.ver]; if (meP is null) { // this component is new dsum.totalMetadata += 1; newInfo = true; } else { // we already have a component with this gcid if (!(*meP).archs.canFind (pkg.arch)) (*meP).archs ~= pkg.arch; continue; } } } else { // we will add a new component dsum.totalMetadata += 1; } MetadataEntry me; me.identifier = cid; me.data = dstore.getMetadata (dtype, gcid); mdata.clearComponents (); if (dtype == DataType.YAML) mdata.parse (me.data, FormatKind.YAML); else mdata.parse (me.data, FormatKind.XML); auto cpt = mdata.getComponent (); if (cpt !is null) { auto iconsArr = cpt.getIcons (); assert (iconsArr !is null); for (uint i = 0; i < iconsArr.len; i++) { import appstream.Icon; auto icon = scoped!Icon (cast (AsIcon*) iconsArr.index (i)); if (icon.getKind () == IconKind.CACHED) { me.iconName = icon.getName (); break; } } me.kind = cpt.getKind (); } else { me.kind = ComponentKind.UNKNOWN; } me.archs ~= pkg.arch; dsum.mdataEntries[pkg.name][pkg.ver][gcid] = me; pkgsummary.cpts ~= format ("%s - %s", cid, pkg.ver); } } // process hints for this package, if there are any if (hintsData !is null) { auto hintsCpts = parseJSON (hintsData); hintsCpts = hintsCpts["hints"]; foreach (cid; hintsCpts.object.byKey ()) { auto jhints = hintsCpts[cid]; HintEntry he; // don't add the same hints multiple times for multiple versions and architectures if (pkg.name in dsum.hintEntries) { auto heP = cid in dsum.hintEntries[pkg.name]; if (heP !is null) { he = *heP; // we already have hints for this component ID he.archs ~= pkg.arch; // TODO: check if we have the same hints - if not, create a new entry. continue; } newInfo = true; } else { newInfo = true; } he.identifier = cid; foreach (jhint; jhints.array) { auto tag = jhint["tag"].str; Hint hint; try { hint = new Hint (tag); } catch (Exception e) { logError ("Encountered invalid tag '%s' in component '%s' of package '%s'", tag, cid, pkid); // emit an internal error, invalid tags shouldn't happen tag = "internal-unknown-tag"; hint = new Hint (tag); jhint["vars"] = ["tag": tag]; } // render the full message using the static template and data from the hint foreach (var; jhint["vars"].object.byKey ()) hint.addExplanationVar (var, jhint["vars"][var].str); const msg = hint.formatExplanation (); // add the new hint to the right category const severity = hint.getSeverity; if (severity == IssueSeverity.INFO) { he.infos ~= HintTag (tag, msg); pkgsummary.infoCount++; } else if (severity == IssueSeverity.WARNING) { he.warnings ~= HintTag (tag, msg); pkgsummary.warningCount++; } else if (severity == IssueSeverity.PEDANTIC) { // We ignore pedantic issues completely for now } else { he.errors ~= HintTag (tag, msg); pkgsummary.errorCount++; } } if (newInfo) he.archs ~= pkg.arch; dsum.hintEntries[pkg.name][he.identifier] = he; } } dsum.pkgSummaries[pkg.maintainer][pkg.name] = pkgsummary; if (newInfo) { dsum.totalInfos += pkgsummary.infoCount; dsum.totalWarnings += pkgsummary.warningCount; dsum.totalErrors += pkgsummary.errorCount; } } // rehash the tables for slightly better performance dsum.hintEntries.rehash; dsum.mdataEntries.rehash; dsum.pkgSummaries.rehash; return dsum; } private void saveStatistics (string suiteName, string section, DataSummary dsum) { auto stat = JSONValue (["suite": JSONValue (suiteName), "section": JSONValue (section), "totalInfos": JSONValue (dsum.totalInfos), "totalWarnings": JSONValue (dsum.totalWarnings), "totalErrors": JSONValue (dsum.totalErrors), "totalMetadata": JSONValue (dsum.totalMetadata)]); dstore.addStatistics (stat); } void exportStatistics () { import std.algorithm : sort; logInfo ("Exporting statistical data."); // return all statistics we have from the database auto statsCollection = dstore.getStatistics (); auto emptyJsonObject () { auto jobj = JSONValue (["null": 0]); jobj.object.remove ("null"); return jobj; } auto emptyJsonArray () { auto jarr = JSONValue ([0, 0]); jarr.array = []; return jarr; } // create JSON for use with e.g. Rickshaw graph auto smap = emptyJsonObject (); foreach (ref entry; statsCollection) { auto js = entry.data; immutable timestamp = entry.time; JSONValue jstats; if (js.type == JSONType.array) jstats = js; else jstats = JSONValue ([js]); foreach (ref jvals; jstats.array) { auto suite = jvals["suite"].str; auto section = jvals["section"].str; if (suite !in smap) smap.object[suite] = emptyJsonObject (); if (section !in smap[suite]) { smap[suite].object[section] = emptyJsonObject (); auto sso = smap[suite][section].object; sso["errors"] = emptyJsonArray (); sso["warnings"] = emptyJsonArray (); sso["infos"] = emptyJsonArray (); sso["metadata"] = emptyJsonArray (); } auto suiteSectionObj = smap[suite][section].object; auto pointErr = JSONValue ([JSONValue (timestamp), JSONValue (jvals["totalErrors"])]); suiteSectionObj["errors"].array ~= pointErr; auto pointWarn = JSONValue ([JSONValue (timestamp), JSONValue (jvals["totalWarnings"])]); suiteSectionObj["warnings"].array ~= pointWarn; auto pointInfo = JSONValue ([JSONValue (timestamp), JSONValue (jvals["totalInfos"])]); suiteSectionObj["infos"].array ~= pointInfo; auto pointMD = JSONValue ([JSONValue (timestamp), JSONValue (jvals["totalMetadata"])]); suiteSectionObj["metadata"].array ~= pointMD; } } bool compareJData (JSONValue x, JSONValue y) @trusted { size_t xv; size_t yv; if (x.array[0].type == JSONType.uinteger) xv = to!size_t (x.array[0].uinteger); else xv = to!size_t (x.array[0].integer); if (y.array[0].type == JSONType.uinteger) yv = to!size_t (y.array[0].uinteger); else yv = to!size_t (y.array[0].integer); return xv < yv; } // ensure our data is sorted ascending by X foreach (suite; smap.object.byKey ()) { foreach (section; smap[suite].object.byKey ()) { auto sso = smap[suite][section].object; sort!(compareJData) (sso["errors"].array); sort!(compareJData) (sso["warnings"].array); sort!(compareJData) (sso["infos"].array); sort!(compareJData) (sso["metadata"].array); } } auto fname = buildPath (htmlExportDir, "statistics.json"); mkdirRecurse (dirName (fname)); auto sf = File (fname, "w"); sf.writeln (smap.toJSON (false)); sf.flush (); sf.close (); } void processFor (string suiteName, string section, Package[] pkgs) { // collect all needed information and save statistics auto dsum = preprocessInformation (suiteName, section, pkgs); saveStatistics (suiteName, section, dsum); // drop old pages auto suitSecPagesDest = buildPath (htmlExportDir, suiteName, section); if (std.file.exists (suitSecPagesDest)) rmdirRecurse (suitSecPagesDest); // render fresh info pages renderPagesFor (suiteName, section, dsum); } void updateIndexPages () { import std.algorithm : sort; logInfo ("Updating HTML index pages and static data."); // render main overview auto context = new Mustache.Context; foreach (suite; conf.suites.sort!("a.name > b.name")) { auto sub = context.addSubContext("suites"); sub["suite"] = suite.name; auto secCtx = new Mustache.Context; secCtx["suite"] = suite.name; foreach (section; suite.sections) { auto secSub = secCtx.addSubContext("sections"); secSub["section"] = section; } renderPage ("sections_index", format ("%s/index", suite.name), secCtx); } foreach (suite; conf.oldsuites.sort!("a < b")) { auto sub = context.addSubContext("oldsuites"); sub["suite"] = suite; } renderPage ("main", "index", context); // copy static data, if present auto staticSrcDir = buildPath (templateDir, "static"); if (std.file.exists (staticSrcDir)) { auto staticDestDir = buildPath (htmlExportDir, "static"); if (std.file.exists (staticDestDir)) rmdirRecurse (staticDestDir); copyDir (staticSrcDir, staticDestDir); } } } unittest { writeln ("TEST: ", "Report Generator"); //auto rg = new ReportGenerator (null); //rg.renderIndices (); } appstream-generator-0.8.6/src/asgen/result.d000066400000000000000000000120011416066706400210600ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.result; import std.stdio; import std.string : format, fromStringz, toStringz; import std.array : empty; import std.conv : to; import std.algorithm : endsWith; import std.json; import appstream.Component; import appstream.c.types : BundleKind; import ascompose.Hint : Hint; import ascompose.Result : Result; import ascompose.c.types : AscHint; static import appstream.Utils; alias AsUtils = appstream.Utils.Utils; import asgen.hintregistry; import asgen.utils : buildCptGlobalID; import asgen.backends.interfaces; import asgen.config : Config; /** * Helper structure to tie a package instance and compose result together. */ struct GeneratorResult { Package pkg; Result res; alias res this; this (Package pkg) { res = new Result; res.setBundleKind (BundleKind.PACKAGE); res.setBundleId (pkg.name); this.pkg = pkg; } this (Result result, Package pkg) { this.res = result; this.res.setBundleKind (BundleKind.PACKAGE); this.res.setBundleId (pkg.name); this.pkg = pkg; } @property string pkid () { return pkg.id; } /** * Add an issue hint to this result. * Params: * id = The component-id or component itself this tag is assigned to. * tag = The hint tag. * params = Dictionary of parameters to insert into the issue report. * Returns: * True if the hint did not cause the removal of the component, False otherwise. **/ @trusted bool addHint (T) (T id, string tag, string[string] params) if (is(T == string) || is(T == Component) || is(T == typeof(null))) { static if (is(T == string)) { immutable cid = id; } else { static if (is(T == typeof(null))) immutable cid = "general"; else immutable cid = id.getId (); } string[] paramsFlat; foreach (const ref varName, ref varValue; params) paramsFlat ~= [varName, varValue]; return addHintByCid (cid, tag, paramsFlat); } /** * Add an issue hint to this result. * Params: * id = The component-id or component itself this tag is assigned to. * tag = The hint tag. * msg = An error message to add to the report. * Returns: * True if the hint did not cause the removal of the component, False otherwise. **/ @safe bool addHint (T) (T id, string tag, string msg = null) { string[string] vars; if (msg !is null) vars = ["msg": msg]; return addHint (id, tag, vars); } /** * Create JSON metadata for the hints found for the package * associacted with this GeneratorResult. */ string hintsToJson () { if (hintsCount () == 0) return null; // FIXME: is this really the only way you can set a type for JSONValue? auto map = JSONValue (["null": 0]); map.object.remove ("null"); foreach (ref cid; getComponentIdsWithHints ()) { auto cptHints = getHints (cid); auto hintNodes = JSONValue ([0, 0]); hintNodes.array = []; for (uint i = 0; i < cptHints.len; i++) { auto hint = new Hint (cast (AscHint*) cptHints.index (i)); hintNodes.array ~= hint.toJsonValue; } map.object[cid] = hintNodes; } auto root = JSONValue (["package": JSONValue (pkid), "hints": map]); return root.toJSON (true); } } unittest { import asgen.backends.dummy.dummypkg; writeln ("TEST: ", "GeneratorResult"); loadHintsRegistry (); auto pkg = new DummyPackage ("foobar", "1.0", "amd64"); auto res = new GeneratorResult (pkg); auto vars = ["rainbows": "yes", "unicorns": "no", "storage": "towel"]; res.addHint ("org.freedesktop.foobar.desktop", "desktop-file-hidden-set", vars); res.addHint ("org.freedesktop.awesome-bar.desktop", "metainfo-validation-error", "Nothing is good without chocolate. Add some."); res.addHint ("org.freedesktop.awesome-bar.desktop", "screenshot-video-check-failed", "Frobnicate functionality is missing."); writeln (res.hintsToJson ()); } appstream-generator-0.8.6/src/asgen/utils.d000066400000000000000000000422631416066706400207170ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.utils; @safe: import std.stdio : File, write, writeln; import std.string; import std.ascii : letters, digits; import std.conv : to; import std.random : randomSample; import std.range : chain; import std.algorithm : startsWith; import std.array : appender, empty; import std.path : buildPath, dirName, buildNormalizedPath; import std.typecons : Nullable, Rebindable; import std.traits : Unqual, hasIndirections; static import std.file; import glib.Bytes : Bytes; import appstream.Component; import appstream.Icon; static import appstream.Utils; alias AsUtils = appstream.Utils.Utils; import asgen.defines; import asgen.logging; import asgen.downloader : Downloader; public immutable DESKTOP_GROUP = "Desktop Entry"; public immutable GENERIC_BUFFER_SIZE = 8192; struct ImageSize { uint width; uint height; uint scale; this (uint w, uint h, uint s) { width = w; height = h; scale = s; } this (uint w, uint h) { width = w; height = h; scale = 1; } this (uint s) { width = s; height = s; scale = 1; } this (string str) { width = -1; height = -1; scale = -1; immutable sep = str.indexOf ("x"); if (sep <= 0) return; immutable scaleSep = str.indexOf ("@"); width = str[0..sep].to!uint; if (scaleSep <= 0) { scale = 1; height = str[sep+1..$].to!uint; } else { if (scaleSep == str.length) throw new Exception ("Image size string must not end with '@'."); height = str[sep+1..scaleSep].to!uint; scale = str[scaleSep+1..$].to!uint; } } string toString () const { if (scale == 1) return "%ux%u".format (width, height); else return "%ux%u@%u".format (width, height, scale); } uint toInt () const { if (width > height) return width * scale; return height * scale; } int opCmp (const ImageSize s) const { // only compares width, should be enough for now if (this.width > s.width) return 1; else if (this.width < s.width) return -1; if (this.scale > s.scale) return 1; else if (this.scale < s.scale) return -1; return 0; } } /** * Generate a random alphanumeric string. */ @trusted string randomString (uint len) { auto asciiLetters = to! (dchar[]) (letters); auto asciiDigits = to! (dchar[]) (digits); if (len == 0) len = 1; auto res = to!string (randomSample (chain (asciiLetters, asciiDigits), len)); return res; } /** * Check if the locale is a valid locale which we want to include * in the resulting metadata. Some locales added just for testing * by upstreams should be filtered out. */ @safe bool localeValid (string locale) pure { switch (locale) { case "x-test": case "xx": return false; default: return true; } } /** * Check if the given string is a top-level domain name. * The TLD list of AppStream is incomplete, but it will * cover 99% of all cases. * (in a short check on Debian, it covered all TLDs in use there) */ @trusted bool isTopLevelDomain (const string value) { if (value.empty) return false; return AsUtils.isTld (value); } /** * Build a global component ID. * * The global-id is used as a global, unique identifier for this component. * (while the component-ID is local, e.g. for one suite). * Its primary usecase is to identify a media directory on the filesystem which is * associated with this component. **/ @trusted string buildCptGlobalID (string cid, string checksum, bool allowNoChecksum = false) in { assert (cid.length >= 2); } do { if (cid is null) return null; if ((!allowNoChecksum) && (checksum is null)) return null; if (checksum is null) checksum = ""; // check whether we can build the gcid by using the reverse domain name, // or whether we should use the simple standard splitter. auto reverseDomainSplit = false; immutable parts = cid.split ("."); if (parts.length > 2) { // check if we have a valid TLD. If so, use the reverse-domain-name splitting. if (isTopLevelDomain (parts[0])) reverseDomainSplit = true; } string gcid; if (reverseDomainSplit) gcid = "%s/%s/%s/%s".format (parts[0].toLower(), parts[1], join (parts[2..$], "."), checksum); else gcid = "%s/%s/%s/%s".format (cid[0].toLower(), cid[0..2].toLower(), cid, checksum); return gcid; } /** * Get the component-id back from a global component-id. */ @trusted string getCidFromGlobalID (string gcid) { auto parts = gcid.split ("/"); if (parts.length != 4) return null; if (isTopLevelDomain (parts[0])) { return join (parts[0..3], "."); } return parts[2]; } @trusted void hardlink (const string srcFname, const string destFname) { import core.sys.posix.unistd; import core.stdc.string; import core.stdc.errno; immutable res = link (srcFname.toStringz, destFname.toStringz); if (res != 0) throw new std.file.FileException ("Unable to create link: %s".format (errno.strerror)); } /** * Copy a directory using multiple threads. * This function follows symbolic links, * and replaces them with actual directories * in destDir. * * Params: * srcDir = Source directory to copy. * destDir = Path to the destination directory. * useHardlinks = Use hardlinks instead of copying files. */ void copyDir (in string srcDir, in string destDir, bool useHardlinks = false) @trusted { import std.file; import std.path; import std.parallelism : parallel; import std.array : appender; auto deSrc = DirEntry (srcDir); auto files = appender!(string[]); if (!exists (destDir)) { mkdirRecurse (destDir); } auto deDest = DirEntry (destDir); if(!deDest.isDir ()) { throw new FileException (deDest.name, " is not a directory"); } immutable destRoot = deDest.name ~ '/'; if (!deSrc.isDir ()) { if (useHardlinks) hardlink (deSrc.name, destRoot); else std.file.copy (deSrc.name, destRoot); } else { auto srcLen = deSrc.name.length; if (!std.file.exists (destRoot)) mkdir (destRoot); // make an array of the regular files and create the directory structure // Since it is SpanMode.breadth, we can just use mkdir foreach (DirEntry e; dirEntries (deSrc.name, SpanMode.breadth, true)) { if (attrIsDir (e.attributes)) { auto childDir = destRoot ~ e.name[srcLen..$]; mkdir (childDir); } else { files ~= e.name; } } // parallel foreach for regular files foreach (fn; parallel (files.data, 100)) { immutable destFn = destRoot ~ fn[srcLen..$]; if (useHardlinks) hardlink (fn, destFn); else std.file.copy (fn, destFn); } } } /** * Escape XML characters. */ @safe S escapeXml (S) (S s) pure { string r; size_t lastI; auto result = appender!S (); foreach (i, c; s) { switch (c) { case '&': r = "&"; break; case '"': r = """; break; case '\'': r = "'"; break; case '<': r = "<"; break; case '>': r = ">"; break; default: continue; } // Replace with r result.put (s[lastI .. i]); result.put (r); lastI = i + 1; } if (!result.data.ptr) return s; result.put (s[lastI .. $]); return result.data; } /** * Get full path for an AppStream generator data file. */ @safe string getDataPath (string fname) { import std.path : buildNormalizedPath, dirName; immutable exeDirName = std.file.thisExePath.dirName; // useful for testing if (!exeDirName.startsWith ("/usr")) { auto resPath = buildNormalizedPath (exeDirName, "..", "..", "data", fname); if (std.file.exists (resPath)) return resPath; resPath = buildNormalizedPath (exeDirName, "..", "..", "..", "data", fname); if (std.file.exists (resPath)) return resPath; } auto resPath = buildPath (DATADIR, fname); if (std.file.exists (resPath)) return resPath; resPath = buildNormalizedPath (exeDirName, "..", "data", fname); if (std.file.exists (resPath)) return resPath; resPath = buildPath ("data", fname); if (std.file.exists (resPath)) return resPath; // Uh, let's just give up return buildPath ("/usr", "share", "appstream", fname); } /** * Check if a path exists and is a directory. */ bool existsAndIsDir (string path) @safe { if (std.file.exists (path)) if (std.file.isDir (path)) return true; return false; } /** * Convert a string array into a byte array. */ ubyte[] stringArrayToByteArray (string[] strArray) pure @trusted { auto res = appender!(ubyte[]); res.reserve (strArray.length * 2); // make a guess, we will likely need much more space foreach (ref s; strArray) { res ~= cast(ubyte[]) s; } return res.data; } /** * Check if string contains a remote URI. */ @safe bool isRemote (const string uri) { import std.regex : ctRegex, matchFirst; auto uriregex = ctRegex!(`^(https?|ftps?)://`); auto match = matchFirst (uri, uriregex); return (!match.empty); } /** * Download or open `path` and return it as a string array. * * Params: * path = The path to access. * * Returns: The data if successful. */ string[] getTextFileContents (const string path, const uint maxTryCount = 4, Downloader downloader = null) @trusted { import core.stdc.stdlib : free; import core.sys.posix.stdio : fclose, open_memstream; if (path.isRemote) { if (downloader is null) downloader = Downloader.get; return downloader.downloadTextLines (path, maxTryCount); } else { if (!std.file.exists (path)) throw new Exception ("No such file '%s'", path); return std.file.readText (path).splitLines; } } /** * Download or open `path` and return it as a byte array. * * Params: * path = The path to access. * * Returns: The data if successful. */ immutable(ubyte[]) getFileContents (const string path, const uint maxTryCount = 4, Downloader downloader = null) @trusted { import core.stdc.stdlib : free; import core.sys.posix.stdio : fclose, open_memstream; if (path.isRemote) { if (downloader is null) downloader = Downloader.get; return downloader.download (path, maxTryCount); } else { if (!std.file.exists (path)) throw new Exception ("No such file '%s'", path); ubyte[] data; auto f = File (path, "r"); while (!f.eof) { char[GENERIC_BUFFER_SIZE] buf; data ~= f.rawRead (buf); } return cast(immutable ubyte[]) data; } } /** * Get path of the directory with test samples. * The function will look for test data in the current * working directory. */ string getTestSamplesDir () @trusted { import std.file : getcwd; auto path = buildPath (getcwd (), "tests", "samples"); if (std.file.exists (path)) return path; path = buildNormalizedPath (getcwd (), "..", "tests", "samples"); return path; } /** * Return a suitable, "raw" icon name (either a stock icon name or local icon) * for this component that can be processed further by the generator. * Return null if this component does not have a suitable icon. */ @system Nullable!Icon componentGetRawIcon (ref Component cpt) { import gobject.c.functions : g_object_ref; Nullable!Icon iconLocal; auto iconsArr = cpt.getIcons (); for (uint i = 0; i < iconsArr.len; i++) { // cast array data to D AsIcon and keep a reference to the C struct auto icon = new Icon (cast(AsIcon*) g_object_ref (iconsArr.index (i)), true); if (icon.getKind == IconKind.STOCK) { Nullable!Icon iconStock = icon; return iconStock; } if (icon.getKind == IconKind.LOCAL) iconLocal = icon; } // only return local icon if we had no stock icon return iconLocal; } template StoredType(T) { static if ( is (T==immutable) || is(T==const) ) { static if ( is(T==class) ) { alias StoredType = Rebindable!T; } else { alias StoredType = Unqual!T; } } else { alias StoredType = T; } } bool UseGCRanges(T)() { return hasIndirections!T; } bool UseGCRanges(Allocator, T, bool GCRangesAllowed)() { import std.experimental.allocator.gc_allocator; return !is(Allocator==GCAllocator) && hasIndirections!T && GCRangesAllowed; } bool UseGCRanges(Allocator, K, V, bool GCRangesAllowed)() { import std.experimental.allocator.gc_allocator; return !is(Allocator == GCAllocator) && (hasIndirections!K || hasIndirections!V ) && GCRangesAllowed; } string filenameFromURI (string uri) { import std.path : baseName; import std.string : indexOf; auto bname = uri.baseName; immutable qInd = bname.indexOf ("?"); if (qInd > 0) bname = bname[0..qInd]; immutable hInd = bname.indexOf ("#"); if (hInd > 0) bname = bname[0..hInd]; return bname; } auto toGBytesCopy (const(ubyte)[] data) @trusted { import glib.c.functions : g_bytes_new_take, g_memdup; auto nc = cast(ubyte[]) data; // FIXME: We should use g_memdup2 here, once we can bump the GLib version! void *ncCopy = g_memdup (nc.ptr, cast(uint) nc.length); auto gBytes = g_bytes_new_take (ncCopy, cast(size_t) nc.length); return new Bytes (gBytes, true); } auto toStaticGBytes (const(ubyte)[] data) @trusted { import glib.c.functions : g_bytes_new_static; auto nc = cast(ubyte[]) data; auto gBytes = g_bytes_new_static (nc.ptr, cast(size_t) nc.length); return new Bytes (gBytes, true); } auto toStaticGBytes (ubyte[] data) @trusted { import glib.c.functions : g_bytes_new_static; auto gBytes = g_bytes_new_static (data.ptr, cast(size_t) data.length); return new Bytes (gBytes, true); } auto toStaticGBytes (const string data) @trusted { return toStaticGBytes (cast(immutable(ubyte)[]) data.representation); } @trusted unittest { import std.exception : assertThrown; writeln ("TEST: ", "Utils"); assert (buildCptGlobalID ("foobar.desktop", "DEADBEEF") == "f/fo/foobar.desktop/DEADBEEF"); assert (buildCptGlobalID ("org.gnome.yelp.desktop", "DEADBEEF") == "org/gnome/yelp.desktop/DEADBEEF"); assert (buildCptGlobalID ("noto-cjk.font", "DEADBEEF") == "n/no/noto-cjk.font/DEADBEEF"); assert (buildCptGlobalID ("io.sample.awesomeapp.sdk", "ABAD1DEA") == "io/sample/awesomeapp.sdk/ABAD1DEA"); assert (buildCptGlobalID ("io.sample.awesomeapp.sdk", null, true) == "io/sample/awesomeapp.sdk/"); assert (getCidFromGlobalID ("f/fo/foobar.desktop/DEADBEEF") == "foobar.desktop"); assert (getCidFromGlobalID ("org/gnome/yelp.desktop/DEADBEEF") == "org.gnome.yelp.desktop"); assert (ImageSize (80, 40).toString () == "80x40"); assert (ImageSize (1024, 420).toInt () == 1024); assert (ImageSize (1024, 800) > ImageSize (64, 32)); assert (ImageSize (48) < ImageSize (64)); assert (ImageSize ("64x64") == ImageSize (64)); assert (ImageSize ("64x64@2") == ImageSize (64, 64, 2)); assert (ImageSize ("128x128@2") == ImageSize (128, 128, 2)); assertThrown!Exception(ImageSize ("48x48@")); assert (ImageSize ("x23@2").height == -1); logInfo ("Size: %s", ImageSize ("64x64")); assert (stringArrayToByteArray (["A", "b", "C", "ö", "8"]) == [65, 98, 67, 195, 182, 56]); assert (isRemote ("http://test.com")); assert (isRemote ("https://example.org")); assert (!isRemote ("/srv/mirror")); assert (!isRemote ("file:///srv/test")); assert (filenameFromURI ("https://example.org/test/filename.txt") == "filename.txt"); assert (filenameFromURI ("https://example.org/test/video.mkv?raw=true") == "video.mkv"); assert (filenameFromURI ("https://example.org/test/video.mkv#anchor") == "video.mkv"); assert (filenameFromURI ("https://example.org/test/video.mkv?raw=true&aaa=bbb") == "video.mkv"); } appstream-generator-0.8.6/src/asgen/zarchive.d000066400000000000000000000445611416066706400213750ustar00rootroot00000000000000/* * Copyright (C) 2016-2021 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 3 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the license, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this software. If not, see . */ module asgen.zarchive; import core.stdc.string : strerror; import std.stdio; import std.string; import std.regex; import std.conv : to; import std.path : buildNormalizedPath; import std.array : appender; import std.typecons : RefCounted, RefCountedAutoInitialize; import std.concurrency : Generator, yield; static import std.file; import asgen.logging; import asgen.utils : GENERIC_BUFFER_SIZE; import asgen.bindings.libarchive; private immutable DEFAULT_BLOCK_SIZE = 65_536; enum ArchiveType { GZIP, XZ } private const(char)[] getArchiveErrorMessage (archive *ar) { return fromStringz (archive_error_string (ar)); } private string readArchiveData (archive *ar, string name = null) { archive_entry *ae; int ret; char[GENERIC_BUFFER_SIZE] buff; auto data = appender!string; data.reserve (GENERIC_BUFFER_SIZE / 4); ret = archive_read_next_header (ar, &ae); if (ret == ARCHIVE_EOF) return data.data; if (ret != ARCHIVE_OK) { if (name is null) throw new Exception (format ("Unable to read header of compressed data: %s", getArchiveErrorMessage (ar))); else throw new Exception (format ("Unable to read header of compressed file '%s': %s", name, getArchiveErrorMessage (ar))); } while (true) { immutable size = archive_read_data (ar, buff.ptr, buff.length); if (size < 0) { if (name is null) throw new Exception (format ("Failed to read compressed data: %s", getArchiveErrorMessage (ar))); else throw new Exception (format ("Failed to read data from '%s': %s", name, getArchiveErrorMessage (ar))); } if (size == 0) break; data ~= buff[0..size]; } return data.data; } string decompressFile (string fname) { int ret; archive *ar = archive_read_new (); scope(exit) archive_read_free (ar); archive_read_support_format_raw (ar); archive_read_support_format_empty (ar); archive_read_support_filter_all (ar); ret = archive_read_open_filename (ar, toStringz (fname), DEFAULT_BLOCK_SIZE); if (ret != ARCHIVE_OK) { auto ret_errno = archive_errno (ar); auto ret_strerr = fromStringz (strerror(ret_errno)); throw new Exception (format ("Unable to open compressed file '%s': %s. error: %s (%d)", fname, getArchiveErrorMessage (ar), ret_strerr, ret_errno)); } return readArchiveData (ar, fname); } string decompressData (ref ubyte[] data) { int ret; archive *ar = archive_read_new (); scope(exit) archive_read_free (ar); archive_read_support_filter_all (ar); archive_read_support_format_empty (ar); archive_read_support_format_raw (ar); auto dSize = ubyte.sizeof * data.length; ret = archive_read_open_memory (ar, data.ptr, dSize); if (ret != ARCHIVE_OK) throw new Exception (format ("Unable to open compressed data: %s", getArchiveErrorMessage (ar))); return readArchiveData (ar); } struct ArchiveDecompressor { private: string archive_fname; const(ubyte)[] readEntry (archive *ar) { const(void)* buff = null; size_t size = 0UL; long offset = 0; auto res = appender!(ubyte[]); res.reserve (GENERIC_BUFFER_SIZE / 4); while (archive_read_data_block (ar, &buff, &size, &offset) == ARCHIVE_OK) { res ~= cast(ubyte[]) buff[0..size]; } return res.data; } void extractEntryTo (archive *ar, string fname) { const(void)* buff = null; size_t size = 0UL; long offset = 0; long output_offset = 0; auto f = File (fname, "w"); // open for writing while (archive_read_data_block (ar, &buff, &size, &offset) == ARCHIVE_OK) { if (offset > output_offset) { f.seek (offset - output_offset, SEEK_CUR); output_offset = offset; } while (size > 0) { auto bytes_to_write = size; if (bytes_to_write > DEFAULT_BLOCK_SIZE) bytes_to_write = DEFAULT_BLOCK_SIZE; f.rawWrite (buff[0..bytes_to_write]); output_offset += bytes_to_write; size -= bytes_to_write; } } } archive *openArchive () { archive *ar = archive_read_new (); archive_read_support_filter_all (ar); archive_read_support_format_all (ar); auto ret = archive_read_open_filename (ar, archive_fname.toStringz, DEFAULT_BLOCK_SIZE); if (ret != ARCHIVE_OK) { auto ret_errno = archive_errno (ar); auto ret_strerr = fromStringz (strerror(ret_errno)); throw new Exception (format ("Unable to open compressed file '%s': %s. error: %s (%d)", archive_fname, getArchiveErrorMessage (ar), ret_strerr, ret_errno)); } return ar; } bool pathMatches (string path1, string path2) { import std.path; if (path1 == path2) return true; immutable path1Abs = buildNormalizedPath ("/", path1); immutable path2Abs = buildNormalizedPath ("/", path2); if (path1Abs == path2Abs) return true; return false; } public: struct ArchiveEntry { string fname; const(ubyte)[] data; } void open (string fname) { archive_fname = fname; } bool isOpen () { return archive_fname !is null; } void close () { archive_fname = null; } bool extractFileTo (string fname, string fdest) { archive_entry *en; auto ar = openArchive (); scope(exit) archive_read_free (ar); while (archive_read_next_header (ar, &en) == ARCHIVE_OK) { auto pathname = fromStringz (archive_entry_pathname (en)); if (pathMatches (fname, to!string (pathname))) { this.extractEntryTo (ar, fdest); return true; } else { archive_read_data_skip (ar); } } return false; } void extractArchive (const string dest) in { assert (std.file.isDir (dest)); } do { import std.path; archive_entry *en; auto ar = openArchive (); scope(exit) archive_read_free (ar); while (archive_read_next_header (ar, &en) == ARCHIVE_OK) { auto pathname = buildPath (dest, archive_entry_pathname (en).fromStringz); /* at the moment we only handle directories and files */ if (archive_entry_filetype (en) == AE_IFDIR) { if (!std.file.exists (pathname)) std.file.mkdir (pathname); continue; } if (archive_entry_filetype (en) == AE_IFREG) { this.extractEntryTo (ar, pathname); } } } const(ubyte)[] readData (string fname) { import core.sys.posix.sys.stat; import std.path; archive_entry *en; auto ar = openArchive (); scope(exit) archive_read_free (ar); while (archive_read_next_header (ar, &en) == ARCHIVE_OK) { immutable pathname = to!string (fromStringz (archive_entry_pathname (en))); if (pathMatches (fname, pathname)) { immutable filetype = archive_entry_filetype (en); if (filetype == S_IFDIR) { /* we don't extract directories explicitly */ throw new Exception (format ("Path '%s' is a directory and can not be extracted.", fname)); } /* check if we are dealing with a symlink */ if (filetype == S_IFLNK) { immutable fnameAbs = absolutePath (fname, "/"); string linkTarget = to!string (fromStringz (archive_entry_symlink (en))); if (linkTarget is null) throw new Exception (format ("Unable to read destination of symbolic link for '%s'.", fname)); try { if (!isAbsolute (linkTarget)) linkTarget = absolutePath (linkTarget, dirName (fnameAbs)); return readData (buildNormalizedPath (linkTarget)); } catch (Exception e) { logError ("Unable to read destination data of symlink in archive: %s", e.msg); return null; } } if (filetype != S_IFREG) { // we really don't want to extract special files from a tarball - usually, those shouldn't // be present anyway. // This should probably be an error, but return nothing for now. logError ("Tried to extract non-regular file '%s' from the archive", fname); return null; } return this.readEntry (ar); } else { archive_read_data_skip (ar); } } throw new Exception (format ("File '%s' was not found in the archive.", fname)); } string[] extractFilesByRegex (Regex!char re, string destdir) { import std.path; archive_entry *en; auto matches = appender!(string[]); auto ar = openArchive (); scope(exit) archive_read_free (ar); while (archive_read_next_header (ar, &en) == ARCHIVE_OK) { auto pathname = fromStringz (archive_entry_pathname (en)); const m = matchFirst (pathname, re); if (!m.empty) { auto fdest = buildPath (destdir, baseName (pathname)); this.extractEntryTo (ar, fdest); matches ~= fdest; } else { archive_read_data_skip (ar); } } return matches.data; } string[] readContents () { import std.conv : to; archive_entry *en; auto ar = openArchive (); scope (exit) archive_read_free (ar); auto contents = appender!(string[]); contents.reserve (20); while (archive_read_next_header (ar, &en) == ARCHIVE_OK) { auto pathname = fromStringz (archive_entry_pathname (en)); // ignore directories if (pathname.endsWith ("/")) continue; auto path = buildNormalizedPath ("/", to!string (pathname)); contents ~= path; } return contents.data; } /** * Returns a generator to iterate over the contents of this tarball. */ auto read () { import core.sys.posix.sys.stat; import std.path; auto gen = new Generator!ArchiveEntry ( { archive_entry *en; auto ar = openArchive (); scope (exit) archive_read_free (ar); while (archive_read_next_header (ar, &en) == ARCHIVE_OK) { auto pathname = fromStringz (archive_entry_pathname (en)); // ignore directories if (pathname.endsWith ("/")) continue; auto path = std.path.buildNormalizedPath ("/", to!string (pathname)); ArchiveEntry aentry; aentry.fname = path; aentry.data = null; auto filetype = archive_entry_filetype (en); /* check if we are dealing with a symlink */ if (filetype == S_IFLNK) { auto linkTarget = to!string (fromStringz (archive_entry_symlink (en))); if (linkTarget is null) throw new Exception (format ("Unable to read destination of symbolic link for '%s'.", path)); // we cheat here and set the link target as data // TODO: Proper handling of symlinks, e.g. by adding a filetype property to ArchiveEntry. aentry.data = cast(ubyte[]) linkTarget; yield (aentry); continue; } if (filetype != S_IFREG) { yield (aentry); continue; } aentry.data = this.readEntry (ar); yield (aentry); } }); return gen; } } /** * Save data to a compressed file. * * Params: * data = The data to save. * fname = The filename the data should be saved to. * atype = The archive type (GZ or XZ). */ void compressAndSave (ref ubyte[] data, const string fname, ArchiveType atype) { import std.file : isFile, remove, rename; auto ar = archive_write_new (); scope (exit) archive_write_free (ar); archive_write_set_format_raw (ar); if (atype == ArchiveType.GZIP) { archive_write_add_filter_gzip (ar); archive_write_set_filter_option (ar, "gzip", "timestamp", null); } else { archive_write_add_filter_xz (ar); } // don't write to the new file directly, we create a temporary file and // rename it when we successfully saved the data. auto tmpFname = fname ~ ".new"; auto ret = archive_write_open_filename (ar, tmpFname.toStringz); if (ret != ARCHIVE_OK) throw new Exception (format ("Unable to open file '%s': %s", tmpFname, getArchiveErrorMessage (ar))); archive_entry *entry; entry = archive_entry_new (); scope (exit) archive_entry_free (entry); archive_entry_set_filetype (entry, AE_IFREG); archive_entry_set_size (entry, ubyte.sizeof * data.length); archive_write_header (ar, entry); archive_write_data (ar, data.ptr, ubyte.sizeof * data.length); archive_write_close (ar); // delete old file if it exists if (std.file.exists (fname)) fname.remove (); // rename temporary file to actual file std.file.rename (tmpFname, fname); // we need to use std.file explicitly, because otherwise core.stdc gets used } final class ArchiveCompressor { private: string archiveFname; archive *ar; bool closed; public: this (ArchiveType type) { ar = archive_write_new (); if (type == ArchiveType.GZIP) { archive_write_add_filter_gzip (ar); archive_write_set_filter_option (ar, "gzip", "timestamp", null); } else { archive_write_add_filter_xz (ar); } archive_write_set_format_pax_restricted (ar); closed = true; } ~this () { close (); archive_write_free (ar); } void open (string fname) { archiveFname = fname; auto ret = archive_write_open_filename (ar, fname.toStringz); if (ret != ARCHIVE_OK) throw new Exception (format ("Unable to open file '%s'", fname, getArchiveErrorMessage (ar))); closed = false; } bool isOpen () { return !closed; } void close () { if (closed) return; archive_write_close (ar); closed = true; } void addFile (const string fname, const string dest = null) in { if (!std.file.exists (fname)) { logError ("File %s does not exist!", fname); assert (0); } } do { import std.conv: octal; import std.path: baseName; import core.sys.posix.sys.stat; immutable BUFFER_SIZE = 8192; archive_entry *entry; stat_t st; ubyte[BUFFER_SIZE] buff; string destName = dest; if (destName is null) destName = baseName (fname); lstat (fname.toStringz, &st); entry = archive_entry_new (); scope (exit) archive_entry_free (entry); archive_entry_set_pathname (entry, destName.toStringz); archive_entry_set_size (entry, st.st_size); archive_entry_set_filetype (entry, S_IFREG); archive_entry_set_perm (entry, octal!755); archive_entry_set_mtime (entry, st.st_mtime, 0); synchronized { archive_write_header (ar, entry); auto f = File (fname, "r"); while (!f.eof) { auto data = f.rawRead (buff); archive_write_data (ar, data.ptr, ubyte.sizeof * data.length); } } } } version (unittest) { version (GNU) { extern(C) char *mkdtemp (char *) nothrow @nogc; } else { import core.sys.posix.stdlib : mkdtemp; } } unittest { writeln ("TEST: ", "Compressed empty file"); ubyte[] emptyGz = [ 0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; assert (decompressData (emptyGz) == ""); writeln ("TEST: ", "Extracting a tarball"); import std.file : tempDir; import std.path : buildPath; import asgen.utils : getTestSamplesDir; auto archive = buildPath (getTestSamplesDir (), "test.tar.xz"); assert (std.file.exists (archive)); ArchiveDecompressor ar; auto tmpdir = buildPath (tempDir, "asgenXXXXXX"); auto ctmpdir = new char[](tmpdir.length + 1); ctmpdir[0 .. tmpdir.length] = tmpdir[]; ctmpdir[$ - 1] = '\0'; tmpdir = to!string(mkdtemp (ctmpdir.ptr)); scope(exit) std.file.rmdirRecurse (tmpdir); ar.open (archive); ar.extractArchive (tmpdir); auto path = buildPath (tmpdir, "b", "a"); assert (std.file.exists (path)); assert (std.file.readText (path).chomp == "hello"); } appstream-generator-0.8.6/src/meson.build000066400000000000000000000000211416066706400204410ustar00rootroot00000000000000 subdir('asgen') appstream-generator-0.8.6/tests/000077500000000000000000000000001416066706400166615ustar00rootroot00000000000000appstream-generator-0.8.6/tests/ci/000077500000000000000000000000001416066706400172545ustar00rootroot00000000000000appstream-generator-0.8.6/tests/ci/Dockerfile-debian-testing000066400000000000000000000006371416066706400241470ustar00rootroot00000000000000# # Docker file for AppStream Generator CI tests # FROM debian:testing # prepare RUN mkdir -p /build/ci/ # install build dependencies COPY install-deps-deb.sh /build/ci/ RUN chmod +x /build/ci/install-deps-deb.sh && /build/ci/install-deps-deb.sh # install 3rd-party stuff COPY ci-install-extern.sh /build/ci/ RUN chmod +x /build/ci/ci-install-extern.sh && /build/ci/ci-install-extern.sh # finish WORKDIR /build appstream-generator-0.8.6/tests/ci/Dockerfile-fedora-rawhide000066400000000000000000000006721416066706400241320ustar00rootroot00000000000000# # Docker file for AppStream Generator CI tests # FROM registry.fedoraproject.org/fedora:rawhide # prepare RUN mkdir -p /build/ci/ # install build dependencies COPY install-deps-rpm.sh /build/ci/ RUN chmod +x /build/ci/install-deps-rpm.sh && /build/ci/install-deps-rpm.sh # install 3rd-party stuff COPY ci-install-extern.sh /build/ci/ RUN chmod +x /build/ci/ci-install-extern.sh && /build/ci/ci-install-extern.sh # finish WORKDIR /build appstream-generator-0.8.6/tests/ci/build-and-test.sh000077500000000000000000000010601416066706400224240ustar00rootroot00000000000000#!/bin/sh # # This script is supposed to run inside the AppStream Generator Docker container # on the CI system. # set -e export LANG=C.UTF-8 if [ "$DC" = "ldc" ]; then export DC=ldc2 fi ROOT_DIR=$(pwd) echo "D compiler: $DC" set -v $DC --version meson --version # # Build & Test # mkdir -p build && cd build meson -Ddownload-js=true .. ninja -j8 # Run tests meson test -v --print-errorlogs # Test install DESTDIR=/tmp/install-ninja ninja install cd $ROOT_DIR # # Other checks # # run D-Scanner ./tests/ci/run-dscanner.py . tests/dscanner.ini || true appstream-generator-0.8.6/tests/ci/ci-install-extern.sh000077500000000000000000000014511416066706400231560ustar00rootroot00000000000000#!/bin/sh # # Build & install AppStream Generator dependencies # set -e set -x # Install dscanner mkdir -p /usr/local/bin/ curl -L https://github.com/dlang-community/D-Scanner/releases/download/v0.11.0/dscanner-v0.11.0-linux-x86_64.tar.gz -o /tmp/dscanner.tar.gz tar -xzf /tmp/dscanner.tar.gz -C /usr/local/bin/ rm /tmp/dscanner.tar.gz dscanner --version # build & install the current Git snapshot of AppStream mkdir /tmp/build cd /tmp/build && \ git clone --depth=10 https://github.com/ximion/appstream.git mkdir /tmp/build/appstream/build cd /tmp/build/appstream/build && \ meson --prefix=/usr \ -Dmaintainer=true \ -Dapt-support=true \ -Dcompose=true \ -Dapidocs=false \ .. cd /tmp/build/appstream/build && \ ninja && ninja install rm -rf /tmp/build appstream-generator-0.8.6/tests/ci/install-deps-deb.sh000077500000000000000000000026431416066706400227470ustar00rootroot00000000000000#!/bin/sh # # Install AppStream Generator build dependencies # set -e set -x export DEBIAN_FRONTEND=noninteractive # update caches apt-get update -qq # install build essentials apt-get install -yq \ eatmydata \ build-essential \ gdb \ gcc \ g++ \ gdc \ ldc \ git # install dependencies eatmydata apt-get install -yq --no-install-recommends \ meson \ gettext \ gobject-introspection \ gtk-doc-tools \ xsltproc \ docbook-xsl \ docbook-xml \ libgirepository1.0-dev \ libglib2.0-dev \ libstemmer-dev \ libxml2-dev \ libyaml-dev \ libxmlb-dev \ libcurl4-gnutls-dev \ gperf \ itstool eatmydata apt-get install -yq --no-install-recommends \ gir-to-d \ libglibd-2.0-dev \ liblmdb-dev \ libarchive-dev \ libpango1.0-dev . /etc/os-release if [ "$ID" = "ubuntu" ]; then gdk_pixbuf_dep="libgdk-pixbuf2.0-dev" else gdk_pixbuf_dep="libgdk-pixbuf-2.0-dev" fi; yarnpkg_dep="yarnpkg" if dpkg -s yarn >/dev/null 2>&1; then # if the conflicting "yarn" package was already installed, # don't try to install yarnpkg yarnpkg_dep="" fi eatmydata apt-get install -yq --no-install-recommends \ $gdk_pixbuf_dep \ librsvg2-dev \ libcairo2-dev \ libfontconfig1-dev \ libpango1.0-dev # install misc stuff eatmydata apt-get install -yq --no-install-recommends \ curl \ gnupg \ ffmpeg \ $yarnpkg_dep appstream-generator-0.8.6/tests/ci/install-deps-rpm.sh000077500000000000000000000025121416066706400230060ustar00rootroot00000000000000#!/bin/sh # # Install AppStream Generator build dependencies # set -e set -x # Install rpmfusion repositories dnf --assumeyes --quiet install https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm --eval %fedora).noarch.rpm # update caches dnf makecache # install dependencies dnf --assumeyes --quiet --setopt=install_weak_deps=False install \ curl \ gdb \ gcc \ gcc-c++ \ gcc-gdc \ git-core \ meson \ gettext \ gir-to-d \ gnupg \ gperf \ docbook-dtds \ docbook-style-xsl \ ldc \ libasan \ libstemmer-devel \ libubsan \ 'pkgconfig(cairo)' \ 'pkgconfig(freetype2)' \ 'pkgconfig(fontconfig)' \ 'pkgconfig(gdk-pixbuf-2.0)' \ 'pkgconfig(glib-2.0)' \ 'pkgconfig(gobject-2.0)' \ 'pkgconfig(gio-2.0)' \ 'pkgconfig(glibd-2.0)' \ 'pkgconfig(gobject-introspection-1.0)' \ 'pkgconfig(libarchive)' \ 'pkgconfig(libcurl)' \ 'pkgconfig(libsoup-2.4)' \ 'pkgconfig(librsvg-2.0)' \ 'pkgconfig(libxml-2.0)' \ 'pkgconfig(lmdb)' \ 'pkgconfig(packagekit-glib2)' \ 'pkgconfig(pango)' \ 'pkgconfig(protobuf-lite)' \ 'pkgconfig(Qt5Core)' \ qt5-linguist \ 'pkgconfig(yaml-0.1)' \ sed \ vala \ xmlto \ itstool \ diffutils \ /usr/bin/node \ /usr/bin/xsltproc \ /usr/bin/yarnpkg appstream-generator-0.8.6/tests/ci/run-dscanner.py000077500000000000000000000042771416066706400222420ustar00rootroot00000000000000#!/usr/bin/env python3 # # This script is supposed to be used on the appstream-generator CI, and makes # assumptions about the build environment. # If you want to run dscanner locally, you will want to adapt this script. # (at some point in future, we need DScanner to be run by a Meson module) # import os import sys import glob import subprocess BUILD_DIR_NAME = 'build' def find_local_include_dirs(source_root): res = list() for fname in glob.iglob(source_root + '/contrib/subprojects/**/*'): basename = os.path.basename(fname) if basename == 'src' or basename == 'source': res.append(fname) return res def find_include_dirs(source_root): incdirs = find_local_include_dirs(source_root) incdirs.append(os.path.join(source_root, 'src')) extra_inc = ['glibd-2'] for d in extra_inc: for inc_root in ['/usr/include/d/', '/usr/local/include/d/']: idir = os.path.join(inc_root, d) if os.path.isdir(idir): incdirs.append(idir) # LDC internal includes incdirs.extend(glob.glob('/usr/lib/ldc/*/include/d/')) incdirs.extend(glob.glob('/usr/lib/ldc/*/include/d/ldc/')) # Generated stuff incdirs.append(source_root + '/' + BUILD_DIR_NAME + '/girepo/') incdirs.append(source_root + '/' + BUILD_DIR_NAME + '/src/') return ['-I' + d for d in incdirs] def run(source_root, dscanner_config): print('===========================') print('= D-Scanner =') print('===========================') subprocess.run(['dscanner', '--version']) cmd = ['dscanner', '--styleCheck', os.path.join(source_root, 'src'), '--config', dscanner_config] cmd.extend(find_include_dirs(source_root)) pres = subprocess.run(cmd, cwd=source_root) if pres.returncode == 0: print('\033[92m:) Success \033[0m') sys.exit(0) else: print('\033[91m:( D-Scanner found issues \033[0m') print(pres) sys.exit(1) if __name__ == "__main__": if len(sys.argv) < 3: print('Need at least source-root and dscanner configuration as parameters!') sys.exit(1) run(source_root=sys.argv[1], dscanner_config=sys.argv[2]) appstream-generator-0.8.6/tests/dscanner.ini000066400000000000000000000110001416066706400211470ustar00rootroot00000000000000; Configure which static analysis checks are enabled [analysis.config.StaticAnalysisConfig] ; Check variable, class, struct, interface, union, and function names against the Phobos style guide style_check="disabled" ; Check for array literals that cause unnecessary allocation enum_array_literal_check="enabled" ; Check for poor exception handling practices exception_check="enabled" ; Check for use of the deprecated 'delete' keyword delete_check="enabled" ; Check for use of the deprecated floating point operators float_operator_check="enabled" ; Check number literals for readability number_style_check="enabled" ; Checks that opEquals, opCmp, toHash, and toString are either const, immutable, or inout. object_const_check="enabled" ; Checks for .. expressions where the left side is larger than the right. backwards_range_check="enabled" ; Checks for if statements whose 'then' block is the same as the 'else' block if_else_same_check="enabled" ; Checks for some problems with constructors constructor_check="enabled" ; Checks for unused variables and function parameters unused_variable_check="enabled" ; Checks for unused labels unused_label_check="enabled" ; Checks for unused function parameters unused_parameter_check="disabled" ; Checks for duplicate attributes duplicate_attribute="enabled" ; Checks that opEquals and toHash are both defined or neither are defined opequals_tohash_check="enabled" ; Checks for subtraction from .length properties length_subtraction_check="enabled" ; Checks for methods or properties whose names conflict with built-in properties builtin_property_names_check="enabled" ; Checks for confusing code in inline asm statements asm_style_check="enabled" ; Checks for confusing logical operator precedence logical_precedence_check="enabled" ; Checks for undocumented public declarations undocumented_declaration_check="disabled" ; Checks for poor placement of function attributes function_attribute_check="enabled" ; Checks for use of the comma operator comma_expression_check="enabled" ; Checks for local imports that are too broad local_import_check="disabled" ; FIXME: enable ; Checks for variables that could be declared immutable could_be_immutable_check="disabled" ; Checks for redundant expressions in if statements redundant_if_check="enabled" ; Checks for redundant parenthesis redundant_parens_check="enabled" ; Checks for mismatched argument and parameter names mismatched_args_check="enabled" ; Checks for labels with the same name as variables label_var_same_name_check="enabled" ; Checks for lines longer than 120 characters long_line_check="disabled" ; Checks for assignment to auto-ref function parameters auto_ref_assignment_check="enabled" ; Checks for incorrect infinite range definitions incorrect_infinite_range_check="enabled" ; Checks for asserts that are always true useless_assert_check="enabled" ; Check for uses of the old-style alias syntax alias_syntax_check="enabled" ; Checks for else if that should be else static if static_if_else_check="enabled" ; Check for unclear lambda syntax lambda_return_check="enabled" ; Check for auto function without return statement auto_function_check="enabled" ; Check for sortedness of imports imports_sortedness="disabled" ; Check for explicitly annotated unittests explicitly_annotated_unittests="disabled" ; FIXME: enable ; Check for properly documented public functions (Returns, Params) properly_documented_public_functions="disabled" ; Check for useless usage of the final attribute final_attribute_check="enabled" ; Check for virtual calls in the class constructors vcall_in_ctor="enabled" ; Check for useless user defined initializers useless_initializer="disabled" ; Check allman brace style allman_braces_check="disabled" ; Check for redundant attributes redundant_attributes_check="enabled" ; Check public declarations without a documented unittest has_public_example="disabled" ; Check for asserts without an explanatory message assert_without_msg="disabled" ; Check indent of if constraints if_constraints_indent="enabled" ; Check for @trusted applied to a bigger scope than a single function trust_too_much="enabled" ; Check for redundant storage classes on variable declarations redundant_storage_classes="enabled" ; Check for unused function return values unused_result="disabled" ; ModuleFilters for selectively enabling (+std) and disabling (-std.internal) i ; ndividual checks [analysis.config.ModuleFilters] style_check = "-asgen.bindings" number_style_check = "-asgen.bindings." object_const_check = "-asgen.backends.interfaces" unused_variable_check = "-asgen.backends.,-asgen.font" appstream-generator-0.8.6/tests/samples/000077500000000000000000000000001416066706400203255ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/appstream-logo.png000066400000000000000000000535061416066706400237760ustar00rootroot00000000000000PNG  IHDRUS pHYs  tIMEtEXtCommentCreated with GIMPWbKGDVIDATx}x6ZRRnwZ܃B$!,Fe}fwn( 6}L;;;3yϹizqSߝјMК-Ƥ?8{/h%ZZk ڗhM+B Z;FshNTa>(m@y-vmZglm;\(fdC u׏ut9a_k~\`XvZmqoKY +7N7"yD֖2H4^ jwA.gh#B[u-6C S?D+ ʒoebJ>!S-:} Í]R%/P xS𴪯)ED[tDkK\K=*i]UOH? "fN};C]\v#0V r RW+eaXrj8Q髓R҉ێP!6'5xAEDkDGeVi@J SQ~-^b1?w`h5"c$1YG`P i32 f#s)=ԁtkwHņ^ {Bqڸx<Bb]|ףPdJ~ Xb,bKG8YX5"qwiоcA#Jio\ѯ nFpCDw.crJt¶@Sk!^ "L7 V2.4>BL#z\df2$HBE+d M|TδoƗ$6j}U( tyrg>* 籉4{u+zo (^KˉOq60+IYH2 9эDBHؒįt2@L2[#7zlO]&:5]:'X2MDH`[_PfRfQP|ZYDBfJ`-OWT|DY(~q )B#_͉`?g2 ̚M[ sXxy{?8"LT aHhQJzڈ$Rp|\'WyM= ],T3-J̬8kĢw 69F/(P)z߃7/ϐzo쌏^;xAxeY"@Lq? )u7^XTǧ >|7thmqwbe k_fn`:-ܻF'Ms|vu`3~$X /b_ݕYZZ0*a]YqĢ bd:~c̞~t[k &gNAƀowOd/kp//ʵ%1&wFpK] /,,7R;ip=o暦w5@]F]j^cXFV&%Xc/9GBsm62$OH䢣əRDsggts1;yxE0j(Wn#]}ptqwww>JA`"$4").CpL$a @3g]{0-~?OΈ|3)Iq-1QayXbT@ɣJ")hbnfW`hMKMI9/4~$4:Q SM2h|F[{ _^rl\|061--)8:2>%-Tr !εɶgq2Ml6b2w37~ 6v`ffo߾@Ett4[ ԏPU%>3S62 iy9v3r~cnYqOFvm2ZŢJ⾍nSo|OX`=)ZPwCu no%8^~wF/; >`}p ΂#`~4’xIw&Pl| Ka1qnF74 l._Ľ|{IB#"|P{X"僁 ۣ;Xڭb6 U޻.BWwMREs_ޱ_=]y͆ώ\'7G APdGa,4T:^ؒw[VoyFuܳP;kEZWDDaV_Qplooncvś0/j͗V]qa7s_A v v50: aπQz"N[,a֐1*#4&㲍WxgcB3HPh ~~{%W?Y:lds|BdDhl)N{icPi|KX$xՉ&{:pgXKtj1?_-)i'Dkt'I:m:,}(FsN;s+z':8݂,ېvj`x<=q!ﮮ¢ҹ4aE7$d2n6Wbc e`kd~Yׇ @d ےA֩MnE7>+az$O]{.$$YFf%R[lAHs-]*Iarse{_3z47?J{A %@?E8]i[/+lB wWjW(ZG<m  {t^ٜ]tD?iIG{ _ۇ'!7<I ,D>Z?)NaaB PA؜_P \,H.7^jX$ciS)"iR1M\,G['c90SA[GArш,Мھ,tIi~eCU(G<1ʮ#Cz15,!hm`k6~)l1㥙뉅u4fr'4ap]?f'!Q~#waʶYln "MYi! e%@y*:P@,օ*N;(o6n#MJ3(("/L+D, SDF9\pLJmE%gi(,TGіDOj~wAԺͷg5)0mn'(i{ )LaKzwmf| ׉ mq +,Τ|q2&•h` )$vuMjS/L{4&@%QVGc9H 22ҹs4ZdsWpTwm+M`)uɘRݏԝȁqo|lωoܣW8asc ۉ`~3zA>нV pYDgcT0JhҞe5 ّOF32**j }DJBGC~m"oLORx<"X9;=>Zd 96bD&~FQ]Y@BɯyѱŷRs$9yʋ!h+M ^߾=&x$y&z΍٬J,`Vx0A 0BiŰMv%>3+smrUL$$E8xkf4ҠdՏ o:;ocf'0r=>,y/3`v5٬}Sz: _w1.G(CEEDGon;;aGlK$^AE%xƌ(R)!ifRN"W(vK4\!!̃Y&f@56&B|ि2=A94Vo?T-\ipԊt583'tT4GMpuz)tvS/`>,mhL}ژ'"I늤p?7sv+T^w[0tMȕ/tx{A/5 H?vR9Oԍɓm˩+Oqn6m*ܵkt˖Et; dSXbs%ODY=} \O#!T+* 5| Ϲx6.*sss{gN`kh#Z!B>Y*//)D(. vҶ29xKO= /qAaxҳ^;gv/7~i&,!@n# M#%42v+A%!1tхwۺ5kܹST_p݂OR ì29$[Y"ͤ~u{uly] a19&EHȀ^ gee 666d!y9yE(ۍ3aw':qnFUI6!rcsօ'mm{aM\ghК u* ܆\;B9%߳ ”bgg[6I/?"14(ֲNN9,"wv{_B-*#)-¬dMWW4o Abbz~~hXחswwYٙ3x)i(P Na46&-U$f tpm;G?(#, R:d??өԢ q'CN[P.LB}/'[d1<# csaql=Ȭgjw$u2 M,SP*}(x7)Lٔϕm {Ń!iXa)CFNg'ȵgYd]LT#A_RK]'Ӑ)q?Q?͕}t=[\Fk?QJAU;4k/d kS(F2%6fհefxTUAe<2r$ /{tB`}K ,C cɰB@Psq)9k\J_ =<A;VCCӳTl^n͟o=Yqa\]>eY\-Voqk-e8^>= !KxPOt>傁S2smlkhyR Faqq555'ݾ}{8jfHB(݅B|hgXMc|]4Dpt3 0PыI %Tڕ ϭ5haitg-mJ2S2JA&$=|"t{F"wiDZ8Z#,ARXB!N͔b @~avq# 60]]Ϙ\8FCY!Dp e.9.ԍcd4PSw8&l0ඔ-S7ԮgneAb;Z7:pDH J5?Шd09$ 4P7{PPg '0Gp'r λ98,Rd><3Hv&="Ұ02Lм :E`xb&vlH+̌L-ft]( T.2((3;e:{YhhTo!oFf,M@=/01L\S$вG6 bs gk;bAnkRC$T{N]i.np- -N7RҙlCKzŪrᅪ*}i¹xB"xIRX[$HHʀ$*&^"2.PSI}xA.4%#yŒ4d kN@!A]#[;XBZFѬ0aj;iNXc)Дѱqё II/ ^oڴYnڵ4nP]K#JYc ]Qo>eYv9eܹ٧]a97pm|T{8o>lS5g DnQoK M{ཱྀ'RË.>rD / ##ox2I}%ߘIRKd qtL07nPŇN8` QуoCe0h}N< `Ķܨ]f0fg 8p;O.JKwsscena9 vYot~ 'Uv6zIK4(8.\ZezZܐ@{ iQaY s3v pgc [9NW:>3c| @bkP̺H$jD]$uL^$/믺wnsVqŘIL83qe_i  Zq@= H6AǏ rW<;f g~À`઻0dc9nr@Fx>7?;z9wI׃IINFRtSbRIPY0PsФD!H6 IhOi..6;UpM7y uWPf%74Qy!wFq~ӣ}Mn,9 KB|Im>-q`(&4«tnJbS5q{-DVkgh.N܍ǶPS H9EbXVA(I,at * ,I)6ZIzlTy w!`!=a"Rx5*-/lL:=x2cR*1%xL|F 'AP\^"xԽ=xf@p+x/${9N6lb$~9v>/'90K$AaQ1>{FVsp>!/3ɵ+|KW 8^~n=5M]s=omv7W6{vܘ-zksrܼ)Qn"R@,pvҋ0r%t+Pg;hڙ2q`Vl-= Zg3 aGx춆?Oz.,E1TJb N\z1@ɒ276;>6hNT]34e?NQ\͉7 5v6Ym'Z R2v%fsc.OXq;褫 s9+;4C2#>p({e'0o̝Y9" @Ar %<(0*cP,C`iλ{lᖁ',p,DlI䥰ҴpVqnN+@Ne$wT']{͛|ԑ3j6dc2"#F eQ 8{ wea4r"": }&R72M$n>.̋{9b 3ao /% |HdSLs$MTr(Zi9RIFn?pNgb$۳NV_x:W,eS\a(eə_% %7x̂\`vSt &0| \ yehg/q*2.0"pF c&izr1 ,$R ! )ɵ xHj"4"dݩ0]JG'P ry᣿..xY샑so,H{Al&$ _( 48@6MW}أK;514LzOtX'^,RrD T+ؐPsLz)$$Bjf Q*b- ]@&,:ö銝V T ݉mwXF5o)@A";ph@L"cYG0KA#"֥cR6pr#a)ʩ9B6^hmYGGc $^yBBװ,$[Hw?GvFZqoF"bag=]`5 *؜3{p3[r \WT~q` ^#RV2pu׽ w  g `[G Hkc 9(⽠(~܃*kT0dn 64;l3fĶ"=DS;Kw/Fd~;퐅x:]v̀OuNOt"҈&Fr``k a 2~KJI^`ƕ6Iۯ;lG%)xM=Aw%!{>/ˁQ[UaڀPP YUad]?ݐ]|S|D'@"630#^(MJElx`12:CZD |E2x{ L0qd[ĉ= /دT eXݖMU`\!c<ІxłKU@Q_ ao\}_ȕ!Fl}-t!oe- u <#sQyҠB]eb\E&䗀@0vb5I-Jw n+r̉UmUyo{=++g {ق;(} fZUqLM~M2N=guljF" ?kyKw =ʌU#0x_~#b[#0E7gj٢& uE]:Mp|]mif<#Bn]z~xl߅/A)i*Fx˞κ gv%w#/##JD!0 0oI^U@Yg ~{AX;/U~_&hi=b}u_S2l;dn"0] 0^1d 7[-0)ޘ:ŻP vLRBq"s( i u(W =54,M<>7pf0pu3O. =oOXx^y/00E lQK.@ P0Huਉ54ʁ^j4#dD2)#@#Sƶ7R-;kX+p`(GD%pԍ9jGAce jEXcm"0Ƚ ]bL\z)7e>h(0A`ȁQ ER0o=ſjje͡{|f*1^ T8|eЮȲ4nQ%<0~1"Z!hs%h3zsl2b˫J{+Ti j&Y :.ZClh%`ެ jFq c|#ksWɥgD2`4m lT!PWR]ۛ@EKAAU0s ű xSwK#}@902SYb20{r`ko0H-BZX:3hmT!sCbhR$@8Z)[yFa-ҏf'^|b]gn\nlQ٪F]QN. y6'9;5! WVw 7F:7R0bsp.=ߦ y[H+4>kP;f&CAj_ e >yӏXx>OA&PДL! %Y6ThH*Bb` zY5C歡i6u#1h>L(̠M ~Y ǧ-t l1`y;FHtA2XCP60T5o zO?jq|DXLް7ls-MB[Rm6A_p%Vk-pV?g-H ~?ri]_~<*2-fe ,;vϿAL9LD0[~>۹ ˒[eYO`(zg5:dG$@7B\ r>@Zf.8>oBњlz(_6!9wTays} ~riSpIJy r~s" |Ӳ'h3,d8.?q}4PjJJЮo0#$ h|:`t\[T`>Nw DkY-y]!V}'h6Z>~o?)G!.ٲ}VZŲ 3Y}h?Fy8em ڌ =3C7uOQp~^wcL<`OV` bBhU+[ $%|Qku`QBwȝf;W݇a>Rq$BK`-gzrhܬe!gGڛA\Wqj d]1uILZA[hº'ѦS2ܚQB} +ɹ Xw'*`(6*i~ 9Q{ED lt/RSBT}U딦-]XٕhF,m@يb6_!~^k_z׫e}L1d{HE u}H4J 7 _Fa9Rsu|HBF Xtzj~8 =sY.u~)-EFYB{Џz[UՔ_q3KF{|ݢ(/8 r`QU30`]\y$,w1%']Qj]V/"05Ջf#k>[ClpI9g> άU7Rg4[&ؠe+8af A+Fv]wXĕ}ߪePi-0И 1-Ȉב )$SĿ}\`l}\8o-A}f} p:[72x3X&i|.04 HbEǮcYOF-,+0BJЇWidb$DAν(AVAka^3~/a]g篱ѥ1^G֨K>Q(}P?)*i6`cXyٍYylWv? ~0{m(,$SrpJ7ق,fvBn mP2U(4iߥ^N?-ˤhGBi& (AP[;u{G^?d{!Tux4E./ANPz/ u)ˆ9+]Kvȅ j^HN>dږ :}ꎨ1Z#AUXSC룺5FDT^fZ֨[u\b,uH\?[ߐ'&AWPpQXpjҼ{zpc$8mP!% '7;$CvrHBiƹg-MY0d8*2sud4&֨:ՃC#rHz9 "@6L)8<\g9IrW:K5 2YF`T^ ZRl[w"{0a!\~7 K%|#Ieb < bKڡZCR bA<:X\s70$Ԅ鬵jOExkh6:u ;ȧkm!H+6"iԬ >ϯ}4Nq &@|Q#g& 7 QY9(u%:Uh-"5>AR]WwQ]lQs噂@D4@?zd+5Lc#  8ߴ -z8 w:aHhI^{ ?~fm:uw &]P!Ih":^H^Ag=[BaCaFP`j#ZruD]z= +`8N>(}4UM|3i[FpT'b[aXu+Jd4mt~T^_-wC8y~(f9:vm'+ԙy1#r`/T-Ȃ!5Yj}5iZt| eTE>[r`8KE. I+y{?A/~;!$IXEȟH@j>eFso _ YɪHF_y`(Ϊ5h#CX0xv`8~jGe7R-V A>v0=(U_/Zh?M[z̈́cQ+epeP;5anSP9DP(`ᬖ 4') 08`HJkvP¶'f^VsU~gNh~hZF1=dKX ,C:\pG#":ċK1u;oࢲZ`:)$f J2iːEp MYD%dO ue8>>PȁQ>M1xxWYIuX%/QP6݃BR>5]tJCYcZ[q s݀T)9B/zT( E_p ׽Aof:VEf!K`QG~JBf)_G>>KnCv_MSKOB[x"{@U`ܨШv,FC4E^Uuoear\Bs`@PQK/"@.SU .4m ?MBI26F_;mYЬumH80|cK[cJļ6L)sGBмgربϑ\tC2tFmxZ'Œh{(M?1cB@TFq%tƍD"ZMB{"(*8{Fޯ0l:"a[yeWF \56?m;ɤ/×w>Z>y,r PċS$#) ~~8nϕ3La_XHLzP̂E(t10Mt7ZVp`)(v(fOi=n}rit!=vp:CJ*L<yK Fju0q)X'YLy8jTņ~Fþ;^(2UVG.q"~v1+9 ;oc5~Zؤ U'B>r)݇΁.Y C, Cܲ%K޸b4cHjTjG"nDJ.Ĉ/a70j1o#=Lmr:),C2_&_+Ն~:0ex2ZK+4Es<WŸ3 QPH'B]XzHz(/S\E04w0nݨnm?@؃,!]S:xjVWM _B#UFu"Lypǭ ʠU"Hf2qDXҐ-EW % "د z.FNfu)+D&(7~`E3e 주VQH֟Aۯ~" i!k/{ Wpo*}q>~? =,Y'Q>*\ ƽJBB:ș:A: SD2˩6кhܾ+f7ˀX ;=Vއ!-WX.d3İ#hԴ9M} ˎ<t_Io\S1J {fq3nT#ikBBQJGks_# h\uOgjbR\G%WS&6 _>÷rD]H1 *'2iz@ס߲pX':J҃:ШI}m UDzcRDD8'AU`Bk5N.C dVylw I R=5dd]y!\zůϭ((K. A+Xds%dM@&HyQ)Ư] #WߪJZv~ 0uX~!7y|Lh,[LBۂ;6nhX愖V}@jwdl%UӜZ3h}bVL5”W@y{QU^/=QAL^CXm[p4%Oq&I-KsB!sƕӱN%W 9].#O$ /xU jx_RY'H3OL9--MBu1@R$תZkF "<L2FVn0c&4W(mGXayZݪ|,/ih߯ e?d )Q%1OQXzIcől+⦐q(vOȽW^:|GEǾDYD{憖E+BOwsH.THe&2R=i~(\w!(yÔ סIvS. y D `[c3l_uSsƱ>ihWў!Hђ(h,\U*_jA};T+Q< EnE7aUGV.b ShMG7;t G&ICc vB{*Ḓ Ba4 N#6D2x"&^*>d>))b;4ϣ],@٣ Pp[k)0lulR,iey% [oLa90,\=5Q֝~6_tMhï>#oǾ _WW^йi^!$-/my10{ Z)P*M*APhW ۚaH< m?:e SK*˼V9~*ܹ=gi{_*[ubXy~zϿX[~pI?ㄝȨbjSH}o^YAdp % 2x ¸۽h"0=(B\C{E4 %  WvGbz-ф#8yi.JsfpVZw:I#!ݿM=#Fas]&?ORĝxE#dJJфrƐ!P1 ݣvwx Shh0O 裳٧nj`V}xd :Z]|8atm r V]79$/s:]ooa<{X +@I,Cآ"K#,_{dF+*L)(1Rڼ RTp)?*ʌn*/3utmno>X?hw|g}sgy?eOO`Xyzf q[V`ǑXy9 '539Fck;0uݣW E5K+6qX+dA&ͪ"A0m=z@-%h@J:$$ÑK@)9y;#,$NzǕ?Tѭm THkյjoLvJ7+Ǿ&Y P Bm{z 76:6fAE/|`9o6yߡP"(]nVinwz % RSJ&R aTZCD3; :a-F-Jl9 9K=D*)iT YlCOd񗫙gj@5=Y; 7(!GB2,G(GZxo ~?qbɛ O᥹MmoWi-mLօ+sF?P{Wh1>SJS!4]|"Z +E8Пy[ f:&aYVުJFJ-'>nɭmI z 8`P IWtI @l?a@Z? bsJƝrcUZ>°P׹`FЧ`O-[U%Q; Y/d@>ShC7dXL\M5KJZV$d =g0v.g{͹@wC#hI誇^ŋMĎ ygNَ^xm*RIENDB`appstream-generator-0.8.6/tests/samples/debian/000077500000000000000000000000001416066706400215475ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/debian/dists/000077500000000000000000000000001416066706400226755ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/debian/dists/chromodoris/000077500000000000000000000000001416066706400252255ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/debian/dists/chromodoris/main/000077500000000000000000000000001416066706400261515ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/debian/dists/chromodoris/main/Contents-amd64.gz000066400000000000000000000216561416066706400312330ustar00rootroot00000000000000jVContents-amd64[6Way]U]$;EY$ CI6%Iʗsx/DRT\<C2RF 72!8+x))uJHR\BȓTJnpIWɡ*ƊU6'K.mz<J%h*d˅,*V6@VHy` $9QDT&NN Z&8 f:@GHJ~[suĖ :mr]ٱ;[׺& n Jr|YqGw B 2sK0cRÂ/?׿|GbЦ>p|`/b-KgK+%7T Cׁk%F!WNɞ0HbIrMÍB"Ҷ>@ eИDzM9/U}baśWU5gr@{nD{HW5>T7h;%0(g U69w;Z;4pR5H֟I@vF=B4!NW_y+\NQ5?*zoKֺSWpLo?|>=,$I-j C  7D-J(ȡN~sBTPF9<8a%ud5 {Ubj2I2=H,czD#7€ 0k6\ -b[z;)qfq@Hk.@A 1SP%~߄'٥%yB`ApśJ #h4䀤A{żu3S䱖2ƉbG'DT˜CQX ^ɥBU#({ۖ4 ᶀ !9XEliʹ*erI ZSO3s5-frސ㶄6߲6X]%g1:! ܥuאqdHQv#pٱhF'i1<:LidEev!H@ RtqIZQPlF01,Y (4-u%ẟ5:$IM *bTnp<\3󅷢,Sf9vV4miG̭WhtIDq4e(rTFL$"\4YI#\/H'rX>Ś% ^mHq|װc&w1Ck V,8~,G9x7U83αhM'{`j[~ :{d8R`ˢ¨{Yo$/W 8+F v!K\nډZRt4RW2 ,'D Nc%AzLοW Q1U*jWh\uqz ƫPy;{)O܏X]dy%6Nc@m+Ӧ7 93CZ2dk\GuZZ \&4GUh.gzt)([ԩ:"3P0|Sae4J"\ j-t5}N8iB~ XN2hTeM2fNC|I[W<$bDm'ŎIG$%:=C0I{~F+|Khp\  Gv{<~xYaBsz{(z.Îֱˮv~ ҄wvN0TQi:AK-j3ͻ<;43v7)pY`uT;ֽEk1UzHWߍ0S1gz/{[dg0rF-Ym$居1cR&dvxTٚYGn3 ~In]M"~i`Q*3L䤂~V"ľJC\`-}Φj2'?,ӪMpp_41x}p@'w P=;,O Trq qI$lʶQS <D`zu,̨>?R 0CUƙY*h-:5~]krͪ 68Ǎ"b5:;j R靨n/Lic4xOU-h$l uD;rJ99Jqc(v-i|(BHAG"PʌV2@kRUהn{}t RCD Z[ {7sfv_Z ESp*.3Zq@4,ؘ21(Vg#p8x+^x>}ﲹ j0jY]uAn tVvP\>a Bk6o)'׵nV@b9Xu/q >b5@)aGZoV3vr75hjQJ>Lh]L/>+/bXg`z:FK`1d[V7vUk"/fR}yz|1pd2}6a?"ǧ?ha&!o?n> G\n>^ne!GCn%ҦmgbzzmҖ2ISŦogB ^%!>% Y9bmy8cO_}L.)r4r@D A#i!e֘ҶG"%,k#' A9BN̏3O/m]~.M!~Ҏ=*HZ|OBɏX'g!;^he]W Șf4=#EG_"׫]iI`hWs aA* :`u*׿N?Fy+Q:zgxN_w H"}kOe?2eG90 PO8@OodkpM쳣6yCe^z~tqV! Ț`S֌.u1F_+6O[}xD@@J,.uBhr$lx-J\P.;Y }i#kuKJȰ7WƂ$8d"H=U)$HF}2>ŞVG@f@ۦ%QX/( t_c Aq uzy(Č؜8oWvVҝ:0EV>SF!u~R\P;>P~m[TjJSH7ToQ]D2C>'RRLA(V43A6m<0ʔ2}qyToPei6xl2dLRJ*N#Bt8VOET OQb MQ 2|]S_I_WB[P (q!mTdP*-LBu9{xSOOoAYZӅ-PKY4e]1Rg,ϑ -SJb(Rڋ̫iIoP<7F3%9DQDqeV>^/9^3Mj\1,4ʏPWM"=8Gj1Nu*smenEMq^aC-}HA}P>efnv\= T+ȕtby29#pW#$>JjL[I%,5/ H YM  fW01ex!m0fɻ^ƘgE& UʥD0+?vK<~%2Rry9T7>.Q⌎ xf6Ҧ1hF?#3=CF'w@?4oD'sgI" )>mk!5 'vߏ0Vc+ a`p>ˊe#.§,+4X=G(1{1߭ݓhѧz)r~;-g>v/?(:dYdqA26vIHPZR*nIA5TVs3"kX85T-;Ttq;pS;NdkOVIT,B7(V ^f_L jxMRK8A(\ؠDa>[Gմ^Rk5n ^r0A`XBknz-2 R"69k0j\49K2%5r#ruTZ|Uv{r| UDcONC"S4yHu&)௜EReMh0wfS11&o_;g='흚+I5Yf@I_[Ŕ%a]δVXY;mۨDIJ 3f:3>Q@&s9@xrJJK\P"Ki>ǚPszlŷZ]H/=REg>|__\*djɍc7wK:֞E@L-Nк?9Gy7R)ޜ(r"n09v )n uGH#[#E 7pp F:yo*h,˭g(] g7 ~7&zєr(aw%՝u 4TVtU"C̦.M) MV9+ޔMo[eraǐ e>g=夞dV<%у"C/ SQZjNsQ+'LOA05=7%a:nBRĕzp6sGgB /1R{QR%V .ġw*!f8RI|td߱@aa`0vتB8yO>[󈦠~ġDq͛;j<%iZRgq ;#ހ YI]HQgģ'"GqG4>+&3(G(Äf XKIt SAi{\;ǎm(}#zRfitZ|HM)щ rE)Aϔ3 1H|z8QoL2B䃘k &?}kc(^S.Tg=8peb Ћ^Ȍ0xNf&?Ok|'^W! -xC>}˻_>5GxJPG{2 ;u0yQe[r/Jb-*Y5GgY{{P6N!ٱcK'lT',p& OdnjKKș5eŻyajec]5S6%X5%27ɩYN1N I1KV2u>H=nŀSBzf7F.~A+955}_ r=a/"P 2D i 3{Kv6*ȚNcnCb4HeCʬ蕖oH&dH3hHA3CEzAeyBs_m8Do/Tf䣴QefO$[ne(rqzu?hzQ)idj@M_A)D{iWbm]0&fAz|* 18:U踙4[u99ڷ7m][ϕUԵDN1LKn0q1~hp123%~h'3@nbzMLp錛e? oswXg߱Zi|`Gm3a]W~B49JpJ9 άCx=A c/0kzn*YQvx\J+b }W,u[bڂƮ;M+QvZ!L&,KN5ϳId)'eΑYwVAvQd[6&Y&Z V&-iVbV$5`֠VsFYʛj8,5=Q+\Avܦ!<3:n%Q/C 4Si^g +<ϲ$R8v6CNؕYdi؍KaYC7NχFQFyZGx/f%yCdB0|1‹GdL̦^*oL uk N!eLmrdž3Y%m[^ݨSFKMrͺGSfpSmwIF9Nd7 l QO@r%ݷb;@4WOMvZ zOєw:]Ufnؾ@ޔ{k/qsappstream-generator-0.8.6/tests/samples/debian/dists/chromodoris/main/binary-amd64/000077500000000000000000000000001416066706400303465ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/debian/dists/chromodoris/main/binary-amd64/Packages.gz000066400000000000000000000122461416066706400324330ustar00rootroot00000000000000nVPackagesZێF} +43wAcX ۰ejxIk5,K_1{%{"IVݲbdwU1qD$_J}!7QYն]xX5BzYUքg[,,}v-wWVVa_nm& Z+:U'gmf5gom`?{] Z{îZ6]*ٻݮ^t7uН7mf욶԰ߛmXvF. .7o0U^bZ -4 -g 3i@eU$$p2qc%FTƂ/O+'/2@@K?/7 @ue}T4,ШaB ʶg`yX*@U^pmN||eKUYkl*f([on<<)hΰ`$V%xxCXHU-@$IS=l2f]8*1ʦL02kb[Hp42 ,NF2ܙ\G<"Uj$˕i!tµyHQ)"Wa^ȵÿ%w!(B{W w⃩˛Щr #'a?`9l:x^N7jW+ٝvlO /$$i7AUv sV|C@)aʶ+?|C*W4('U=6 tV15w4+ v3lwd^xc~5a7E)5d5áojހZ]5C#oU#+l;ٰP lGѶ-$(k]  @Y7=o[Ry ?aS:qɠ7OΞC:B$5BmKlBη7okf?K?ξ=?Me>; @]y)#4@Dk}BW@3ECW6TsfeIh\<Ϳ=16ԯ]d?CgD9J!>gOW |PeHoM}87%93#-;Ph|+Cϱڣ-b44.\&^QsYn'ԅ6 8k)fr,c)Jc@;P '5 jZPUGQhPu7Y ^Pi_ayݘ£)vmRLC%9iZyP7'v{8/y;oޏ@1\yZ߉gZRu2ˢ߯U'Kg3Y`dxL_Ƽc<؁ njDy[ueQ‚rDa0ԥ+ kJzX%k|sG|jl˴$PHaL dR7WA;SFmw7;LLhwmAiju&%@%ˁw.@$fg)>\!޽͍V!mC8%|;Tl}l%5rd;}gA< /ĖFX=Dd nZm[KEM |֔}X|zVBkгHn7c~@D4h'>k۴K铙APnƃnebCwͩd#D'Ɉ>=: <9ZB7G;NRvس%xUyQ>["~E8J&p'O}z 'xifɸXu,υq6O9NFuBdFg87RgP -H Al/mAqΤFsĖ)NKkL$cgҡ{~}c|"e; l^0<:dHA%1yes\Ȫ\ίY: їdQ32cLƀЂ bSc י*vq "Lf\0P' 8 5x-y*o^80ʔ.2iKˣYQwQmvжmI}l-߿=%>,E8 s>\$ք;ͻvzE̹CÉLZٕ~Ua^Lz>ƼVoݦ@ʺa$Y[)K fd&U.֦]-"_(gwj6ޕ>3E]S>8[w\h, ;8 qɠj?_Ox;oJKY,"Y* eApTi "#9Q$P̹cG0v:Ӈn0c +GZ8$BU|5/<a?K{&1fQ3#٬<FI*KgbGej`.mٯ qۍnhD1ʫ}hV2M+4X/~lJ}~|,}v+ahw`jX|3hwl=b\p'%Z(-j*`aJ6(7ApMAG(B谔4 ;D9| ]0(JV!F HSd¯oR6~s2| SďT+NIhrh*LZ~ʼnr)]Z@51[C M;c K6*)ꆸ H[`Z赁F!/7appstream-generator-0.8.6/tests/samples/debian/dists/sid/000077500000000000000000000000001416066706400234545ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/debian/dists/sid/InRelease000066400000000000000000006306541416066706400252640ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 Origin: Debian Label: Debian Suite: unstable Codename: sid Changelogs: http://metadata.ftp-master.debian.org/changelogs/@CHANGEPATH@_changelog Date: Thu, 28 Jul 2016 09:29:12 UTC Valid-Until: Thu, 04 Aug 2016 09:29:12 UTC Acquire-By-Hash: yes Architectures: amd64 arm64 armel armhf hurd-i386 i386 kfreebsd-amd64 kfreebsd-i386 mips mips64el mipsel powerpc ppc64el s390x Components: main contrib non-free Description: Debian x.y Unstable - Not Released MD5Sum: 76936f00190cab4ae8001838366675c6 1318264 contrib/Contents-amd64 bfef3ff1a24f2458f4aa0daa8db8809a 23860 contrib/Contents-amd64.diff/Index 9bbc070747a7fbb835e1bbd4b28c1efb 101470 contrib/Contents-amd64.gz 45789bdfeebc45e9d3f2393b0457080e 1175578 contrib/Contents-arm64 59ef2e526db59981b2f697376317c89c 18448 contrib/Contents-arm64.diff/Index b585710cb7b529f99babeb018a751995 87797 contrib/Contents-arm64.gz 9616ad3465885d02f89ce99b09c18bad 1177832 contrib/Contents-armel f42265fa148e7beb1f36d8a95185109d 19432 contrib/Contents-armel.diff/Index a74899c8f4800f519ba25c6b5ec9f1a5 88158 contrib/Contents-armel.gz 6e95aa32591033b7a46851f5762215f1 1164602 contrib/Contents-armhf 68f315bf0c2fb1b8f0ea53c92b142d48 19432 contrib/Contents-armhf.diff/Index eb0f7db757ce657c2edc96b9ee8a9d34 87806 contrib/Contents-armhf.gz 38cab633587a72acf54abd8411ef10c0 1103107 contrib/Contents-hurd-i386 9f78d5d835696aca78d83f7516e7bd9f 18940 contrib/Contents-hurd-i386.diff/Index f1362e02cb534fcc7aabc4bce3cb7b0f 80883 contrib/Contents-hurd-i386.gz 12a23604a09cab75c561dc3038349b27 1290908 contrib/Contents-i386 ae9a0202d7b4ef970e9d91156186430b 23368 contrib/Contents-i386.diff/Index 0af8716ba23577cf2bbe7037d8085c28 99126 contrib/Contents-i386.gz 61898d041d0bcb5ea76278ae8ea9b144 1110069 contrib/Contents-kfreebsd-amd64 4907d62cb00264448fd093b647935563 17956 contrib/Contents-kfreebsd-amd64.diff/Index 8aadeee8fa90ac0a634181983f15484f 81795 contrib/Contents-kfreebsd-amd64.gz cac4d7a16d185e4605c8b3af2956ed1d 1109182 contrib/Contents-kfreebsd-i386 e757b31a95be3c13da48be30b24fc861 17464 contrib/Contents-kfreebsd-i386.diff/Index fc3f02dd8748ba208c0178efaa75b1b8 81545 contrib/Contents-kfreebsd-i386.gz a1a09e6b574814d24738516662ebfd28 1159398 contrib/Contents-mips 0936793b8f08ebc300238341e49654c8 19924 contrib/Contents-mips.diff/Index 4b180f2c69ef52d31f541234c7219b2f 86916 contrib/Contents-mips.gz 4d320fb7d8d6c187cc96350df207c435 1151939 contrib/Contents-mips64el 5bc2d41d3ce5ee999616f51f7b5d6346 18940 contrib/Contents-mips64el.diff/Index 8e243ddd024a70102ef0824877d06c93 86243 contrib/Contents-mips64el.gz 99d29dc09a61774dad60f7d69ae98eff 1159421 contrib/Contents-mipsel 8b404ccb477256c402724384a0efd039 19924 contrib/Contents-mipsel.diff/Index a91548dd6caf580f18232f971ffbf021 86916 contrib/Contents-mipsel.gz 4dccbf22e95bb24f37b2467678c11ffb 1180561 contrib/Contents-powerpc ca38e407233736ff497ae81f5409ef5b 18940 contrib/Contents-powerpc.diff/Index 6fb207e6264260bfaad79316a683fe76 88488 contrib/Contents-powerpc.gz f88f3a5246a95ab161511ccdbbffb514 1142562 contrib/Contents-ppc64el b3b4f516282cd659e37a01dd45eb3ec2 18940 contrib/Contents-ppc64el.diff/Index b1114d1d622a1394424d5c80a8f31948 85835 contrib/Contents-ppc64el.gz ac54b385917a98856179ed8dfb914dbc 1155432 contrib/Contents-s390x ad6daa027695744e5429b26045b4af80 19432 contrib/Contents-s390x.diff/Index 71d6f26fcd2323d63c0a84e39eb41dbb 86413 contrib/Contents-s390x.gz 5f21f15aaca421a1beb2727ca531bacd 3519818 contrib/Contents-source 18b82875a5dc6fd42142dcafaf44e798 24352 contrib/Contents-source.diff/Index 95bd86b0649fe75730b7a7c90e09c330 389700 contrib/Contents-source.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-amd64 b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-amd64.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-arm64 b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-arm64.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-armel b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-armel.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-armhf b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-armhf.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-hurd-i386 b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-hurd-i386.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-i386 b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-i386.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-kfreebsd-amd64 b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-kfreebsd-amd64.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-kfreebsd-i386 b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-kfreebsd-i386.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-mips b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-mips.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-mips64el b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-mips64el.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-mipsel b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-mipsel.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-powerpc b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-powerpc.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-ppc64el b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-ppc64el.gz 68292125276158d468c91a58be0f30b1 451 contrib/Contents-udeb-s390x b49002146fad4eb7e4dce23bc3f48e38 271 contrib/Contents-udeb-s390x.gz db07a500b8e2d51a1c6165f7c7d49263 90903 contrib/binary-all/Packages 035ea549e0c204722715991e2c48fd3f 26745 contrib/binary-all/Packages.gz b47817473f85f3dc1166283d6dbb9970 23788 contrib/binary-all/Packages.xz e00258bbbf38227aadbea5bf9f08af11 105 contrib/binary-all/Release cc6ec5981c3a16e2b5f2d5d1c10b83ca 224553 contrib/binary-amd64/Packages cf47005475a2d07a2df1f7e2efad5424 27796 contrib/binary-amd64/Packages.diff/Index 7464e2b4bc1ddd4e6ba5ca25c3bfc987 62636 contrib/binary-amd64/Packages.gz 079d8fff6e0703ed548c1ca2bc772641 52468 contrib/binary-amd64/Packages.xz 465133a3a391b0f6285452fbcc621120 107 contrib/binary-amd64/Release ccee7273456185be5c0f53645145cb5a 173735 contrib/binary-arm64/Packages 69a6ad82fab38b1703abcf309aa7b1b5 27796 contrib/binary-arm64/Packages.diff/Index b4320a9705f7b81ed37cf39813d66091 48846 contrib/binary-arm64/Packages.gz 6e5e7f45a525e9a727c48e7aa049340a 41604 contrib/binary-arm64/Packages.xz 70e72d09cd1216faff1786b186f7b629 107 contrib/binary-arm64/Release e0c8be877cd53deb2ee92c8aff46f475 175162 contrib/binary-armel/Packages 13dda2b297a266f2c19b98abd92dff1d 27796 contrib/binary-armel/Packages.diff/Index 769ebb9f9e23f8cb5238d2b1eb895d17 49399 contrib/binary-armel/Packages.gz 472f6b9e57038c3f4f30f93406c6945b 42060 contrib/binary-armel/Packages.xz d7da68ee268629ac14567afdb9c16075 107 contrib/binary-armel/Release ad63a40a45ff953ec3f62620366b906e 181954 contrib/binary-armhf/Packages ba028858b08e6027e0fabd2e60f90684 27796 contrib/binary-armhf/Packages.diff/Index d0092f2594449140e4f010b088949f16 51101 contrib/binary-armhf/Packages.gz cb8631172db58f6e7f938c3ecf28008b 43400 contrib/binary-armhf/Packages.xz a2cb49824f6ea7e5fc6c3398d93f6c90 107 contrib/binary-armhf/Release 181d8cd4fe1abf6a0348d9cbd600efd2 147251 contrib/binary-hurd-i386/Packages fac098136e0c11301cfe90c102e052e1 27796 contrib/binary-hurd-i386/Packages.diff/Index 1a1e4a66c5ae37926fdd73af40cccac5 42491 contrib/binary-hurd-i386/Packages.gz bd6bb3766b9fbd05b8b9c043ece636a0 36440 contrib/binary-hurd-i386/Packages.xz 5de9e1d5c34c6429e3280c611901acad 111 contrib/binary-hurd-i386/Release 2ebdb99ac445fd1e9772ed59b0c3a4cb 214181 contrib/binary-i386/Packages ce79e3f92870afca25f1df71e4cb8c43 27796 contrib/binary-i386/Packages.diff/Index 5bab9a27ffa70a483e211281259f8b55 59731 contrib/binary-i386/Packages.gz 312215b0ba43ed704660cf713f76181c 50404 contrib/binary-i386/Packages.xz 78512545310e20e1a5269eeec8964b85 106 contrib/binary-i386/Release 1e54f91f258b788190f48119b88c40e2 156547 contrib/binary-kfreebsd-amd64/Packages 5058edabd42c8d7208ddd7f177a06b9b 27796 contrib/binary-kfreebsd-amd64/Packages.diff/Index 23e787dd54c05786365f596682a08405 44496 contrib/binary-kfreebsd-amd64/Packages.gz ea98bda487f9dab96c3807d925bfb143 38132 contrib/binary-kfreebsd-amd64/Packages.xz e50c34bd5f47b30a4bcb86bef7975614 116 contrib/binary-kfreebsd-amd64/Release 9778c40897b37b57ba2924f3e99e8b8a 156427 contrib/binary-kfreebsd-i386/Packages b910c20a6f70cd62fc36b362f30b03c4 27796 contrib/binary-kfreebsd-i386/Packages.diff/Index 8e0cb14dc33a1b37009eb0ff7c9cd5d1 44488 contrib/binary-kfreebsd-i386/Packages.gz 17f02a26c38946428018c8c672c7bbc0 38184 contrib/binary-kfreebsd-i386/Packages.xz 1ca18bf7c94cd46cdaf297567a59bc9e 115 contrib/binary-kfreebsd-i386/Release 0d78956fc1c55b256dfa21c1770f2a9e 173496 contrib/binary-mips/Packages eecc813f58f3b8980415e1483c3a8b87 27796 contrib/binary-mips/Packages.diff/Index a59e89c627cf65d148f2b865c3d31916 48888 contrib/binary-mips/Packages.gz 9b9a41523ca7d7fe2711df2526b43c40 41788 contrib/binary-mips/Packages.xz 96afd38f08284a16cb242eee20402fd1 106 contrib/binary-mips/Release c2023c4785237ff0569511b1be28ac29 167584 contrib/binary-mips64el/Packages c998e06384ea1f1d7c5dc0b9dbb23b94 27796 contrib/binary-mips64el/Packages.diff/Index 1d623dfe80c49e48557457d1978c5aa0 47081 contrib/binary-mips64el/Packages.gz d02dc52e7e1c93be4368546a501dac56 40424 contrib/binary-mips64el/Packages.xz 3b13c342bd7a0607c43a230cc7138790 110 contrib/binary-mips64el/Release 996788368459cd1955bfab1aaacb1f87 173896 contrib/binary-mipsel/Packages 6c763cd323977a5957a4ef90c2ff95de 27796 contrib/binary-mipsel/Packages.diff/Index e7c7f731589e0e4a0a8a5dd612bf80de 49030 contrib/binary-mipsel/Packages.gz ee073d5641887bf4a17420f1674dcee0 41792 contrib/binary-mipsel/Packages.xz 74cf4bae45fc7682a08d6d8f1e6dd760 108 contrib/binary-mipsel/Release 0407fc65942986fc957b1633a28e3768 177845 contrib/binary-powerpc/Packages d2de0ec9f243ed2155323f2e73e7ed62 27796 contrib/binary-powerpc/Packages.diff/Index b5423c69151e767d765fc3e43bf77581 50212 contrib/binary-powerpc/Packages.gz 7d06e3a3ac87ae984bae3a31253e80a3 42644 contrib/binary-powerpc/Packages.xz 14c6768d1b56622a250e90692b59a46d 109 contrib/binary-powerpc/Release 6e8256f750f2c308408e3a0f841de602 172740 contrib/binary-ppc64el/Packages 42e8c4480c8bcb0fddda6e5968b20048 27796 contrib/binary-ppc64el/Packages.diff/Index 39700d0783456519411c94038ab716a6 48549 contrib/binary-ppc64el/Packages.gz 3ee7fd2fb2bfcf64d2d5d90fc14bf9f1 41448 contrib/binary-ppc64el/Packages.xz b90b505d19146c0e066863ea36412c80 109 contrib/binary-ppc64el/Release 0598d44f3e15f67e367aef8789931f9b 170484 contrib/binary-s390x/Packages 2040deb0094389f1179e1477e30727c6 27796 contrib/binary-s390x/Packages.diff/Index 9fda9aad132aacc8049a29d4a56d2153 48250 contrib/binary-s390x/Packages.gz bbed3a3f3ca3e5e7fe1b4a7692bd2a50 41044 contrib/binary-s390x/Packages.xz 3755c075b383fc15a93ab8b29ce641fe 107 contrib/binary-s390x/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-all/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-all/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-all/Packages.xz e00258bbbf38227aadbea5bf9f08af11 105 contrib/debian-installer/binary-all/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-amd64/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-amd64/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-amd64/Packages.xz 465133a3a391b0f6285452fbcc621120 107 contrib/debian-installer/binary-amd64/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-arm64/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-arm64/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-arm64/Packages.xz 70e72d09cd1216faff1786b186f7b629 107 contrib/debian-installer/binary-arm64/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-armel/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-armel/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-armel/Packages.xz d7da68ee268629ac14567afdb9c16075 107 contrib/debian-installer/binary-armel/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-armhf/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-armhf/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-armhf/Packages.xz a2cb49824f6ea7e5fc6c3398d93f6c90 107 contrib/debian-installer/binary-armhf/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-hurd-i386/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-hurd-i386/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-hurd-i386/Packages.xz 5de9e1d5c34c6429e3280c611901acad 111 contrib/debian-installer/binary-hurd-i386/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-i386/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-i386/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-i386/Packages.xz 78512545310e20e1a5269eeec8964b85 106 contrib/debian-installer/binary-i386/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-kfreebsd-amd64/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-kfreebsd-amd64/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-kfreebsd-amd64/Packages.xz e50c34bd5f47b30a4bcb86bef7975614 116 contrib/debian-installer/binary-kfreebsd-amd64/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-kfreebsd-i386/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-kfreebsd-i386/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-kfreebsd-i386/Packages.xz 1ca18bf7c94cd46cdaf297567a59bc9e 115 contrib/debian-installer/binary-kfreebsd-i386/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-mips/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-mips/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-mips/Packages.xz 96afd38f08284a16cb242eee20402fd1 106 contrib/debian-installer/binary-mips/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-mips64el/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-mips64el/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-mips64el/Packages.xz 3b13c342bd7a0607c43a230cc7138790 110 contrib/debian-installer/binary-mips64el/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-mipsel/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-mipsel/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-mipsel/Packages.xz 74cf4bae45fc7682a08d6d8f1e6dd760 108 contrib/debian-installer/binary-mipsel/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-powerpc/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-powerpc/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-powerpc/Packages.xz 14c6768d1b56622a250e90692b59a46d 109 contrib/debian-installer/binary-powerpc/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-ppc64el/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-ppc64el/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-ppc64el/Packages.xz b90b505d19146c0e066863ea36412c80 109 contrib/debian-installer/binary-ppc64el/Release d41d8cd98f00b204e9800998ecf8427e 0 contrib/debian-installer/binary-s390x/Packages 4a4dd3598707603b3f76a2378a4504aa 20 contrib/debian-installer/binary-s390x/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 contrib/debian-installer/binary-s390x/Packages.xz 3755c075b383fc15a93ab8b29ce641fe 107 contrib/debian-installer/binary-s390x/Release ce92e6dfe91c9390868e0f3e4cf6df8f 37465 contrib/dep11/Components-amd64.yml d47781428f68e71f3320856df3252808 9645 contrib/dep11/Components-amd64.yml.gz 114dbfdc20818ddbb3b17f60ed129161 9052 contrib/dep11/Components-amd64.yml.xz 5fffee3d51fc8b194b027c25c191d163 33085 contrib/dep11/Components-arm64.yml 7156b72fec41d12624350bca57e5fc57 8364 contrib/dep11/Components-arm64.yml.gz 4651beb52d002f94d1871cd2d3614368 7872 contrib/dep11/Components-arm64.yml.xz 0afdc6e63cc65122c28f499028c4a4a9 33085 contrib/dep11/Components-armel.yml 7990e09e3e99d3082030ee26e430ccef 8255 contrib/dep11/Components-armel.yml.gz f833ee70cac40f2a65e99ed857963680 7832 contrib/dep11/Components-armel.yml.xz 464c4a3e1ba350097d8680df4a734435 33085 contrib/dep11/Components-armhf.yml d9211402d4c2964a4fbcc9b2fe652b8c 8344 contrib/dep11/Components-armhf.yml.gz 1db57318262e97a48dba53e17c9c0f8c 7860 contrib/dep11/Components-armhf.yml.xz e3ff4f7307186538b127f37345b07447 37465 contrib/dep11/Components-i386.yml 4a7c9a72f94c91cf233a6c91767e532f 9620 contrib/dep11/Components-i386.yml.gz f50339d2be9d743aac18cf0a05a5e11b 9052 contrib/dep11/Components-i386.yml.xz ad46de43c30f96bb42cd5533e43d173c 31558 contrib/dep11/Components-kfreebsd-amd64.yml d8d82ed4b793161ec52db891285a9a3d 7777 contrib/dep11/Components-kfreebsd-amd64.yml.gz 75dee44d1dcf47a17f1829bc3c795c55 7328 contrib/dep11/Components-kfreebsd-amd64.yml.xz eabf301f181ae921726c9920248d1e7d 33085 contrib/dep11/Components-powerpc.yml 16657094afc59692c45be4125329ec61 8314 contrib/dep11/Components-powerpc.yml.gz aeb166ef90550957bec79ca35c253688 7864 contrib/dep11/Components-powerpc.yml.xz 9bb126b19f2888542a1ae114b7c2a4af 372224 contrib/dep11/icons-128x128.tar 94860c8d9551a0b04c101126043bf69b 304821 contrib/dep11/icons-128x128.tar.gz aa64c0a5409688d71fa34973339bfe3d 160768 contrib/dep11/icons-64x64.tar 20fc54d701f75bdac50deeb8801ad200 120717 contrib/dep11/icons-64x64.tar.gz e0f526a83297781edfcdc5204f1bf866 180882 contrib/i18n/Translation-en 71128f827188b4bb28cbd9847f854650 47717 contrib/i18n/Translation-en.bz2 30db714c4408541408d20dcbdeaccd3f 12544 contrib/i18n/Translation-en.diff/Index 96a1ec18a257b9c5da342e87336af24d 108 contrib/source/Release 360cfdcde53f0ffd9fbef781fc813e71 189731 contrib/source/Sources e31b4abf0a8727194f8f8b485fcb0e9d 27796 contrib/source/Sources.diff/Index 5680a4b1ace341717f497b26d49c0dc2 56034 contrib/source/Sources.gz 18441936c32701b1f4c82fd4fdc8b683 47676 contrib/source/Sources.xz ee316089d76ec1b3157e7e602205706a 486486404 main/Contents-amd64 8736f7d7d3c00865bb699339da3378ac 28024 main/Contents-amd64.diff/Index 1a77b48abd1d53423d2b7d0ae206cde6 33741709 main/Contents-amd64.gz 38db2b28d8fa75e08e4be36d9f34a790 464406406 main/Contents-arm64 d65b379a3c964c199434eb746f3d4ac7 28024 main/Contents-arm64.diff/Index e4ca2a7f4ce99ee7e0ff0c7fe6120c04 32427484 main/Contents-arm64.gz 4a6e2f3be652de4b53fee4ae8fc65cf0 462714353 main/Contents-armel 6d9a1550e947646f09155a9a742cc87f 28024 main/Contents-armel.diff/Index 733848b5a7d4bc790ac4049524a7e93f 32372160 main/Contents-armel.gz 55fbd13d082def8c31a7ebc389e86325 465331124 main/Contents-armhf 774925c7655d72dee4a8cfd62e64b9be 28024 main/Contents-armhf.diff/Index ae9463dba41ea69ccb5367b7eed8d07e 32551542 main/Contents-armhf.gz 6aa25806e19961331001b5ff64b9a014 437011535 main/Contents-hurd-i386 a5be6b90e1eef3659752df9978815a32 28024 main/Contents-hurd-i386.diff/Index 857f0045f8eed012e88c337911fea1c0 30282285 main/Contents-hurd-i386.gz 30364de05bf12a8449cced18c78f2bf7 484515088 main/Contents-i386 3c20ff9867dc54ff8311ae90d2767974 28024 main/Contents-i386.diff/Index 241077710e7229a4f9c43ad795723bfc 33649417 main/Contents-i386.gz 3631d88d325e24fba0a1e9a66e98d3dd 449136892 main/Contents-kfreebsd-amd64 b099938d9f72059efb46676206d19be1 28024 main/Contents-kfreebsd-amd64.diff/Index 607925cb1665ad6e0b98773683d54a36 31341686 main/Contents-kfreebsd-amd64.gz 999ec3b31309d9483b551be3954d6a62 448627460 main/Contents-kfreebsd-i386 3a78f1e5220f291c6817431f9bbeb07c 28024 main/Contents-kfreebsd-i386.diff/Index ca08c72ab78447398cc48592cd8676e8 31305004 main/Contents-kfreebsd-i386.gz 30e37a196f4cee83ed4383411260bfac 460036580 main/Contents-mips 0707bb5adde70601f195ab49bea34deb 28024 main/Contents-mips.diff/Index 935c7cdc3d205ecb98c83b5597f5dca2 32236221 main/Contents-mips.gz 6f55fd9c61da45fd1e8cfbaa299bbf37 459438445 main/Contents-mips64el 1a87ca3ab23d02394c31d57418241818 28024 main/Contents-mips64el.diff/Index 7c6e9beae58cb39db8333a380fd4dd91 32030589 main/Contents-mips64el.gz 44b1854e38e2ed5f1d4b5eea129e4236 465041084 main/Contents-mipsel 93900fb47da7b47c5331a7d9b245bfa2 28024 main/Contents-mipsel.diff/Index e8f93fa8b1d6aaf7b8517c3fb0bdd704 32541643 main/Contents-mipsel.gz 9353556da7ca10327824835fb4e1e5ed 465654055 main/Contents-powerpc de1614badf54805be0c72cf33d0a027d 28024 main/Contents-powerpc.diff/Index e38a7263ce5cb08e96b2bb948fe26f2f 32538984 main/Contents-powerpc.gz e7a2f63c83949133414374616601c1b8 462882892 main/Contents-ppc64el aa12e85431c9315c4468fd333b2b0a1d 28024 main/Contents-ppc64el.diff/Index 50da71c6c8a0ed04f94ea744a3fdd2ae 32352649 main/Contents-ppc64el.gz 09078c37a3aa277346d09df4ded07d2f 459302164 main/Contents-s390x 2d7987fd743413a9e0d42762b186faee 28024 main/Contents-s390x.diff/Index 5d007fb854cad448b0dc980ec96e72b8 32174741 main/Contents-s390x.gz 3f9f3f180230bcb30a731f2fc06b686e 482453699 main/Contents-source 766aeb64fbed4ff551403313689f2b57 28024 main/Contents-source.diff/Index a75c9bc97dfa4fe9434ee525d24251d3 53745770 main/Contents-source.gz 928e37ebff0cfa9ce1618985c5f37dc7 484751 main/Contents-udeb-amd64 a5a9820e853661e0fe8b9aac0cab5958 38535 main/Contents-udeb-amd64.gz 04c87ab2627b88fc32088f66809ad659 419372 main/Contents-udeb-arm64 a6525b449bde4dfcbad07f351eb5f9b3 34115 main/Contents-udeb-arm64.gz a911b6fba961d936169e7ed99a184c59 414623 main/Contents-udeb-armel feb351d9b874d290d516fe4efbb2c539 32491 main/Contents-udeb-armel.gz 757dc43eb85187560e2cbd417fff1786 504043 main/Contents-udeb-armhf d1286fae424148204ce9c8bbb2069120 39965 main/Contents-udeb-armhf.gz 9ab52d365a5f55c411b45f0b6771977b 266208 main/Contents-udeb-hurd-i386 ed815b721e699b73a7a4285820d2f04a 22517 main/Contents-udeb-hurd-i386.gz 953d938f13d70a717cada03b45f2c0d1 710802 main/Contents-udeb-i386 42540645d271e7b3636d0a8f0a76c775 54082 main/Contents-udeb-i386.gz 3219c6cca0fdf4ecef1dbf2767a6acda 289951 main/Contents-udeb-kfreebsd-amd64 76839f660ea6e3850c43b10457e40842 24182 main/Contents-udeb-kfreebsd-amd64.gz c0bc594c1b8d7633eaab1a45b0e7966d 289869 main/Contents-udeb-kfreebsd-i386 f0819488c505e6ff36622bed54c91e1f 24244 main/Contents-udeb-kfreebsd-i386.gz 143a17bbfd91d49c510d64c74917a2e8 577017 main/Contents-udeb-mips 2bb0fd7e0ea134b96b9031c790892353 44213 main/Contents-udeb-mips.gz 9cd61e3907666da87ebde517eb37869e 723583 main/Contents-udeb-mips64el f55c7401a1680861fb8b55a189dd76a9 53323 main/Contents-udeb-mips64el.gz 31ecad337aa42bf23b8a7c198656b32a 1008989 main/Contents-udeb-mipsel 4c47c966560843331329545a4a1505ee 71028 main/Contents-udeb-mipsel.gz a259b0594e077f1ce85e735a8a31532e 583828 main/Contents-udeb-powerpc c00518e1f8b131c0000fc64934b7bc48 43594 main/Contents-udeb-powerpc.gz 9605e715d0da43c2703d028833b1a7c0 396155 main/Contents-udeb-ppc64el 03c887f7e6af3ebd8f8dae91b477b055 31809 main/Contents-udeb-ppc64el.gz e77d64695018da365373e7a7b44134c8 307502 main/Contents-udeb-s390x 327c1a251237ba0ca23db736f54528f6 26131 main/Contents-udeb-s390x.gz 8ade86bbd39832fc327cb974cea3a596 17781690 main/binary-all/Packages 83b43a550aebbf511d73f1efd74e4296 4391664 main/binary-all/Packages.gz ccd2fe9f6c28333f59a81a16ea5330dd 3337812 main/binary-all/Packages.xz 10085e8cf6c39e33baf83b03965074f2 102 main/binary-all/Release 9d15c1d6773cd2a24e580d556a636c8c 39256459 main/binary-amd64/Packages 380e3c0605f49bd1937e96f6e6171fc4 27910 main/binary-amd64/Packages.diff/Index 3bf28cd4699902485fb817afca2521ff 9576288 main/binary-amd64/Packages.gz e65fb137990e2ee6449910bc122bc1d7 7147528 main/binary-amd64/Packages.xz e3a090a211110a1a0fdffc2cc7a1c915 104 main/binary-amd64/Release 8fe0e406851d474ceaf3369468bd41b0 37571520 main/binary-arm64/Packages 60efb4ecdc4242c296ed5889f36706fa 27910 main/binary-arm64/Packages.diff/Index c908fe0fb189cd2de4308fd22a3cc349 9227318 main/binary-arm64/Packages.gz 019e1c8630c4b2174921061629aa0579 6884520 main/binary-arm64/Packages.xz 806d63700dac5cea4d29e6a86e58597d 104 main/binary-arm64/Release 4b36b97c3b0e47a788af02d0d2c7b4d0 37699046 main/binary-armel/Packages dbf57f1b126980bbbd7aa4634f0f164b 27910 main/binary-armel/Packages.diff/Index 2c60d28349f8c6c687fc37287b3d0a0e 9275300 main/binary-armel/Packages.gz ab9ddab168b634af1462f1bb0f5a353b 6921736 main/binary-armel/Packages.xz 5c1866d5f05996c75c2150ae69ad9815 104 main/binary-armel/Release 24762c704bc847754986b33caad4a738 37788052 main/binary-armhf/Packages 55af3c509d618db8d9f61e527d0afdf4 27910 main/binary-armhf/Packages.diff/Index 51ada163986a84e78bebd0b03b558024 9300334 main/binary-armhf/Packages.gz 1299022e3a2b7f332e6b65d20a05f3a0 6934520 main/binary-armhf/Packages.xz 87de7b798ba50f1e06ba3ee7a1db41b4 104 main/binary-armhf/Release 19fc25a37c5921182d993cdf0780b760 33806130 main/binary-hurd-i386/Packages 556b6b5ec18daf59f55191ab6c3b9ef6 27910 main/binary-hurd-i386/Packages.diff/Index d4590d9af46bd2435fedd57ef85c4335 8234995 main/binary-hurd-i386/Packages.gz dca28a9d3407408ca288e32a5adec10b 6163664 main/binary-hurd-i386/Packages.xz a84d93ba0ec25a5d1986ea4fcd76d92a 108 main/binary-hurd-i386/Release c06fa33ebe82447a723cef2af9d11a8e 39044462 main/binary-i386/Packages 3bc70aa8440f0a1caf914d5ac142123f 27910 main/binary-i386/Packages.diff/Index 81501e7c090fe73023f9351b1e02ce85 9548697 main/binary-i386/Packages.gz 910d74a441a289bab8de245237aca84f 7122292 main/binary-i386/Packages.xz cf30be831f54859034d2e11501806247 103 main/binary-i386/Release 47f030c0fa64704d93015f8b6b144bb8 35913320 main/binary-kfreebsd-amd64/Packages 9659e819847108e0e39f9e5e23d6b1d3 27910 main/binary-kfreebsd-amd64/Packages.diff/Index 5ec898199fe93755d1fb64c86e224935 8718641 main/binary-kfreebsd-amd64/Packages.gz 44f38681311cea43878ab21aa70496b8 6512728 main/binary-kfreebsd-amd64/Packages.xz 05d24fbef387f91e082ab5dc7c46b9ee 113 main/binary-kfreebsd-amd64/Release 0e46ca35edca54c68c6fc6fa40cfffc8 35809649 main/binary-kfreebsd-i386/Packages 1cf37e1a15bf701fb58276911deb792e 27910 main/binary-kfreebsd-i386/Packages.diff/Index 83faa3a7b131530d4c1f135facae2bda 8704036 main/binary-kfreebsd-i386/Packages.gz 6789374d0ef709221e9683719bcf21a7 6502680 main/binary-kfreebsd-i386/Packages.xz 262a86cd91b0d0d5fb921a317d76df0e 112 main/binary-kfreebsd-i386/Release 229a406feded170996ebefb13ec08cd3 37366754 main/binary-mips/Packages 53f100c30462cdb18aa9f097c0394d42 27910 main/binary-mips/Packages.diff/Index 3a348375ed9851ae75692a436641ea10 9210212 main/binary-mips/Packages.gz 1c1bc0b691df9cfd5790d2055f648c68 6873512 main/binary-mips/Packages.xz 9d9f639b9f52548b2eb8392e691a5c5e 103 main/binary-mips/Release 5b3a92396132930feb2c5d1afbccac67 37340330 main/binary-mips64el/Packages bf4b6c4f633568da3717a24c9163ef7b 27910 main/binary-mips64el/Packages.diff/Index 67cd956d14111c950b0b0171aaee3a96 9156063 main/binary-mips64el/Packages.gz fc0ee60864c7483d6a8d98a81e2047ad 6834608 main/binary-mips64el/Packages.xz 8246be42dc917113cba664e98bd50d3a 107 main/binary-mips64el/Release 5e364fe4381b0d18d9fa3407a13607c2 37668023 main/binary-mipsel/Packages 7e63b52935765be1f4f8f41bfff932d1 27910 main/binary-mipsel/Packages.diff/Index 93887e92bcede6b341b1f22762b4630b 9262851 main/binary-mipsel/Packages.gz ef3c44197a1b9a1a7ff49d5dbbe52bc7 6912400 main/binary-mipsel/Packages.xz 3dc8b9d46dd15d877218c7c13d0e2884 105 main/binary-mipsel/Release 91ad2fa7d560ad9a114562a6f66d9a00 37823973 main/binary-powerpc/Packages 97fcc2b38b44cd0f1a6f1055adc8b8b6 27910 main/binary-powerpc/Packages.diff/Index 8aa5805ab669d80b7174f37beab2061b 9292207 main/binary-powerpc/Packages.gz 99602114bee93a0f56d25dc4f8add0a2 6929276 main/binary-powerpc/Packages.xz 066a5dd8d4763961ca7e72ab118e91d4 106 main/binary-powerpc/Release dbcd9dca16846ab516d809d1fe9c6d3d 37859587 main/binary-ppc64el/Packages 07322d6518dd19ac2e177e1bba7aa39f 27910 main/binary-ppc64el/Packages.diff/Index 1ab9453951f0af4d9c4f19bb6a0b7ccc 9277244 main/binary-ppc64el/Packages.gz 03d1d9b800de33125a75f75e59ecde60 6921148 main/binary-ppc64el/Packages.xz cc8afb9a3d24cea535c031ff83cc1bdf 106 main/binary-ppc64el/Release 6f03bcfea77b6b05e5959c9a48e0736c 37432240 main/binary-s390x/Packages 1d2a46185ed0bb161ae606673816e2b8 27910 main/binary-s390x/Packages.diff/Index 55b2873204d740bb61d1fef7c44f3a9b 9217612 main/binary-s390x/Packages.gz c04e71c9af4f85a3cb3572a47bfb6b02 6875604 main/binary-s390x/Packages.xz 8644bd20faff93a0b7716ca2e987ed96 104 main/binary-s390x/Release 1f772f7b647e73ad65bd8244ccf6606c 63066 main/debian-installer/binary-all/Packages c53b106eab1021074e41b465e7d7cf93 16829 main/debian-installer/binary-all/Packages.gz cde4986c79280dae866d3ac5893cf58b 14836 main/debian-installer/binary-all/Packages.xz 10085e8cf6c39e33baf83b03965074f2 102 main/debian-installer/binary-all/Release 5c134da0018d86ecfa464c17fd4c4807 258901 main/debian-installer/binary-amd64/Packages ff7d6e3aef8ad0d2606f498b9b9b312d 65129 main/debian-installer/binary-amd64/Packages.gz 0a0d7f601780e2e9e251bf77b95835cc 54808 main/debian-installer/binary-amd64/Packages.xz e3a090a211110a1a0fdffc2cc7a1c915 104 main/debian-installer/binary-amd64/Release 7f27f4fd16c5a61495f79920c2b69152 239565 main/debian-installer/binary-arm64/Packages 803a71ee6cb342142e7cab22d068bfaf 61678 main/debian-installer/binary-arm64/Packages.gz a536ce6e1d687a3c14dc6368d9edac6a 52152 main/debian-installer/binary-arm64/Packages.xz 806d63700dac5cea4d29e6a86e58597d 104 main/debian-installer/binary-arm64/Release 0c2f83d7603e1a94a75bb0fb55c2db5e 277695 main/debian-installer/binary-armel/Packages 759db9003daa3771dc6f7df2378654fe 67415 main/debian-installer/binary-armel/Packages.gz 9d496d50885bda800a9b28b082317616 56652 main/debian-installer/binary-armel/Packages.xz 5c1866d5f05996c75c2150ae69ad9815 104 main/debian-installer/binary-armel/Release 216554fdcabdecf7396d03436278a2d8 238490 main/debian-installer/binary-armhf/Packages 412f4ece6f0a6415e671b9ae6f3a5d2e 61449 main/debian-installer/binary-armhf/Packages.gz a65b1e027e43be059b0595a99661577e 51968 main/debian-installer/binary-armhf/Packages.xz 87de7b798ba50f1e06ba3ee7a1db41b4 104 main/debian-installer/binary-armhf/Release 46aebcdbbbfb439976bdf6cbab8cb1c4 163351 main/debian-installer/binary-hurd-i386/Packages d2c999206c127e2756b1fef9bca78460 44433 main/debian-installer/binary-hurd-i386/Packages.gz c53a434fd94adb1f01102945896b018a 37804 main/debian-installer/binary-hurd-i386/Packages.xz a84d93ba0ec25a5d1986ea4fcd76d92a 108 main/debian-installer/binary-hurd-i386/Release fae19170644abeb877ef7cb7fefa8389 326037 main/debian-installer/binary-i386/Packages cb6eb5aa7994ec1603e7b7dfa40eac2d 75140 main/debian-installer/binary-i386/Packages.gz 175c0a257d4384a9920d870c0a887b52 63056 main/debian-installer/binary-i386/Packages.xz cf30be831f54859034d2e11501806247 103 main/debian-installer/binary-i386/Release 31edeb172d84f67e53565e513f72d5ac 199536 main/debian-installer/binary-kfreebsd-amd64/Packages 996c98fcf568bb706df7f12b74d591cb 52052 main/debian-installer/binary-kfreebsd-amd64/Packages.gz b69fe29f33ef0f127075501561e02c04 44020 main/debian-installer/binary-kfreebsd-amd64/Packages.xz 05d24fbef387f91e082ab5dc7c46b9ee 113 main/debian-installer/binary-kfreebsd-amd64/Release d8c67469af6a623d0378a0a77260e952 198799 main/debian-installer/binary-kfreebsd-i386/Packages 12cefc0c7ad5d9485c3f1210e95320ac 51848 main/debian-installer/binary-kfreebsd-i386/Packages.gz ba7940166304e6d4c9226cbf083bcac6 43944 main/debian-installer/binary-kfreebsd-i386/Packages.xz 262a86cd91b0d0d5fb921a317d76df0e 112 main/debian-installer/binary-kfreebsd-i386/Release cd6ba1715c8dd3206a1db6d760aaaf65 299343 main/debian-installer/binary-mips/Packages ecca0c469dc3f398c41cecb551fac41e 70317 main/debian-installer/binary-mips/Packages.gz f97068d8c284bfb47af9b1f762ee9291 59428 main/debian-installer/binary-mips/Packages.xz 9d9f639b9f52548b2eb8392e691a5c5e 103 main/debian-installer/binary-mips/Release cf3dbee9b700902fe4067509874623f8 360842 main/debian-installer/binary-mips64el/Packages cc4a7d0dbb5eac93e698c021eae9f686 78622 main/debian-installer/binary-mips64el/Packages.gz a716d07ad9d6abcf990fd8dfd2c7e344 66228 main/debian-installer/binary-mips64el/Packages.xz 8246be42dc917113cba664e98bd50d3a 107 main/debian-installer/binary-mips64el/Release ace3200c84545df887a44de51ff45a3d 479718 main/debian-installer/binary-mipsel/Packages f2fe95f520fbacc0e344551e1ac6dc60 94984 main/debian-installer/binary-mipsel/Packages.gz 962396873962d1c525397b9d1c1cb238 80008 main/debian-installer/binary-mipsel/Packages.xz 3dc8b9d46dd15d877218c7c13d0e2884 105 main/debian-installer/binary-mipsel/Release dbc98c4149ed3bd0887dc8ebe14877ba 310055 main/debian-installer/binary-powerpc/Packages 57bc261239119875a729b73d9e95b181 72594 main/debian-installer/binary-powerpc/Packages.gz ef0d4217eaab3d6dfee94503a6e7bd35 60748 main/debian-installer/binary-powerpc/Packages.xz 066a5dd8d4763961ca7e72ab118e91d4 106 main/debian-installer/binary-powerpc/Release 31198895a526db6c434912f95566a0fb 241368 main/debian-installer/binary-ppc64el/Packages 0f77f3afb0bcbbf78b4352f870bcdf06 61537 main/debian-installer/binary-ppc64el/Packages.gz 793f00c0010c2ce15f474450f61c7da6 51868 main/debian-installer/binary-ppc64el/Packages.xz cc8afb9a3d24cea535c031ff83cc1bdf 106 main/debian-installer/binary-ppc64el/Release 61441145bd99af408307e3aba37da10e 217996 main/debian-installer/binary-s390x/Packages 911f2e61806f7487700cfcdda189c0ef 58135 main/debian-installer/binary-s390x/Packages.gz 97393e1d43b85a370b055cabfb4c2fad 48892 main/debian-installer/binary-s390x/Packages.xz 8644bd20faff93a0b7716ca2e987ed96 104 main/debian-installer/binary-s390x/Release 1be16ae9bcaa4d71ad54aefa158d0741 7796441 main/dep11/Components-amd64.yml 732f1040515480d9e43b70640103b84b 2854073 main/dep11/Components-amd64.yml.gz c6698a2a0283112674726e53f7fab4ab 1850876 main/dep11/Components-amd64.yml.xz 3d0a7cb0fb243858415b8f7101380071 7735559 main/dep11/Components-arm64.yml a9591dd40c9ca0eea351ddd652da8e53 2835371 main/dep11/Components-arm64.yml.gz 0c8465b31304d95dc287c570f8ddf0ef 1839368 main/dep11/Components-arm64.yml.xz eacbffae318216658b9a18add435a6d2 7744621 main/dep11/Components-armel.yml fdfe5bbb75136248ff776fbd3c7b7934 2837621 main/dep11/Components-armel.yml.gz 160860e7053cb47167c651525b52e39c 1840304 main/dep11/Components-armel.yml.xz 6a8923dce0d3d21ce7a089a69e2e2608 7753412 main/dep11/Components-armhf.yml f0c4edf897f8ca8cc93f7201173a4ca4 2838544 main/dep11/Components-armhf.yml.gz 980acfd0a92bff12bcdd7b83515bc70f 1842132 main/dep11/Components-armhf.yml.xz 37d3152322d44bdf96bdc19da2f8405b 7795011 main/dep11/Components-i386.yml f24043ea4e83a25f76d67e533004a2f1 2853321 main/dep11/Components-i386.yml.gz 54ae9c7371b0593b99e935684b61e4b9 1850760 main/dep11/Components-i386.yml.xz 55ba75bfecfa1fd5cbb1fd4f97dcbe1d 7285280 main/dep11/Components-kfreebsd-amd64.yml 5788f902e8bc2b33ea5800bf4e0b876b 2669347 main/dep11/Components-kfreebsd-amd64.yml.gz 578df9dbc63281664eedea849e60631c 1732772 main/dep11/Components-kfreebsd-amd64.yml.xz 46df1c219af20dd2c1951903df5e7eed 7747672 main/dep11/Components-powerpc.yml 242ab1e62e0bea1e80dbade25f2d2df1 2838839 main/dep11/Components-powerpc.yml.gz 4f980e4a58d23d7f732989dcf796303f 1841208 main/dep11/Components-powerpc.yml.xz e52bdd59f2f7dbbcb176a4f0a43f16b9 17068032 main/dep11/icons-128x128.tar c9efbb8a8005e19429ad2e15a76f04a2 15132129 main/dep11/icons-128x128.tar.gz 650e62ecc56676b2f445b7bbdf2f9b1b 8170496 main/dep11/icons-64x64.tar f643dd47e28d5a08c5f8f688c16596e9 6466066 main/dep11/icons-64x64.tar.gz 0071a785652b9a6f6c7d1dbec292a25c 8628 main/i18n/Translation-ca cab00f4af70523ecdbf645e7f7ee2492 3543 main/i18n/Translation-ca.bz2 c1961e8d860a293b033a6a975ca17c68 1755527 main/i18n/Translation-cs af72078ec1a260d5778aa99c2354e8e9 456958 main/i18n/Translation-cs.bz2 ca359d40eb141112515e2bc9f2032f08 14512 main/i18n/Translation-cs.diff/Index e6aa62edfced0fd7ac54da06eb6deefd 17190796 main/i18n/Translation-da 7a484c530f3ca3dcf5f5b45c0ea270fa 3716728 main/i18n/Translation-da.bz2 fe455625cace78a9bd4494b0d67aac04 27910 main/i18n/Translation-da.diff/Index d3b62a232514714ef632dcafb4df1989 7563488 main/i18n/Translation-de 0cf191dd8ca9aebfefe0034fc720dfd3 1724541 main/i18n/Translation-de.bz2 86e70b7b43dadbfc6cb932293268ecd8 27304 main/i18n/Translation-de.diff/Index a344219bf0eec9139d5270017ecfceee 1347 main/i18n/Translation-de_DE 0fe0725f74bb5249f15f30ce965142d5 830 main/i18n/Translation-de_DE.bz2 c26d1f5d454d59a734435115a463bc5e 8287 main/i18n/Translation-el a2e3d718b49885932660c9c5dbdc0570 1896 main/i18n/Translation-el.bz2 b5e6c14f747fd3d3ea480e4b0fdcb597 27076273 main/i18n/Translation-en 7cd099961072cc7a1a685d18f8b1c84b 5427424 main/i18n/Translation-en.bz2 8248703cda79af6d6df64ebcb607cf90 27910 main/i18n/Translation-en.diff/Index b38a08cc72a01f896fa745a25ef55c00 2517 main/i18n/Translation-eo 82bf9de0303a2ff3fb791f19c84f3c25 1279 main/i18n/Translation-eo.bz2 745a93b46c684078c290249362cdcf30 1326503 main/i18n/Translation-es 691f4e9949f6a13c01b0dc016015bdd3 323379 main/i18n/Translation-es.bz2 bf1ae7fcdadb3150158681852b8117fa 17464 main/i18n/Translation-es.diff/Index fe1a2c966238e27a62bc1d9531978927 14383 main/i18n/Translation-eu c2dc13f9f8a39978e10ae070437a5362 5475 main/i18n/Translation-eu.bz2 a894e9b1a8c56b927d37fc606a2d92fd 736 main/i18n/Translation-eu.diff/Index 48d0c6ee8c241ea6335713ce281e0980 386506 main/i18n/Translation-fi 798db04510751ffa53c3848205b95efd 106618 main/i18n/Translation-fi.bz2 0678f7e2ee77b76f2866b47c88713a03 11068 main/i18n/Translation-fi.diff/Index acf5e6f0027a3756ad32b596aec21793 7401641 main/i18n/Translation-fr 26d9074edbb798bbd1b4837a5343a772 1518856 main/i18n/Translation-fr.bz2 f00d9eeb8f2cc8e344fb76e071fabec1 27796 main/i18n/Translation-fr.diff/Index 54790b326f496d11042e7afa4c4631b1 21899 main/i18n/Translation-hr 9fdbf9e07c96ee5af56030d154ff607f 5692 main/i18n/Translation-hr.bz2 cabf65aab935b431ac8030167ef8eeef 2212 main/i18n/Translation-hr.diff/Index cc423bce94fd7d062ec63881438d6373 96810 main/i18n/Translation-hu e1205e9042cd5be51989f5b4533aa8a6 31851 main/i18n/Translation-hu.bz2 4119e3b251dda916b184607c8514ec78 1720 main/i18n/Translation-hu.diff/Index cb9bfeca0fea2c93ecccdc320b485294 7124 main/i18n/Translation-id d0624bdc42d2602cb4664b190e372468 2899 main/i18n/Translation-id.bz2 2381f089c05c4e0ac6cb5c8ffd600b2d 21789753 main/i18n/Translation-it fdc75537379d05d4bed7acd588b01f1f 4268806 main/i18n/Translation-it.bz2 6cccac8e38b9357ff436204a1a084056 27910 main/i18n/Translation-it.diff/Index 64697acfcd63339ba56af18a4de28a08 5075815 main/i18n/Translation-ja 0bbc55ae9583df8ff28b694b60640a83 913629 main/i18n/Translation-ja.bz2 2045762863e31cddc9166a4e80a84b62 27304 main/i18n/Translation-ja.diff/Index f6fe5be7465cd63813c244b1a1f54c45 19077 main/i18n/Translation-km 17c60ce8f72b68982629b2e469915109 3407 main/i18n/Translation-km.bz2 12087f416d16de7c5ffd6698a9dedb0f 1720 main/i18n/Translation-km.diff/Index f375c313c1e93832540f1e2f47f503bd 1631004 main/i18n/Translation-ko dd61eca2bc9adc13f33cf80f106713d6 348522 main/i18n/Translation-ko.bz2 c8d9248d7a963c2b5aa9bda9b1fa7599 22876 main/i18n/Translation-ko.diff/Index d41d8cd98f00b204e9800998ecf8427e 0 main/i18n/Translation-ml 4059d198768f9f8dc9372dc1c54bc3c3 14 main/i18n/Translation-ml.bz2 5676b1dbf1afed1dfd2ed8c4d87d6f8e 2325 main/i18n/Translation-nb be1e645de35839db0778d87b8e903e43 1302 main/i18n/Translation-nb.bz2 92a2666c8a09bb4cb4fd0649a87adf99 264671 main/i18n/Translation-nl 0b2c46361ecfdeb5025a5cd67f3beb89 71487 main/i18n/Translation-nl.bz2 6c0270d045ed66a981290e1a31177f35 9100 main/i18n/Translation-nl.diff/Index 70503f8efbcb3ab4092ab5198c5d78df 2458040 main/i18n/Translation-pl b6e413762a2dc3b79dc0f7ea6e27802c 569778 main/i18n/Translation-pl.bz2 3d7080de3027e28aa20808e3248a077c 26812 main/i18n/Translation-pl.diff/Index e7a1a2f02d37eaafc8a23437aad0a43a 1564381 main/i18n/Translation-pt 55e4c007261255a9991e72546fd4a3ba 386197 main/i18n/Translation-pt.bz2 2222e20f5c51815f96e3062e0b1d9d25 15988 main/i18n/Translation-pt.diff/Index 3febc6212a82148533ece34ba3012a46 3548094 main/i18n/Translation-pt_BR ed8e05782286ae5b37940b23bb305d72 846754 main/i18n/Translation-pt_BR.bz2 5cfd36393ef4c067d78f7ca3d948a0fa 26812 main/i18n/Translation-pt_BR.diff/Index 60f08a8c65af4e8895cf8c53d1e35b98 2710 main/i18n/Translation-ro a998260e0995f073174d9e0d1fb65a9f 1374 main/i18n/Translation-ro.bz2 b0c2a9c949dcc75045cb71e66a08912c 2655478 main/i18n/Translation-ru ff4a328011b0ff8aad87691a86d220b7 431543 main/i18n/Translation-ru.bz2 9c0448669aac690b8fc06ff2cbff4bad 21400 main/i18n/Translation-ru.diff/Index 88d33ae4bd81a0727d8dce11de7273d7 3819007 main/i18n/Translation-sk 6f2322531d7839e2a27ca48dd65e767a 843783 main/i18n/Translation-sk.bz2 1b4fb1d9ea9d7e58ea738aa8e498a737 27796 main/i18n/Translation-sk.diff/Index 3f7669405e04d1862d6eb8e6817724d5 441964 main/i18n/Translation-sr aa1268bd55a5b92f37682ac36f8cbc78 78350 main/i18n/Translation-sr.bz2 34509802078a71613ebd27a1c42ddc14 8608 main/i18n/Translation-sr.diff/Index 2e7e17ccaed45a46635480f54b65ab39 117165 main/i18n/Translation-sv 20113f96e4db968fa1ad21398e563477 36066 main/i18n/Translation-sv.bz2 1fb32f3ac643d745064a5fdea2152a7a 3196 main/i18n/Translation-sv.diff/Index 617fcb26803e9b2a60c2716ea4744451 902 main/i18n/Translation-tr 96c9deb10e4bb691e778589dec582fd7 530 main/i18n/Translation-tr.bz2 f352a71d97747abb0b7ec10b17df6fed 4277439 main/i18n/Translation-uk 46389306c392dacc4ba9a57a03899c06 669553 main/i18n/Translation-uk.bz2 62b32c0ed3d5609f449841b550306847 22384 main/i18n/Translation-uk.diff/Index 3490920ca94901031bfef0b0179c4530 30443 main/i18n/Translation-vi 4b9f8610634f77136a0ff15daaa2e206 8837 main/i18n/Translation-vi.bz2 31d75bce045815aa393d2275c1e536d7 2212 main/i18n/Translation-vi.diff/Index 7717fa25bd691ec385bddacf42419c9f 2799 main/i18n/Translation-zh b5a4e7e47da47ac7596ed5d72fdeef93 1526 main/i18n/Translation-zh.bz2 cbdf4b385a9e472b4ec44142a2dd6c37 334563 main/i18n/Translation-zh_CN d76c54843dbd3f0e47c236bfc42d8f79 92636 main/i18n/Translation-zh_CN.bz2 145978f6e98c3cb6ff3d37ab5358cb41 7132 main/i18n/Translation-zh_CN.diff/Index 591bd2e425d8755cd1af8b449070ef3e 57122 main/i18n/Translation-zh_TW d565663e6a0417f70b1e1eecd2f0b3eb 20289 main/i18n/Translation-zh_TW.bz2 0bd805f62422f7ee5a4d8cc0f0b59e01 3196 main/i18n/Translation-zh_TW.diff/Index c06312d27a05b395a5a45ae7c2507ab5 53815 main/installer-amd64/20150422/images/MD5SUMS bc3feb487bafcbdfd06e06284000e368 72131 main/installer-amd64/20150422/images/SHA256SUMS 7f129f18d483d8678691e32bb6954436 53815 main/installer-amd64/20150718/images/MD5SUMS b7cab70bdf269ab34b0a5b0d36ec0734 72131 main/installer-amd64/20150718/images/SHA256SUMS 68f30bd7c1f371b82a894837a1643ab9 53815 main/installer-amd64/20150813/images/MD5SUMS 1b9cd6c615f7d0ee59aeee91e089cc7f 72131 main/installer-amd64/20150813/images/SHA256SUMS dcd4a129eb03d7b79764465d97468658 53635 main/installer-amd64/20150828/images/MD5SUMS 7da4e9a6338cbda2de43afefc75ee2f9 71887 main/installer-amd64/20150828/images/SHA256SUMS 515b783412be83349fc94cbcc41f3983 53635 main/installer-amd64/20150911/images/MD5SUMS 30ed9399e20e911c5046120fa65026ca 71887 main/installer-amd64/20150911/images/SHA256SUMS 1f897dfc4d7ae54c3a9d8df23bb7c314 53727 main/installer-amd64/20151023/images/MD5SUMS 6d2f81899356555e441421d51dc741e8 72011 main/installer-amd64/20151023/images/SHA256SUMS c9583f4597e52933689397d09d890d80 53635 main/installer-amd64/20160101/images/MD5SUMS a45f291d4f5c4128e9014271c7b12c7c 71887 main/installer-amd64/20160101/images/SHA256SUMS d6d4aa83312ae55d273bd8385f16aa38 53727 main/installer-amd64/20160106/images/MD5SUMS ab78bc26828b883a79bc9cc23b1286a9 72011 main/installer-amd64/20160106/images/SHA256SUMS bb47b77e4f3d80c9bf892a9d4dd4f8ba 53727 main/installer-amd64/20160516+b1/images/MD5SUMS 421bc2a824adecce9d2376963eb9b63d 72011 main/installer-amd64/20160516+b1/images/SHA256SUMS e4f3a21784d6a3102a130ba20e92f0d8 53727 main/installer-amd64/20160516/images/MD5SUMS 1a3e7aaf715ce42b17cfb6360b16e236 72011 main/installer-amd64/20160516/images/SHA256SUMS b0d1c9a722a2b5affd29b77b902e3a0c 53727 main/installer-amd64/20160630/images/MD5SUMS ce41b2fa73adf559477b37a65d4ead20 72011 main/installer-amd64/20160630/images/SHA256SUMS b0d1c9a722a2b5affd29b77b902e3a0c 53727 main/installer-amd64/current/images/MD5SUMS ce41b2fa73adf559477b37a65d4ead20 72011 main/installer-amd64/current/images/SHA256SUMS c52390d4695c984028c98a32f7a76dff 19148 main/installer-arm64/20150422/images/MD5SUMS f4160f9f0b4b49d8997ee2c920004594 25912 main/installer-arm64/20150422/images/SHA256SUMS 8bb2134b1f6eb311047621b8327b92ce 19221 main/installer-arm64/20150718/images/MD5SUMS 00212197e96943527cd7d877599c1f26 26017 main/installer-arm64/20150718/images/SHA256SUMS 7a76d85513a26f4d247d12f07169a483 19221 main/installer-arm64/20150813/images/MD5SUMS b9582e565c71f9803f145308d626fb58 26017 main/installer-arm64/20150813/images/SHA256SUMS 46aa54cf6adecef384915e9e97139b08 19221 main/installer-arm64/20150828/images/MD5SUMS fc369331103d7c5128e55907b8a65dfc 26017 main/installer-arm64/20150828/images/SHA256SUMS 792e4f72b73996159ba8c760cf005fc5 19221 main/installer-arm64/20150911/images/MD5SUMS 0a85072cf979aa24d6e95e9a2fd05682 26017 main/installer-arm64/20150911/images/SHA256SUMS bbd9976457d3354a20702dc3837a501c 19366 main/installer-arm64/20151023/images/MD5SUMS 3c0d447a3310c59b52634d2536f28dc2 26226 main/installer-arm64/20151023/images/SHA256SUMS 1e7d8258b04971e32b2a1eaaf03f1cb7 19366 main/installer-arm64/20160101/images/MD5SUMS 16a6252eeac87cec2a8a63d5aa643882 26226 main/installer-arm64/20160101/images/SHA256SUMS 81694c96c2c2a89af5094dd5cc62ae43 19366 main/installer-arm64/20160106/images/MD5SUMS 50eb4893fcb29fa5e101ad3d89e16a68 26226 main/installer-arm64/20160106/images/SHA256SUMS a27a3ca9b9201d08a6959c203abd6665 19788 main/installer-arm64/20160516+b1/images/MD5SUMS 24d396d0e93b2f1b994015a7b17b80c0 26840 main/installer-arm64/20160516+b1/images/SHA256SUMS 530f897d2f040bb2171355b978ce3ce5 19788 main/installer-arm64/20160516/images/MD5SUMS b7ff35aeda772a129d9ed692546d0a44 26840 main/installer-arm64/20160516/images/SHA256SUMS 7801f2f68870b7629876cefe1b876644 20520 main/installer-arm64/20160630/images/MD5SUMS 154af14e29eff0e06367dbdb7f319479 27892 main/installer-arm64/20160630/images/SHA256SUMS 7801f2f68870b7629876cefe1b876644 20520 main/installer-arm64/current/images/MD5SUMS 154af14e29eff0e06367dbdb7f319479 27892 main/installer-arm64/current/images/SHA256SUMS 42fdeb26bb11aeba2b73bb665394ed5d 8985 main/installer-armel/20150422/images/MD5SUMS 5508c992d754756a76d94459694d0846 12645 main/installer-armel/20150422/images/SHA256SUMS 68f868a28043100a893c564b0faae3da 10136 main/installer-armel/20150718/images/MD5SUMS af69eb139df5e30ef86e8e9959e82cf6 14244 main/installer-armel/20150718/images/SHA256SUMS 01e076ec44be1c77c251d7d5fc1c9ebc 10214 main/installer-armel/20150813/images/MD5SUMS 652fce61997776934cc902891dfbef34 14354 main/installer-armel/20150813/images/SHA256SUMS 1fe56992ad57188f6c64ef4a587fca08 10214 main/installer-armel/20150828/images/MD5SUMS 2e1860e17b51a02ab3dcd4a6ca2b8270 14354 main/installer-armel/20150828/images/SHA256SUMS ab92cc7d8af85dcbdfeb1c23a1a4ada3 10214 main/installer-armel/20150911/images/MD5SUMS 81239b7aeb188258c05bcd148ba6aca7 14354 main/installer-armel/20150911/images/SHA256SUMS d1ef8153db55b4c7fefea9b5d665268b 10214 main/installer-armel/20151023/images/MD5SUMS 685702eda13193131532e737ac847225 14354 main/installer-armel/20151023/images/SHA256SUMS 907bdf89e1cd0b04c3ea3c5ac5293524 11320 main/installer-armel/20160101/images/MD5SUMS a174d2d7c9f86588e9e6dbd64c1fe95b 15908 main/installer-armel/20160101/images/SHA256SUMS 4d091ea335e1cc28c501fac36f834e73 11320 main/installer-armel/20160106/images/MD5SUMS 303a7bf2d68e0ee414803a694a692871 15908 main/installer-armel/20160106/images/SHA256SUMS b9a3925fa4cc3d6746f3091037410b46 21918 main/installer-armel/20160516+b1/images/MD5SUMS 1cecfbd15243177f6811e81cd44c0fb2 30634 main/installer-armel/20160516+b1/images/SHA256SUMS e2a460d19a1987783987245a64c82843 21918 main/installer-armel/20160516/images/MD5SUMS 985f5387499527325e845ddc5a97207b 30634 main/installer-armel/20160516/images/SHA256SUMS eaf0a967e3ddddbf271a22eb172a253d 22683 main/installer-armel/20160630/images/MD5SUMS db333669e2bc24baf7b5edf891dd2df6 31687 main/installer-armel/20160630/images/SHA256SUMS eaf0a967e3ddddbf271a22eb172a253d 22683 main/installer-armel/current/images/MD5SUMS db333669e2bc24baf7b5edf891dd2df6 31687 main/installer-armel/current/images/SHA256SUMS 77d9da266c887d99f50e4a78770da16a 19599 main/installer-armhf/20150422/images/MD5SUMS e1ea97c8e0f0ab02e0acf66302b64072 28379 main/installer-armhf/20150422/images/SHA256SUMS 084e58bfd24c832bf0518b2087ef57f9 21631 main/installer-armhf/20150718/images/MD5SUMS d331ede290678323e8b2c2ee8b64c1eb 31339 main/installer-armhf/20150718/images/SHA256SUMS fd95ab71c8125b6393c95a658b1c4208 21993 main/installer-armhf/20150813/images/MD5SUMS 53b5644ab2d2dceb5a1aa76a29646520 31861 main/installer-armhf/20150813/images/SHA256SUMS 91a3bb58bdb3f081fd69ee0e5e64d7c5 21993 main/installer-armhf/20150828/images/MD5SUMS d76f6202ffae6064a7e5d8d91b746cdf 31861 main/installer-armhf/20150828/images/SHA256SUMS 6743052cfa5856911fadfec7d185b3a7 21993 main/installer-armhf/20150911/images/MD5SUMS aac8a16428226be115dc66569c3462fa 31861 main/installer-armhf/20150911/images/SHA256SUMS 9af75cfdb021ecfe4ca81d83ade7c198 23951 main/installer-armhf/20151023/images/MD5SUMS a48ab9a8bc1bf849804ffc53eb06e768 34683 main/installer-armhf/20151023/images/SHA256SUMS e25a9a21c87af432d1de665392121307 25780 main/installer-armhf/20160101/images/MD5SUMS 008a0740f85cd184a7da076dc416a0a0 37312 main/installer-armhf/20160101/images/SHA256SUMS ca01103d2b6f761a26d2c1e54d504a80 25780 main/installer-armhf/20160106/images/MD5SUMS 4acd1721546591fcab76797c815826fc 37312 main/installer-armhf/20160106/images/SHA256SUMS 904c6dc5f2981eca165615bf4f5e903e 32094 main/installer-armhf/20160516+b1/images/MD5SUMS 0398c7bcc09de0655cca935474d7b114 46346 main/installer-armhf/20160516+b1/images/SHA256SUMS f435648778a72cc5758610cdd56ac726 32094 main/installer-armhf/20160516/images/MD5SUMS 497beeebd06717cdac51210460acea86 46346 main/installer-armhf/20160516/images/SHA256SUMS b70948b0dd60cec7390d2a38b8124eae 33942 main/installer-armhf/20160630/images/MD5SUMS 69e0668080dd6865f9410c93e2ab58a5 49026 main/installer-armhf/20160630/images/SHA256SUMS b70948b0dd60cec7390d2a38b8124eae 33942 main/installer-armhf/current/images/MD5SUMS 69e0668080dd6865f9410c93e2ab58a5 49026 main/installer-armhf/current/images/SHA256SUMS 6945a44a20596ed6e81e3dea9c2f3ddf 52495 main/installer-i386/20150422/images/MD5SUMS 34ce5cc3c7b4ac330eb592aa8fb8c41e 70875 main/installer-i386/20150422/images/SHA256SUMS 1c5cc19000791e5d93bc7ffe81108eff 52495 main/installer-i386/20150718/images/MD5SUMS a0e176faa0be4cae8d8aeb3103e69b87 70875 main/installer-i386/20150718/images/SHA256SUMS 8047fd41b6469d046c4319e96a25b948 52495 main/installer-i386/20150813/images/MD5SUMS 0203c7fa623222c0de479518d7e164b1 70875 main/installer-i386/20150813/images/SHA256SUMS 7997f898ef1496cb221aebfe5c461988 52317 main/installer-i386/20150828/images/MD5SUMS 9f784e11d66674a93f119cacbc279b28 70633 main/installer-i386/20150828/images/SHA256SUMS ffe9b11bcae11587bc3923ec6523bfe6 52317 main/installer-i386/20150911/images/MD5SUMS 838890aa65f653db9ef53aea88a1582f 70633 main/installer-i386/20150911/images/SHA256SUMS 4bc3cc82cd40a2f863a1566ee59b868d 52317 main/installer-i386/20151023/images/MD5SUMS 0a41ec2e6856567d4356bda25cbda097 70633 main/installer-i386/20151023/images/SHA256SUMS 68cafe55e10bac36cfad4a57c47a89ad 52317 main/installer-i386/20160101/images/MD5SUMS 461c856624885a1979aea49ac3fd9f2d 70633 main/installer-i386/20160101/images/SHA256SUMS 0ba0a658a6f4e044805d79ae12618636 52317 main/installer-i386/20160106/images/MD5SUMS 6526fc3c4bc794aa1d005b26cf1e8dc3 70633 main/installer-i386/20160106/images/SHA256SUMS 1e77990145c4df025f08354f9c35dad5 52317 main/installer-i386/20160516+b1/images/MD5SUMS 76d42cd927e94ca61cc2e019efaaf897 70633 main/installer-i386/20160516+b1/images/SHA256SUMS 883125e6b492a708d24da1be02c8c34f 52317 main/installer-i386/20160516/images/MD5SUMS d9397411580ac3f3036df6faf381aff2 70633 main/installer-i386/20160516/images/SHA256SUMS 996eba6a5d82e580efa86b1ddd8bc6cb 52317 main/installer-i386/20160630/images/MD5SUMS 111ce06fba5d621871d271dbe4f3b514 70633 main/installer-i386/20160630/images/SHA256SUMS 996eba6a5d82e580efa86b1ddd8bc6cb 52317 main/installer-i386/current/images/MD5SUMS 111ce06fba5d621871d271dbe4f3b514 70633 main/installer-i386/current/images/SHA256SUMS d318581f7a29594cc02e0dfacb2dbfe8 2147 main/installer-kfreebsd-amd64/20150422/images/MD5SUMS 97b2421e3bde5da15598d1aa3ce795c3 3183 main/installer-kfreebsd-amd64/20150422/images/SHA256SUMS d318581f7a29594cc02e0dfacb2dbfe8 2147 main/installer-kfreebsd-amd64/current/images/MD5SUMS 97b2421e3bde5da15598d1aa3ce795c3 3183 main/installer-kfreebsd-amd64/current/images/SHA256SUMS 02c599b2311ccde70340e801d33e8483 1209 main/installer-kfreebsd-i386/20150422/images/MD5SUMS 9d81e3c19d2c88f0d3ca2cb0e06e3123 1861 main/installer-kfreebsd-i386/20150422/images/SHA256SUMS 02c599b2311ccde70340e801d33e8483 1209 main/installer-kfreebsd-i386/current/images/MD5SUMS 9d81e3c19d2c88f0d3ca2cb0e06e3123 1861 main/installer-kfreebsd-i386/current/images/SHA256SUMS 48639d5b34148741df2132cb9079bd79 940 main/installer-mips/20150422/images/MD5SUMS e592333fd6d054e559d15027b9c3ebaa 1496 main/installer-mips/20150422/images/SHA256SUMS 224d0f46f32a094f7a42295de8bc1039 937 main/installer-mips/20150718/images/MD5SUMS 55ce95ed5bbd19b1a93c8233fb5269bc 1493 main/installer-mips/20150718/images/SHA256SUMS 92be92e017b485e49883407faeee2994 937 main/installer-mips/20150813/images/MD5SUMS 71732fb124d12d18d2818285d5750cfb 1493 main/installer-mips/20150813/images/SHA256SUMS 7067906c661fbf1aa6aff1b1e3b84006 413 main/installer-mips/20150828/images/MD5SUMS 34285cf66d7d6f7ddc6eb60ec3ecf454 713 main/installer-mips/20150828/images/SHA256SUMS c2eecb82d870fab4132fb5ed53d55d05 413 main/installer-mips/20150911/images/MD5SUMS 42a45de7b7382c3be754544c384a7217 713 main/installer-mips/20150911/images/SHA256SUMS 27c4bd02410bbe9bb7ad681fe0965af1 413 main/installer-mips/20151023/images/MD5SUMS 5e0a2b483d487d151f938cc2d4a827bb 713 main/installer-mips/20151023/images/SHA256SUMS be8382a52df21fc4828ea0cd16d3e6b5 413 main/installer-mips/20160101/images/MD5SUMS 22da8c160c210653e42a34f097e94a41 713 main/installer-mips/20160101/images/SHA256SUMS f36092c5a0f5a57252aabee5235ed957 413 main/installer-mips/20160106/images/MD5SUMS ce69ee304af61fdfb6c72890929c0bbf 713 main/installer-mips/20160106/images/SHA256SUMS 419d3e461777185fd5f1dd0a098d828a 413 main/installer-mips/20160516+b1/images/MD5SUMS 042f2834caa5d1dac12d59ba54261d3d 713 main/installer-mips/20160516+b1/images/SHA256SUMS 2347e235966b2ef3d2e5a64a090a7664 413 main/installer-mips/20160516/images/MD5SUMS 83d65a4470a7bb99c20e77e542936677 713 main/installer-mips/20160516/images/SHA256SUMS ed29a35844345b187504a5b9bbd7f042 413 main/installer-mips/20160630/images/MD5SUMS a21668b1da7ae41d6be97169d2a89dd8 713 main/installer-mips/20160630/images/SHA256SUMS ed29a35844345b187504a5b9bbd7f042 413 main/installer-mips/current/images/MD5SUMS a21668b1da7ae41d6be97169d2a89dd8 713 main/installer-mips/current/images/SHA256SUMS a1c3f76716993801388b8224d75b0968 1213 main/installer-mipsel/20150422/images/MD5SUMS 266b1b098b295678ad04fba1e9734dd1 1865 main/installer-mipsel/20150422/images/SHA256SUMS 067b78e650973a61d0fc05c7789a8c16 1208 main/installer-mipsel/20150718/images/MD5SUMS 731af366b19b62a0dd2cfe33e74ce5a6 1860 main/installer-mipsel/20150718/images/SHA256SUMS 68bdb7f12ffc64f0491605bd33ef2ec2 1208 main/installer-mipsel/20150813/images/MD5SUMS 4f5951aaa4bd36dd4c6b4ecdf63f305a 1860 main/installer-mipsel/20150813/images/SHA256SUMS 144356eb6a59b11efefeccc0bd4c7f46 919 main/installer-mipsel/20150828/images/MD5SUMS d8b729cfd672eeb091b6ceb40f447123 1443 main/installer-mipsel/20150828/images/SHA256SUMS 4f178f3b0a49b9d640ef93e26bf37d0b 919 main/installer-mipsel/20150911/images/MD5SUMS 78afc840fd1494ac4245a4b533a21f6d 1443 main/installer-mipsel/20150911/images/SHA256SUMS cd31221bc19cfb52dcedbc984d153c0d 919 main/installer-mipsel/20151023/images/MD5SUMS c957ab7846cfa747a98815ba700fb33a 1443 main/installer-mipsel/20151023/images/SHA256SUMS 20ddf497b0b8ab7bab70762b17d43327 919 main/installer-mipsel/20160101/images/MD5SUMS 87aee614424b805f75fac5a08020c6a8 1443 main/installer-mipsel/20160101/images/SHA256SUMS d479dae7d216bbce0ebe88e7ef28e83f 919 main/installer-mipsel/20160106/images/MD5SUMS eea0cdfa9fa9463281705b81bb046243 1443 main/installer-mipsel/20160106/images/SHA256SUMS 96f526d410c28b3d5654677835ec32b7 919 main/installer-mipsel/20160516+b1/images/MD5SUMS c9b4ec898f63605d0b3e78c0aa3af1b6 1443 main/installer-mipsel/20160516+b1/images/SHA256SUMS 384596ffdee88d68205697cf6f294b6c 919 main/installer-mipsel/20160516/images/MD5SUMS cad28dcdc8c7b000aec4acb11bb00393 1443 main/installer-mipsel/20160516/images/SHA256SUMS 6e45f9890334ddd5952cbc9f66ff6b70 624 main/installer-mipsel/20160630/images/MD5SUMS d45a277d7403f1a21eb246a6508c294b 1020 main/installer-mipsel/20160630/images/SHA256SUMS 6e45f9890334ddd5952cbc9f66ff6b70 624 main/installer-mipsel/current/images/MD5SUMS d45a277d7403f1a21eb246a6508c294b 1020 main/installer-mipsel/current/images/SHA256SUMS 757a78a03000d563e55139d067fd3463 2128 main/installer-powerpc/20150422/images/MD5SUMS 76c2a153bd1076a232bf969c35155903 3292 main/installer-powerpc/20150422/images/SHA256SUMS 5245865eb003d4be1ed33fd642905b6f 2128 main/installer-powerpc/20150718/images/MD5SUMS 8c9ccd5536735c8f7b991c30fed75674 3292 main/installer-powerpc/20150718/images/SHA256SUMS 0580bccea0f11c44e1d57157a47aeec0 2128 main/installer-powerpc/20150813/images/MD5SUMS bdd024d098d697461fa4a96b4aebb945 3292 main/installer-powerpc/20150813/images/SHA256SUMS a4f9a02bd66f89fde4b88bc0082dde84 2128 main/installer-powerpc/20150828/images/MD5SUMS d33c3fa9fd6cc09f73ac64358d1adbbf 3292 main/installer-powerpc/20150828/images/SHA256SUMS 29a9a229be7ce7813f1c8302c8ba2cb6 2128 main/installer-powerpc/20150911/images/MD5SUMS d949aadd330e8b09cb867b28aeaa0e7a 3292 main/installer-powerpc/20150911/images/SHA256SUMS abd509be91d2158ff0670bc5a7a0949f 2128 main/installer-powerpc/20151023/images/MD5SUMS 708146f02bef4cc752e99c3859fdb95f 3292 main/installer-powerpc/20151023/images/SHA256SUMS 9ca3081166b7b2d71224257ca41007a0 2128 main/installer-powerpc/20160101/images/MD5SUMS 0085cdcca6703fdb838d06fb2cf1a42f 3292 main/installer-powerpc/20160101/images/SHA256SUMS 92cd6aec3ece889809e885dfc4669c0a 2128 main/installer-powerpc/20160106/images/MD5SUMS 1655af9c8bda75e0f89b5af81469e7fd 3292 main/installer-powerpc/20160106/images/SHA256SUMS 07e9f60a46fd4937ec6c4a7202476998 2128 main/installer-powerpc/20160516+b1/images/MD5SUMS 908bd1aa3a7fcc3904add5938081fab9 3292 main/installer-powerpc/20160516+b1/images/SHA256SUMS c4340ecd280d5db8de64db0d767e9622 2128 main/installer-powerpc/20160516/images/MD5SUMS 0f97c9252b9fb5b3c02e362b9c16d21d 3292 main/installer-powerpc/20160516/images/SHA256SUMS c8f9568908e8704b30440ce52da1ebbb 2128 main/installer-powerpc/20160630/images/MD5SUMS adb7777418ccc4e4427274ed251b7856 3292 main/installer-powerpc/20160630/images/SHA256SUMS c8f9568908e8704b30440ce52da1ebbb 2128 main/installer-powerpc/current/images/MD5SUMS adb7777418ccc4e4427274ed251b7856 3292 main/installer-powerpc/current/images/SHA256SUMS 6730181232434b5e0b07f070e4037e5b 576 main/installer-ppc64el/20150422/images/MD5SUMS 6b13d9e4427cf78bdd9a3b70ac84ff0b 972 main/installer-ppc64el/20150422/images/SHA256SUMS 595ceae6c00d35e395a3af5c655c851b 576 main/installer-ppc64el/20150718/images/MD5SUMS 9ab151980e2dd5c500a833a0a341f70e 972 main/installer-ppc64el/20150718/images/SHA256SUMS 8a5cd37d58b4647c5613e7091678a55b 576 main/installer-ppc64el/20150813/images/MD5SUMS 471ec1ca07a0feebb528f874886340c0 972 main/installer-ppc64el/20150813/images/SHA256SUMS cd63cf1e35d8ac047a67802e5dcc9cef 576 main/installer-ppc64el/20150828/images/MD5SUMS ab4431025e32c4dc83cfa311da161eaa 972 main/installer-ppc64el/20150828/images/SHA256SUMS d653d80f04396e2da59447fda908c5f7 576 main/installer-ppc64el/20150911/images/MD5SUMS 85c4644ecd578fb3ba95002dadb441c0 972 main/installer-ppc64el/20150911/images/SHA256SUMS 928c032d7073c4fb5104c1d56da6a289 576 main/installer-ppc64el/20151023/images/MD5SUMS ff02d742114719e31bfb2ad1be3970f9 972 main/installer-ppc64el/20151023/images/SHA256SUMS 6be90a72534881c30ecd59f457276204 576 main/installer-ppc64el/20160101/images/MD5SUMS 98cbf26c82a1e4406ffac22a08269b13 972 main/installer-ppc64el/20160101/images/SHA256SUMS 7b8cbfee46550eea6b2438af6ef94026 576 main/installer-ppc64el/20160106/images/MD5SUMS 701189655541d16f1218ca97f41285ac 972 main/installer-ppc64el/20160106/images/SHA256SUMS af797e06df9a57856950f7d67d4f5368 576 main/installer-ppc64el/20160516+b1/images/MD5SUMS 0a5d484587f0adb492df4b1812af9ca1 972 main/installer-ppc64el/20160516+b1/images/SHA256SUMS e89c504b16aad9e473dcb8cc3f4fbd8a 576 main/installer-ppc64el/20160516/images/MD5SUMS bce4e7471038aeba7ef43ebf15dfe7b7 972 main/installer-ppc64el/20160516/images/SHA256SUMS 2189eb589afec2cd0f0cd91089900aa5 576 main/installer-ppc64el/20160630/images/MD5SUMS d69e675fb6cd05e602bc48a5d687cd2a 972 main/installer-ppc64el/20160630/images/SHA256SUMS 2189eb589afec2cd0f0cd91089900aa5 576 main/installer-ppc64el/current/images/MD5SUMS d69e675fb6cd05e602bc48a5d687cd2a 972 main/installer-ppc64el/current/images/SHA256SUMS 9a80df21f5bd9c52f25e424e490f97bb 374 main/installer-s390x/20150422/images/MD5SUMS 20355389634104504def0522c0a4f62c 674 main/installer-s390x/20150422/images/SHA256SUMS 4e0e1ede661ac15ee62558ffc11ddec6 374 main/installer-s390x/20150718/images/MD5SUMS 4a3f0ba0fa11852a16356a02d7c65d61 674 main/installer-s390x/20150718/images/SHA256SUMS b7b0380dbafde0465b236bbefc97b084 374 main/installer-s390x/20150813/images/MD5SUMS a04317d84c2090c0b0a1000fcc6343f9 674 main/installer-s390x/20150813/images/SHA256SUMS 380794fd117148bc3e26705fe2e389dd 374 main/installer-s390x/20150828/images/MD5SUMS 21f3ba7fe04d79569e6a077c5c374553 674 main/installer-s390x/20150828/images/SHA256SUMS 31e504890d163f03b21abb6082a37de1 374 main/installer-s390x/20150911/images/MD5SUMS 6d3d1016f5df0e95773ce708b70f7bc8 674 main/installer-s390x/20150911/images/SHA256SUMS 1e2bb4157cdf43ad7f176cd58d756f1d 374 main/installer-s390x/20151023/images/MD5SUMS c0b251bb4720835ce2f25f38585f24f1 674 main/installer-s390x/20151023/images/SHA256SUMS 0ee1d4c8c85e7850d2b9c1ccf49ae5d0 374 main/installer-s390x/20160101/images/MD5SUMS 40f55c378fe0761416e3052c0db33d1f 674 main/installer-s390x/20160101/images/SHA256SUMS c1fdd09c06b8f99bf1ea705af74a11d6 374 main/installer-s390x/20160106/images/MD5SUMS 23323a568afe8645e589e5fbc4ba2e1c 674 main/installer-s390x/20160106/images/SHA256SUMS e2beb798b9b891099fe05138e6980b41 374 main/installer-s390x/20160516+b1/images/MD5SUMS 21b59fa9b484d049a30cabcc75a3556b 674 main/installer-s390x/20160516+b1/images/SHA256SUMS fe8310a09e76c93b7d32a13639efe7e0 374 main/installer-s390x/20160516/images/MD5SUMS 524f09e9c4e8910eecdb146d3de7d754 674 main/installer-s390x/20160516/images/SHA256SUMS 2355c08000e7e86685cb6c032aa38ce9 374 main/installer-s390x/20160630/images/MD5SUMS eea708fab440fdf7bce3182abe6b1376 674 main/installer-s390x/20160630/images/SHA256SUMS 2355c08000e7e86685cb6c032aa38ce9 374 main/installer-s390x/current/images/MD5SUMS eea708fab440fdf7bce3182abe6b1376 674 main/installer-s390x/current/images/SHA256SUMS 1ead3b3d8b3cb4ffb20a960556cf6df0 105 main/source/Release 0e7952ec17b7235b111d7a5016032b47 35489733 main/source/Sources f52c0103811638eb1388eeb351af4243 27910 main/source/Sources.diff/Index fa83d0a187aac7405285e3cd33aa197b 9072408 main/source/Sources.gz c64e879844fc7c8cd30f88347aae8635 6901228 main/source/Sources.xz 58aafa7955bc6322b4f57c7f5b5c3018 14518404 non-free/Contents-amd64 990b6c32148ca37b7c7aa07eb2949f1f 27416 non-free/Contents-amd64.diff/Index 6cb38c97f16f34a79db97b2bfb936a1f 812504 non-free/Contents-amd64.gz 9a08d553e6aebddee752270c37349262 13465086 non-free/Contents-arm64 f4b9c3bbbbcf805bff50e5b8f22a16e4 19512 non-free/Contents-arm64.diff/Index d1aff0dd8d4fe3a55e06387756ab0c40 731747 non-free/Contents-arm64.gz 9f70f37064a74691ff5851349343d6e7 13478582 non-free/Contents-armel 2a253ee5195c2b122c0174d3050af2de 20500 non-free/Contents-armel.diff/Index 406c5fc71cd9b16c600d88aeef6f9a43 733675 non-free/Contents-armel.gz 7f0fec44ed7dfcfe2964771b2c83525c 13539201 non-free/Contents-armhf 0a3241cb8687807a8d077340af4be04b 22476 non-free/Contents-armhf.diff/Index e7ce03d12d6ae7d25f839a034595d22a 738525 non-free/Contents-armhf.gz 4f7d7a89369a9c286e286859c0fe471b 13374121 non-free/Contents-hurd-i386 c0de361dd201bfdb7ade8971076089fb 19018 non-free/Contents-hurd-i386.diff/Index 7e6b1db1e79f35ee2be131075cb57ad4 724269 non-free/Contents-hurd-i386.gz 4e5031961c48943298aab229cc67a8c1 13755254 non-free/Contents-i386 7cd295a19aeb292d1990feed2f842d43 23464 non-free/Contents-i386.diff/Index 609a7efdd18a827fa1ee767a89da9814 757556 non-free/Contents-i386.gz 428bc6e6ca1470d39fee62c2766edd2c 13514206 non-free/Contents-kfreebsd-amd64 b022ca3c67d39f53aba5d5d5b2f31e24 19018 non-free/Contents-kfreebsd-amd64.diff/Index 9b7d361f085bfa524f03fef26692fd37 736421 non-free/Contents-kfreebsd-amd64.gz 7846ef7bc79eacff2f07ab2472b558f5 13515299 non-free/Contents-kfreebsd-i386 d839f9e53bb550ed6138f70147c856d7 19018 non-free/Contents-kfreebsd-i386.diff/Index 7b8e46a5df5e075cd606c818dd1455db 736493 non-free/Contents-kfreebsd-i386.gz a1a52318fa404fe3d51aef8281bff12b 13468449 non-free/Contents-mips e86bdef249071f965d7134d5b1e417e0 19512 non-free/Contents-mips.diff/Index 3df62209f599304456ee8665c92e0c8d 732565 non-free/Contents-mips.gz 9d3241068cc75b8480b457a51854f897 13465548 non-free/Contents-mips64el bcd527f4cf51d2aac5e8f647267b35b3 18030 non-free/Contents-mips64el.diff/Index 34703e3fbd05080c4ed72c0b99773997 731477 non-free/Contents-mips64el.gz 0fb464e5128d6bd4173a678ef5bd6c64 13479076 non-free/Contents-mipsel 4acb6639264d35253ca940fed332e59c 19512 non-free/Contents-mipsel.diff/Index cc0dc451b315c016022bdfd04b540a14 733446 non-free/Contents-mipsel.gz 6f430fbb40d213488eb2cfdadb17ade8 13467317 non-free/Contents-powerpc 489d298e3b7044f82a7f1ff0d074c131 19512 non-free/Contents-powerpc.diff/Index 07a79847ad475e8570bbb5575171bfa9 732439 non-free/Contents-powerpc.gz 72740ef0a54bf294ec73677123334a44 14067773 non-free/Contents-ppc64el 8eac145ea2dc0966125d183c8613444f 19512 non-free/Contents-ppc64el.diff/Index a953fc4a17cd0c0023c20a69638492eb 774292 non-free/Contents-ppc64el.gz 2866dc930f991ecc6cd4af049a220445 13475399 non-free/Contents-s390x f12a646b4a8c57277da1ce1a8db96ed4 20006 non-free/Contents-s390x.diff/Index 291d364ee21cee9216bd7a2f1bdf51a9 733090 non-free/Contents-s390x.gz ce45d44323ceffdbca768c2f4f25f1b0 8203337 non-free/Contents-source e1fdae378171cb5f64c6d0128d5e70f5 27796 non-free/Contents-source.diff/Index 27a2aeaebcfde059706aa256c2ae8398 878132 non-free/Contents-source.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-amd64 b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-amd64.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-arm64 b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-arm64.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-armel b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-armel.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-armhf b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-armhf.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-hurd-i386 b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-hurd-i386.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-i386 b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-i386.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-kfreebsd-amd64 b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-kfreebsd-amd64.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-kfreebsd-i386 b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-kfreebsd-i386.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-mips b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-mips.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-mips64el b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-mips64el.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-mipsel b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-mipsel.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-powerpc b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-powerpc.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-ppc64el b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-ppc64el.gz 68292125276158d468c91a58be0f30b1 451 non-free/Contents-udeb-s390x b49002146fad4eb7e4dce23bc3f48e38 271 non-free/Contents-udeb-s390x.gz a4fced351b7a9ee638a35475019f373d 194055 non-free/binary-all/Packages 84ef1554bd13e4f7265586ae3cc2a1d1 52716 non-free/binary-all/Packages.gz 1e890bc84314e880a30769b446b66e21 44872 non-free/binary-all/Packages.xz 6b4250a4eecedfd2aa185a72c35e068d 106 non-free/binary-all/Release 703be0e6bda36aa7ca226d1349c9a18b 372384 non-free/binary-amd64/Packages 07b6b19ff9fab45b2f68311fa15d051e 27796 non-free/binary-amd64/Packages.diff/Index 6d16aece55e77beb6cba6a6f0b947c09 94091 non-free/binary-amd64/Packages.gz debdd06bf579c3fb42f85f7b4268d164 78516 non-free/binary-amd64/Packages.xz 51280d23e2848a2f94270b448880dc07 108 non-free/binary-amd64/Release 602432a5f5800c8fedf44dddcbb4f2ea 228434 non-free/binary-arm64/Packages da7a4a3d93a2e9d496b20e89c327e07f 27796 non-free/binary-arm64/Packages.diff/Index 2b9040d3ce03ca155b25f3ab3e4839b6 62112 non-free/binary-arm64/Packages.gz e5452aaea83dcb29a2c5693811836e1a 52564 non-free/binary-arm64/Packages.xz 283b50d104942e828ae94f7d33cb2881 108 non-free/binary-arm64/Release 4810004f68789de4392cb741faf9bf07 235109 non-free/binary-armel/Packages 33b93ad35bf0dafb6775d54b97807b92 27796 non-free/binary-armel/Packages.diff/Index 59aea2b46f5b236f240c3f2130efaade 64174 non-free/binary-armel/Packages.gz bf1146a16f5f61261f082461b2cd28c5 54144 non-free/binary-armel/Packages.xz c3554a3861dcf3fc787aea5e23f1f059 108 non-free/binary-armel/Release 7dcb40c3bc6c3ba6fe9561b245720641 275515 non-free/binary-armhf/Packages 7429fff5c9e6d55007de8443a5505b94 27796 non-free/binary-armhf/Packages.diff/Index b529a99048903f1df03794f226c15330 71048 non-free/binary-armhf/Packages.gz a484a8e5281b93ded782820358054bbf 59664 non-free/binary-armhf/Packages.xz 994fe90c27992fac81995cd3fcc71d1d 108 non-free/binary-armhf/Release b89850fe04095051eef6fc2af4fd3db1 232894 non-free/binary-hurd-i386/Packages 661a10c10925b1352dcf45418639be97 27796 non-free/binary-hurd-i386/Packages.diff/Index ce78986f71d2a59a4f468bdb414060a0 62661 non-free/binary-hurd-i386/Packages.gz 6e420d06c496326aaca1d86c1f3fbe59 53192 non-free/binary-hurd-i386/Packages.xz f982a54eecaaff5105fc67afbd737441 112 non-free/binary-hurd-i386/Release 44b55f5cd7d84bfff6966d982b42ccdc 337604 non-free/binary-i386/Packages 6b86b3eb6dbde16ec7bd882061c73e73 27796 non-free/binary-i386/Packages.diff/Index 647a330e6b06b7702ba7c93e46ca07b0 85001 non-free/binary-i386/Packages.gz 9ea96c26fc6fda3d4e162e654a8d73e4 70972 non-free/binary-i386/Packages.xz fa526d0f76b842e864dd98295084bf1b 107 non-free/binary-i386/Release d3c299bbc70d6c7c38bf9df17344bc44 238159 non-free/binary-kfreebsd-amd64/Packages e552627a9086ad5fee01012a55bac259 27796 non-free/binary-kfreebsd-amd64/Packages.diff/Index 6389516ec9a9860892fadab427aae47b 64110 non-free/binary-kfreebsd-amd64/Packages.gz 1e9b5c2b716a5d5d1793e7383237ca8f 54172 non-free/binary-kfreebsd-amd64/Packages.xz 589a0d6328a246508c14362fffa65259 117 non-free/binary-kfreebsd-amd64/Release 91f9c8e8222d29ce84c653b11a5a56fe 238774 non-free/binary-kfreebsd-i386/Packages 69cdeb202295c8d3ec9c35f88edf4085 27796 non-free/binary-kfreebsd-i386/Packages.diff/Index 3aa0ba33f1579d5ff39de8e62dbcd7fb 64166 non-free/binary-kfreebsd-i386/Packages.gz 2465604b8cb9d3242db3c36b3be09488 54388 non-free/binary-kfreebsd-i386/Packages.xz 0b463c3a00fe3ebd875dd7795faabe17 116 non-free/binary-kfreebsd-i386/Release ac2f4e224e6abc968526cfb3f7ac32fd 232072 non-free/binary-mips/Packages 91842a9c9b311fae669060f8ba37c0fe 27796 non-free/binary-mips/Packages.diff/Index 8e3883d86f2e273c0f4489f74b95af7f 62918 non-free/binary-mips/Packages.gz 2134132b120ed581a67a235276292886 53340 non-free/binary-mips/Packages.xz a5146fc4bacdd167cc7be86231e52b89 107 non-free/binary-mips/Release 9e385da97ab1c5a0577f14e88de5e43b 229428 non-free/binary-mips64el/Packages d9e178b926025e57fb1f2347357c4416 27796 non-free/binary-mips64el/Packages.diff/Index f3afc71b380945324c27680c2522978e 62084 non-free/binary-mips64el/Packages.gz dd3217bfdcd0fad42a134b059c64b538 52544 non-free/binary-mips64el/Packages.xz 21b7cbdd20ea26dd83b31d8c3842f895 111 non-free/binary-mips64el/Release d953db2519456105ed385d3ff8ed71ce 235706 non-free/binary-mipsel/Packages 247cd4912fef0b89161407eb4b89b682 27796 non-free/binary-mipsel/Packages.diff/Index 4bf19d62515ac650088f10e64a7b5022 63921 non-free/binary-mipsel/Packages.gz 7233cb9bfec2c71e83cf1db246f09ba5 54036 non-free/binary-mipsel/Packages.xz 0f7da028e5005e5d19386284091a326e 109 non-free/binary-mipsel/Release 79f8939746dbdc7617efa22ff06d83da 231104 non-free/binary-powerpc/Packages 5377a7223ece6f27a3916ccf14c52d9d 27796 non-free/binary-powerpc/Packages.diff/Index e09d405b7aea1d80e0dbe97da2b01849 62821 non-free/binary-powerpc/Packages.gz 838370756a541c617efd97d5237652af 53196 non-free/binary-powerpc/Packages.xz 3c481bd491603b28ccef0dc33accdeac 110 non-free/binary-powerpc/Release 6fe1371819def349a4f6d6dad6309eb3 255982 non-free/binary-ppc64el/Packages ad8ded9ddda4f792725ed70e6cf77dd4 27796 non-free/binary-ppc64el/Packages.diff/Index f7e5fed4fce50cb61338d0218bac83fd 68178 non-free/binary-ppc64el/Packages.gz 640207f938532cdfcc19a3b12d587c2c 57476 non-free/binary-ppc64el/Packages.xz d4aed8973b2fdb4ebe2539a352911330 110 non-free/binary-ppc64el/Release cccf31ccee4d9552ff84ef1168890b2b 232449 non-free/binary-s390x/Packages 44b46d2a337dd0a2975fd5dd877c88cd 27796 non-free/binary-s390x/Packages.diff/Index fcf0c878aa0e389ca495d118f4df3383 63088 non-free/binary-s390x/Packages.gz eef11937550a01455d59b2b0fc465ee1 53436 non-free/binary-s390x/Packages.xz e7ceeb59c6faba0761d4bec329d3d26d 108 non-free/binary-s390x/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-all/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-all/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-all/Packages.xz 6b4250a4eecedfd2aa185a72c35e068d 106 non-free/debian-installer/binary-all/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-amd64/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-amd64/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-amd64/Packages.xz 51280d23e2848a2f94270b448880dc07 108 non-free/debian-installer/binary-amd64/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-arm64/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-arm64/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-arm64/Packages.xz 283b50d104942e828ae94f7d33cb2881 108 non-free/debian-installer/binary-arm64/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-armel/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-armel/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-armel/Packages.xz c3554a3861dcf3fc787aea5e23f1f059 108 non-free/debian-installer/binary-armel/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-armhf/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-armhf/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-armhf/Packages.xz 994fe90c27992fac81995cd3fcc71d1d 108 non-free/debian-installer/binary-armhf/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-hurd-i386/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-hurd-i386/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-hurd-i386/Packages.xz f982a54eecaaff5105fc67afbd737441 112 non-free/debian-installer/binary-hurd-i386/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-i386/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-i386/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-i386/Packages.xz fa526d0f76b842e864dd98295084bf1b 107 non-free/debian-installer/binary-i386/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-kfreebsd-amd64/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-kfreebsd-amd64/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-kfreebsd-amd64/Packages.xz 589a0d6328a246508c14362fffa65259 117 non-free/debian-installer/binary-kfreebsd-amd64/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-kfreebsd-i386/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-kfreebsd-i386/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-kfreebsd-i386/Packages.xz 0b463c3a00fe3ebd875dd7795faabe17 116 non-free/debian-installer/binary-kfreebsd-i386/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-mips/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-mips/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-mips/Packages.xz a5146fc4bacdd167cc7be86231e52b89 107 non-free/debian-installer/binary-mips/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-mips64el/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-mips64el/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-mips64el/Packages.xz 21b7cbdd20ea26dd83b31d8c3842f895 111 non-free/debian-installer/binary-mips64el/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-mipsel/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-mipsel/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-mipsel/Packages.xz 0f7da028e5005e5d19386284091a326e 109 non-free/debian-installer/binary-mipsel/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-powerpc/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-powerpc/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-powerpc/Packages.xz 3c481bd491603b28ccef0dc33accdeac 110 non-free/debian-installer/binary-powerpc/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-ppc64el/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-ppc64el/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-ppc64el/Packages.xz d4aed8973b2fdb4ebe2539a352911330 110 non-free/debian-installer/binary-ppc64el/Release d41d8cd98f00b204e9800998ecf8427e 0 non-free/debian-installer/binary-s390x/Packages 4a4dd3598707603b3f76a2378a4504aa 20 non-free/debian-installer/binary-s390x/Packages.gz 8dc5aea5b03dff8595f096f9e368e888 32 non-free/debian-installer/binary-s390x/Packages.xz e7ceeb59c6faba0761d4bec329d3d26d 108 non-free/debian-installer/binary-s390x/Release 0d8c70d4810fc47c5574ea213a9c640d 47445 non-free/dep11/Components-amd64.yml 61137cbe5792a2e5ba604e230e6b3000 7189 non-free/dep11/Components-amd64.yml.gz ff2ef29352315f747293e44ddaf8d8ff 6624 non-free/dep11/Components-amd64.yml.xz 4c111a07a096bb22ae344787a2a0b5da 45122 non-free/dep11/Components-arm64.yml 0a2b11b7f8c0ace3bda202507458239b 6383 non-free/dep11/Components-arm64.yml.gz b857ec04a99d2bd5d2fd7918d5336378 5860 non-free/dep11/Components-arm64.yml.xz 846fd7550c016f1f937f5c169ee2ef73 45122 non-free/dep11/Components-armel.yml 2b41081c0d486828af96cc9ea91df4a7 6421 non-free/dep11/Components-armel.yml.gz 771901c6f9faf59ed153e341ecc5313b 5868 non-free/dep11/Components-armel.yml.xz 1d9f53e51cd5bea731edb5b2ac54899f 45122 non-free/dep11/Components-armhf.yml f0a9a5d74b7c9b32df0bd72af83edb50 6463 non-free/dep11/Components-armhf.yml.gz 2d076be34424c23c50d059632ec94fd2 5884 non-free/dep11/Components-armhf.yml.xz f1514d4dfd5377908c95ef1b5be233d4 47903 non-free/dep11/Components-i386.yml 9889d80e20699cbb3a7e4b8b63ee34ab 7512 non-free/dep11/Components-i386.yml.gz 63ca74d5181dd5e45f1ede237192437c 6904 non-free/dep11/Components-i386.yml.xz cf0a3a824d5b36b88fa02b39e232594c 46201 non-free/dep11/Components-kfreebsd-amd64.yml 66bc6b47f324b213c3b0bc3001adc1d1 6927 non-free/dep11/Components-kfreebsd-amd64.yml.gz 8b9fd3566dfb573bdfcb33adb28a51a3 6344 non-free/dep11/Components-kfreebsd-amd64.yml.xz 04004d656d3eae5941928412bad31c9b 45122 non-free/dep11/Components-powerpc.yml 3a8feb831b6dcacb673e0ab3ccf66580 6441 non-free/dep11/Components-powerpc.yml.gz 3c5bc01d9c759c40e202dd953dbe3979 5896 non-free/dep11/Components-powerpc.yml.xz 9999642388b37c5410858bbfc5ded8cb 30720 non-free/dep11/icons-128x128.tar 9d4f8d008f7bfb01e7a0cb59c2e284f9 20291 non-free/dep11/icons-128x128.tar.gz 5591cd3a317cc78d9c2e3e9097b6c167 17920 non-free/dep11/icons-64x64.tar 3b9db0b0b6fc27c1fdfda57f249bc87c 10022 non-free/dep11/icons-64x64.tar.gz a1bf9aa6de9a10af1f4128004b077cf0 374836 non-free/i18n/Translation-en 4457f974180756e905bcb6adbc8dafc0 79968 non-free/i18n/Translation-en.bz2 207d7da0217dfe95433d5441e3e0403f 25336 non-free/i18n/Translation-en.diff/Index d90d6c59bcfbf244d26d38dca492e911 109 non-free/source/Release d4192792d76738466a698895212e1126 344837 non-free/source/Sources 109518eaae9db1e0ca222a21cf2d0e48 27796 non-free/source/Sources.diff/Index a6025e53a124caa1909653747769499b 98981 non-free/source/Sources.gz db30aa8f2a582e5a7bc17ce090390460 82868 non-free/source/Sources.xz SHA256: bf829b9d7e546bbf1db28ddf3a73e77f7f93f67b91ae20d18ee1529302414f35 1318264 contrib/Contents-amd64 ab5bca923318b59d331499317b25c30f901f2e47d31dd5cdefe0eecf25477dea 23860 contrib/Contents-amd64.diff/Index d8556a0196c369faecf88c179a69cc40c47d415cbd88fe3ed38a998dd4e48fe5 101470 contrib/Contents-amd64.gz 9b36e83393744044bc4571bbeedcb0ecb498211a754414dd9f685caa0a5732e4 1175578 contrib/Contents-arm64 7fcfe688e3a1405f84ccc52fa1c37972df1050ff4c75614d14f67437d0404c23 18448 contrib/Contents-arm64.diff/Index e6a6f730b083d16d01ff2b373e2df4c88bd3b741d9f05f4541577d5d5040687a 87797 contrib/Contents-arm64.gz 1a6477d6fd5c244477f6e677bca783d66c0e121f845eabfd286030c51cfd460a 1177832 contrib/Contents-armel 7f9f1465482feff3671cba39d3498ecb64aac75d48d2011464f3cf33f05068e6 19432 contrib/Contents-armel.diff/Index 382df57a2efbd5baf8e4d53d200c3b1052a7bd56f044cb9f3a9a7c24687ad4af 88158 contrib/Contents-armel.gz 61c5a2da5a2fe2f7c55677e028f0a16c850a2494aa23c40ee1df0a678836386b 1164602 contrib/Contents-armhf d17a8ee62e75f8b9443ca6dda19fa60a585dbc94d5fcae9d7d64a733d413fe12 19432 contrib/Contents-armhf.diff/Index 5bb87109f97302af2c0ebf4265a847a40abe9ab07f1a363ed3f9ebbe32544cd1 87806 contrib/Contents-armhf.gz 76621d859ae45809fd32e16dafcdbd9d006e9ce3e3ece25b98a8e535a2326dba 1103107 contrib/Contents-hurd-i386 d7b5b0d20ae4ccaadbd1b9e22d485d8bb03c5d4e491303b7f91f792e2d66c982 18940 contrib/Contents-hurd-i386.diff/Index 4595035baf79710a4e815a97b8a5ce958eedec839acf0f9fd916d469d29557b4 80883 contrib/Contents-hurd-i386.gz 78cac2715a0248ec8cf654e5110f868ad8150b945bbf358bcf59f9c22708bf31 1290908 contrib/Contents-i386 51577f70807bc460f53804433cc04164a0a61ec8dc5b1885a9d28285b25eb883 23368 contrib/Contents-i386.diff/Index ec1cdfa5e88caa54d1c9bca8549cc2d3750b2ec6791de88793c89b434362bcac 99126 contrib/Contents-i386.gz 9037e8faf877ef7e6896dd775d15a8e19b6ff7495ce3882d83b0782536257632 1110069 contrib/Contents-kfreebsd-amd64 a5ec69d36e16d7ee56e6efb20f2245786a656f3dd2931656a350a2e2f0dc9f5c 17956 contrib/Contents-kfreebsd-amd64.diff/Index 7f42c4aedcf65663a9b836a87fd9e3008a53f7167e08ed56221a24f46d33b48e 81795 contrib/Contents-kfreebsd-amd64.gz f9cf27931323ef075571a3dffca5dcf83977970752c7d55fc39ca791183c2b6f 1109182 contrib/Contents-kfreebsd-i386 9fdac1b056fa31a758d23e24056cb55043261399f32275f8c53de297a8507009 17464 contrib/Contents-kfreebsd-i386.diff/Index 45429180529863f1a8cb7cc764aaaa97260bfa5033327f9f7d0fedb77a3242e1 81545 contrib/Contents-kfreebsd-i386.gz a794935674b9287803b6ed7c03bdb91a1fb03b3f23219980a2fa4f8c142222b8 1159398 contrib/Contents-mips 7e81b9f46f8c9290cd83a96154b65c372680af41bbe36c8f6b25141cfdc4808a 19924 contrib/Contents-mips.diff/Index 743114afe75f74144de2e1874c711cf32ff254e7528dde43b32caf9160ec69d0 86916 contrib/Contents-mips.gz 9375ad4cad79a2e584b5b56ffbc0d37b188036337d2b9978b9e9659e9d38dd7c 1151939 contrib/Contents-mips64el 97281609deaa3ca2adb23d5e6dd31c2481ae7468b0d1818da349bac1e9f6a5d2 18940 contrib/Contents-mips64el.diff/Index 8808223dee98ff200d94afba90b7e17d5382205b63489897f5b266bd839be261 86243 contrib/Contents-mips64el.gz a7248cf41cf4f7582308b0a19faf1e140e8e0bd6d8027a9e3f609afead138532 1159421 contrib/Contents-mipsel b687657154fd666d6845dc685622831a80c9815c5740d2af7bca08633c719d0f 19924 contrib/Contents-mipsel.diff/Index fd3fea533b1368c2ad5b2f74105959b351240566ae6e739f0b183a6c268c8545 86916 contrib/Contents-mipsel.gz bb6c8927ba12948726e4b4c0f70ab8c7b8c1be973dc2f4e6b044d1ff1936967c 1180561 contrib/Contents-powerpc a99de55e3fdb31a71a6dda91e1f03a104d3e2bb0ec61cb1a6a189d2703fd8e31 18940 contrib/Contents-powerpc.diff/Index 0fabc143d764e168c95874d711c97358a224a74988760551f93d4f50733e67bc 88488 contrib/Contents-powerpc.gz 20ac4bc872eca554d3068e9a643bd0ed16fe678bbaa694e58286673f12beb71f 1142562 contrib/Contents-ppc64el 24d692bce4db68b1baf0bbeed30a8a87ef33efca469336c0c2a73c0df9f12e7c 18940 contrib/Contents-ppc64el.diff/Index f81f1599796af960f64a1ab726b01aff8023e5e6ec8f058ba9fb05906e1441d9 85835 contrib/Contents-ppc64el.gz c0807bca647b9e0f4089125fad517bcbf1d5bd28afedddc9df7492fb6428fbe0 1155432 contrib/Contents-s390x fe9b1970c85a4b42f2b3209fec732f746492ed5e2ae1ac2a36e956452a373a8a 19432 contrib/Contents-s390x.diff/Index 185ea7e3893ce2f835835d45d79984aa574f728a708ee417859932aa5f901794 86413 contrib/Contents-s390x.gz f260d59581ec8a6186a656334880144cdfbc3c6bd930191dcc9c815890259d76 3519818 contrib/Contents-source a6e0b88b478d587aba4387c9c69460bf86521ed9903a9c02c98e40c423b138ab 24352 contrib/Contents-source.diff/Index 9526c7c619de2b5ff3dd4b7f83a71bd2fc60c46c67bd687b754ace8de084a9e1 389700 contrib/Contents-source.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-amd64 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-amd64.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-arm64 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-arm64.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-armel 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-armel.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-armhf 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-armhf.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-hurd-i386 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-hurd-i386.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-i386 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-i386.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-kfreebsd-amd64 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-kfreebsd-amd64.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-kfreebsd-i386 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-kfreebsd-i386.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-mips 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-mips.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-mips64el 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-mips64el.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-mipsel 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-mipsel.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-powerpc 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-powerpc.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-ppc64el 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-ppc64el.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 contrib/Contents-udeb-s390x 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 contrib/Contents-udeb-s390x.gz 1ea1989e37e40dda9c215e652ac3c935d6649f0a0914419e35177ccf68e3ab25 90903 contrib/binary-all/Packages 463e11941e379695305bc633fd891c832180e2a71d821eea09cb514d503021d5 26745 contrib/binary-all/Packages.gz 39c2cbecebdf636b1358becfa8b06f21087572a4e3c84124df9f72efab09a926 23788 contrib/binary-all/Packages.xz ee0fc1c54e8d7dd16c06529baa5cb605ae70f9b9e2e46737973b80a2d91e6f31 105 contrib/binary-all/Release 12c0df77c229d0e18c4919b695dd9117fb1689f31530ce2e05c4bc6290777ee2 224553 contrib/binary-amd64/Packages e56d7d0cdfce75a723483600892226977743d907c377bc26104253bf62d67c18 27796 contrib/binary-amd64/Packages.diff/Index f07a086987b992444c215a60862bccbc185c60bf1316e8755abedfcedd158ccb 62636 contrib/binary-amd64/Packages.gz 0f786f1852935e2adaaf5c0e788fc5ca9f70e7727afc3305a3a0ce9a76f5a721 52468 contrib/binary-amd64/Packages.xz 71490e3ed4b8693dbff8fabba92c4269580e5c8ca917687a1179723c60e92dd3 107 contrib/binary-amd64/Release 00b5edada3ad1f90dc26a03a06abec4f1a0ee9d398b62fd9c71a05a780846710 173735 contrib/binary-arm64/Packages 5497271dccea31838151b903f242b47731dfb6c897b370604a1f614fbebf7d71 27796 contrib/binary-arm64/Packages.diff/Index f2c1ee4ef17b110475ea8bcd0efbb26ff0c60e0fc9c33e856804bd8e098ca93a 48846 contrib/binary-arm64/Packages.gz 06c36e7e8bab6ad2f3de2d659d4f4a59676e2e7a1c876c50bb0219d48b54673b 41604 contrib/binary-arm64/Packages.xz 5a270e8dea0fdec6a7ed08da01d439832f5c344909df7bc9ccb9e8ca4c81b53d 107 contrib/binary-arm64/Release b054d3a8fdf7e08073dc39f89f93b181960aace7cbca39c6ff1f8fcabcd4bdd3 175162 contrib/binary-armel/Packages ffd6f56218f57ce7f871af0265d480b013c207e6776dc79668e9da682a9a120d 27796 contrib/binary-armel/Packages.diff/Index a06d6f53c62c8b0600939389b62ba31683551e0855f6db530e5d262af1e6d045 49399 contrib/binary-armel/Packages.gz b1f0dae364c8219ba8047a15218eb03b4670892a5458673bb4b2292061338f44 42060 contrib/binary-armel/Packages.xz 6263dd4ca1f185b99ab37a1469467f45a945e04f36b448e4357643ab3d52ea75 107 contrib/binary-armel/Release def29e3d5b1a0df2266caebbebb94a6891f3565085881fd7276a8741956ed81b 181954 contrib/binary-armhf/Packages 4a0f54805991a15330df30a3ffc40e15f07d50792eceac5e9c29f2a27eb4fd5e 27796 contrib/binary-armhf/Packages.diff/Index 6d1562abdc002652a901a24e5c0da959592d856e58b4a1e1d02b1ed54110c83d 51101 contrib/binary-armhf/Packages.gz 993d0bd9f4390f9813c102dded8b535379d73132bfee180e7b85906f5d441be2 43400 contrib/binary-armhf/Packages.xz 1768bea4edde872aab0d5f086bc86bc256db503127ec434fe8d358617be1b76a 107 contrib/binary-armhf/Release d7c74c7e9a0430fa5b9eac7ba1ae935fae3687d92be253b4151c89953e634dbd 147251 contrib/binary-hurd-i386/Packages 410faf6bdd56fdbb6c86c66b710199d08eeac35309e606ff6c2070b6898c833d 27796 contrib/binary-hurd-i386/Packages.diff/Index fcf5759143512b23025e72283bb21f9ac5a534719a9b33ebcfa0be5db2b53e9b 42491 contrib/binary-hurd-i386/Packages.gz a2c012ec62047685c6c88074a28e34ff3b538936037ca0b527c26e816de75646 36440 contrib/binary-hurd-i386/Packages.xz e68963de7ec8a12061249ab755334be781d8a50ddc50748a317c73280336f569 111 contrib/binary-hurd-i386/Release 267e7aea22b22e99de2c7ba5630841c35ef141c707b08d28105613442905f91b 214181 contrib/binary-i386/Packages 4c737caa1250f6ccfacf44364cdc36549dec65377e365b1363e761e758e863af 27796 contrib/binary-i386/Packages.diff/Index a98040341e6a86f4c389dfa85c8a03dcb75c4177c46b6838c9049394f7ab809c 59731 contrib/binary-i386/Packages.gz eb80caa293ec826e4fbcd40668ee5158a467b15492b9ec9c3fb3d2818ae2cc2e 50404 contrib/binary-i386/Packages.xz 58f7a0b704e8d9ee2e832921e7c192be4787ee5f0c0d7156ff92b8d46aa452a4 106 contrib/binary-i386/Release c948929837d7911de9132656a8e52b3f7987b6e91c6c40926af92654240ade13 156547 contrib/binary-kfreebsd-amd64/Packages e66f6e99a8f7dc319b9bda3403a244a0473912a811282b2c83ca2df327de62f6 27796 contrib/binary-kfreebsd-amd64/Packages.diff/Index 7a6b4a359c792325526b74e3359c654d556d60838e7cb95bdcc3867625c5a5bb 44496 contrib/binary-kfreebsd-amd64/Packages.gz c619628473073f0dad2dea7ba6597fb75808201bca49518348b309a6482cbeb0 38132 contrib/binary-kfreebsd-amd64/Packages.xz cb57e35c4b6f12a1f7748f5a2cc4ff1716bc6b6c41e61a22b8784703716f6c1a 116 contrib/binary-kfreebsd-amd64/Release 47ce85d78a9b1e71faff4f1d824e6b00523167dd3bf0c1654984c8c9a57d06b9 156427 contrib/binary-kfreebsd-i386/Packages d503ee74a30afdc2655f6337c66e84dd4200c2f70548e4df7f61b6a74064c1ae 27796 contrib/binary-kfreebsd-i386/Packages.diff/Index 3dcaf601aee0f58d869f17485195cbde4575df497b70222b3aa5fb7d225bf139 44488 contrib/binary-kfreebsd-i386/Packages.gz 9b78795b34651bd2c1a7e70970bea1553afd625731fff10911d7324f7c87807a 38184 contrib/binary-kfreebsd-i386/Packages.xz 996020567cd141c5c6f913f468bc135dd64be57451ced8d4a05b7c392f7a455a 115 contrib/binary-kfreebsd-i386/Release de9956e00c161bbcb834192e56ccde18d71cb8a6b4118078d25f93b87cd09cb5 173496 contrib/binary-mips/Packages 5c754744e81b140f7e690c701b182e9af00a734458d370e71d54807ebdb0a2b9 27796 contrib/binary-mips/Packages.diff/Index a265202b13f18a6806b3df639675c629e27f6ddc3a05e8b9445079fd478fe4df 48888 contrib/binary-mips/Packages.gz ceb47b947b3aa7e6f487a61f6828be1ca7f3dedaa7247000bb9ca9af5e7919e5 41788 contrib/binary-mips/Packages.xz e49289714a6854e16ead5461848a9957f79ce2f0307542601af39c4c8459ce4c 106 contrib/binary-mips/Release 950648b6bedafaa9022e7e527eb1ce76fd4f60ca92e2bd7f2ae2c313bec2bf99 167584 contrib/binary-mips64el/Packages 4c598a298d38c762d63b94d40564f6a6d2f58cd30ed76603b416a456f22c8b1f 27796 contrib/binary-mips64el/Packages.diff/Index d2edf9f61acf1c2bbbb700b682279411ff18229069e2f31804e08e7941230454 47081 contrib/binary-mips64el/Packages.gz 93035259c23aca915d5870864b552f3fe76839710cdb99cd54948fc4813d9001 40424 contrib/binary-mips64el/Packages.xz 00181dfaa08ecd4abab02a610866aa334fd1a2dad4314e8faa5adcc4427ac80d 110 contrib/binary-mips64el/Release 4ac2a145c535a3943bfbdcea4be99a5a63de1642bb42811a30cb34a921851938 173896 contrib/binary-mipsel/Packages 2ea3f7225ce31cdfb039b587051ad60984f2b2c3a6bcf93377d733874ee077e1 27796 contrib/binary-mipsel/Packages.diff/Index 642ec0434e7d533a201b520346bbac9967949d5286315a4212fada7f6ecadd2d 49030 contrib/binary-mipsel/Packages.gz 274b6236427cd7d94da2d2da55d1251832a6c552a719b53cbeb8e17636d43378 41792 contrib/binary-mipsel/Packages.xz a1bd09097f80e277ad3a05af1f8428ffce1bb52e64bbe6ae3e56f8b2ac22740c 108 contrib/binary-mipsel/Release 3977d5f4dada6ee41dd0b4d4b0468014188952229eb8442d6b6752c9fb064ad0 177845 contrib/binary-powerpc/Packages cb499b93a201f701abcb3228e75941a7b0f4aa2c1cc12b9060400b0c76a0c5cc 27796 contrib/binary-powerpc/Packages.diff/Index 45205d404b8239dae53596c6a1ccf2d5043bb0ec19e586d926c1896b879bfd07 50212 contrib/binary-powerpc/Packages.gz 52b942cae4ee8188c9599f3d8afd2cd3179d270000c57d67f1e013f713ebce27 42644 contrib/binary-powerpc/Packages.xz 181b6b8ee07f298141eb500b77df63ec8acd1bd4cdecb21fd7d14d67670ff19b 109 contrib/binary-powerpc/Release e7ca4866f44abeb26a55f0f07336bf65373af386c3dd9b09d42127c20b7d4958 172740 contrib/binary-ppc64el/Packages 3b2bb1d0adbe3f2639835f5a90f9b38279134c36a2c930ddff65abba9089fc14 27796 contrib/binary-ppc64el/Packages.diff/Index 82b29a770b41b119565f463d7f484a9e6c9444a633fc11df9c823dc61247e63f 48549 contrib/binary-ppc64el/Packages.gz d1523d88aaa31a4d82199d57d318c8fe0be1cbcd3f3fc14ee823384ab072378d 41448 contrib/binary-ppc64el/Packages.xz c6338b206c1fbe67f93d3d0e27d2930c6eca65a5beff7da3424264ba555b9605 109 contrib/binary-ppc64el/Release b9a0264481d72145a597ec381b331c0bb53f7a97cca5727ee41a78558cfd8a51 170484 contrib/binary-s390x/Packages 1238a854a54996aea3fb6a5b1641c31c5aa5145abfdd8a65db061dbd6b94946f 27796 contrib/binary-s390x/Packages.diff/Index 5782269ecdcf30e029cb1dd8b2b91c50f0686d3feaac385f506a7d092c62f544 48250 contrib/binary-s390x/Packages.gz 771f6cb295fd5076a880d48131d137ebe0ff485b567baf66425766d11a98e8c9 41044 contrib/binary-s390x/Packages.xz 0aefde72db93681168f317101bc2f41a38f560db2b2589c4dbc317c5362967ce 107 contrib/binary-s390x/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-all/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-all/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-all/Packages.xz ee0fc1c54e8d7dd16c06529baa5cb605ae70f9b9e2e46737973b80a2d91e6f31 105 contrib/debian-installer/binary-all/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-amd64/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-amd64/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-amd64/Packages.xz 71490e3ed4b8693dbff8fabba92c4269580e5c8ca917687a1179723c60e92dd3 107 contrib/debian-installer/binary-amd64/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-arm64/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-arm64/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-arm64/Packages.xz 5a270e8dea0fdec6a7ed08da01d439832f5c344909df7bc9ccb9e8ca4c81b53d 107 contrib/debian-installer/binary-arm64/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-armel/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-armel/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-armel/Packages.xz 6263dd4ca1f185b99ab37a1469467f45a945e04f36b448e4357643ab3d52ea75 107 contrib/debian-installer/binary-armel/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-armhf/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-armhf/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-armhf/Packages.xz 1768bea4edde872aab0d5f086bc86bc256db503127ec434fe8d358617be1b76a 107 contrib/debian-installer/binary-armhf/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-hurd-i386/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-hurd-i386/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-hurd-i386/Packages.xz e68963de7ec8a12061249ab755334be781d8a50ddc50748a317c73280336f569 111 contrib/debian-installer/binary-hurd-i386/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-i386/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-i386/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-i386/Packages.xz 58f7a0b704e8d9ee2e832921e7c192be4787ee5f0c0d7156ff92b8d46aa452a4 106 contrib/debian-installer/binary-i386/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-kfreebsd-amd64/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-kfreebsd-amd64/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-kfreebsd-amd64/Packages.xz cb57e35c4b6f12a1f7748f5a2cc4ff1716bc6b6c41e61a22b8784703716f6c1a 116 contrib/debian-installer/binary-kfreebsd-amd64/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-kfreebsd-i386/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-kfreebsd-i386/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-kfreebsd-i386/Packages.xz 996020567cd141c5c6f913f468bc135dd64be57451ced8d4a05b7c392f7a455a 115 contrib/debian-installer/binary-kfreebsd-i386/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-mips/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-mips/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-mips/Packages.xz e49289714a6854e16ead5461848a9957f79ce2f0307542601af39c4c8459ce4c 106 contrib/debian-installer/binary-mips/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-mips64el/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-mips64el/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-mips64el/Packages.xz 00181dfaa08ecd4abab02a610866aa334fd1a2dad4314e8faa5adcc4427ac80d 110 contrib/debian-installer/binary-mips64el/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-mipsel/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-mipsel/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-mipsel/Packages.xz a1bd09097f80e277ad3a05af1f8428ffce1bb52e64bbe6ae3e56f8b2ac22740c 108 contrib/debian-installer/binary-mipsel/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-powerpc/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-powerpc/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-powerpc/Packages.xz 181b6b8ee07f298141eb500b77df63ec8acd1bd4cdecb21fd7d14d67670ff19b 109 contrib/debian-installer/binary-powerpc/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-ppc64el/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-ppc64el/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-ppc64el/Packages.xz c6338b206c1fbe67f93d3d0e27d2930c6eca65a5beff7da3424264ba555b9605 109 contrib/debian-installer/binary-ppc64el/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-s390x/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 contrib/debian-installer/binary-s390x/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 contrib/debian-installer/binary-s390x/Packages.xz 0aefde72db93681168f317101bc2f41a38f560db2b2589c4dbc317c5362967ce 107 contrib/debian-installer/binary-s390x/Release 78803324d080a7b582ee0019e2e1e2ad88b81c6deec0fa74640b3ad9c6fa239a 37465 contrib/dep11/Components-amd64.yml 4e7d5b7881a856935b6b24fa3ca0406d8481c864034b5a4a57fef15308e43d53 9645 contrib/dep11/Components-amd64.yml.gz 2061e19433c9f9636aaef0ea231dd5b3652cb599ecb293bf809bb6799d1a8034 9052 contrib/dep11/Components-amd64.yml.xz 8d42504a336fb39b0822c62ea4ad541d101396e54ba445b1e2fb08fc52bdfa7b 33085 contrib/dep11/Components-arm64.yml ddcd1c524806b5566b61b5f40c1ae4f7174c9232d417c5f60147414f18013759 8364 contrib/dep11/Components-arm64.yml.gz 44e353dfad2a5be0a64707c25a19ca07bf43f4e532ac026d348da31004906eaa 7872 contrib/dep11/Components-arm64.yml.xz 040a21a982ea92421bf97e5e09448f2f1b0aa808d9ca40acaa0d86e336627983 33085 contrib/dep11/Components-armel.yml 332ffa4734e92ad7c8feaf398473517b5058b86e99736b5e42692ddbd7771303 8255 contrib/dep11/Components-armel.yml.gz e5c0382a448aec0f5118c3d8d82eb408196ca394959689bac38a44b6b4f2b5ed 7832 contrib/dep11/Components-armel.yml.xz 79e3101f5d6491a7a49ff98323498aaa470d260a0edd04021c8f8cdd6d145b0b 33085 contrib/dep11/Components-armhf.yml c7e6ce867f6b68ccabfdb28be189401578aa5f0c2670c8164cfcf0afd050d724 8344 contrib/dep11/Components-armhf.yml.gz d03bb387c09e6ba87b3081ec501be351345e9232f7f8fe0025fef8beeaf52d36 7860 contrib/dep11/Components-armhf.yml.xz 437e11772ff3b605e6fb72637964ab1f87890dbcb09a24eda6604d538e7cfad8 37465 contrib/dep11/Components-i386.yml a191ae6e249fc6cc2a36b9a8d099c00f896e5b199ea9db9e42a587d63aa130da 9620 contrib/dep11/Components-i386.yml.gz 65823984f864de6cbb01eca72114d9aa5452f94489bb2d44fb14fc4a15ae344c 9052 contrib/dep11/Components-i386.yml.xz 1350606ca684e6b33c10baba53b221417658e6e874dc7dd0c8ba23bf3d0e2cb7 31558 contrib/dep11/Components-kfreebsd-amd64.yml 5aa324f0a3d86754f615547f81a3098946dad126898a114bf3384b1aa25ae2b2 7777 contrib/dep11/Components-kfreebsd-amd64.yml.gz 8ba91b03d935a3c1b27b2502d4b84fd8f4802798a1306ecf9e5f7d31681a8b89 7328 contrib/dep11/Components-kfreebsd-amd64.yml.xz b6f4ec3d75799ba55e273013e16c565435d22cec1f953850ab25b255c894c1e6 33085 contrib/dep11/Components-powerpc.yml 70738901d0e9380795c42f8912a75255875182e85e29f3f0653994a0d37620bf 8314 contrib/dep11/Components-powerpc.yml.gz b372cd613418c5aa2c007c769681d371bcf9d1b9d64f9589f5ee8c08ad1bbb50 7864 contrib/dep11/Components-powerpc.yml.xz 7212d818f92be37551ab43b69c5e8407dbb1fc6d284b87a499f9f2ba2ebe6c46 372224 contrib/dep11/icons-128x128.tar ae92d6c1fad90050aacc63f8ec533470b997b7a3ae1ec2a52eba6dda005218ef 304821 contrib/dep11/icons-128x128.tar.gz c7ad3e1be349e072e5573ca6af62e2ac39d37736a055fc95f3f16b1798690ce5 160768 contrib/dep11/icons-64x64.tar b9c9334927887f5fd32145190e9bd95a36591f93da1ac193a8128c4a20943c0e 120717 contrib/dep11/icons-64x64.tar.gz da44e12b5a6d7558895de021e780837656f411fea9c88b4255701916b947a936 180882 contrib/i18n/Translation-en 6d16a7bc3696e35f6479ed18ba8e90c3b9dffe2147f81d02707f3427b80f84a2 47717 contrib/i18n/Translation-en.bz2 69dec5274c95b0631e486fd3d648df374c1dccce066188db40ba97e0a822c645 12544 contrib/i18n/Translation-en.diff/Index e403c8d0095bcf93eef7d4360dbdb20089048b68afab3d91426ff1e2c8f48d76 108 contrib/source/Release dc48b06a2e96795f1a6f4c45f56c240b67e565f66ce61582bdfd82cf7e17ae5f 189731 contrib/source/Sources ff30ad72c4c4b7570435ae32862f412eaa3c90d3c6985205287ce5c1e38a8eb1 27796 contrib/source/Sources.diff/Index 2a18ad43b0b5c24a64131651e4e7c18dc7b24bdbd9172b05071e6681cf0e21b2 56034 contrib/source/Sources.gz a1548fce14a6d0ac8b2e8aa86ac38a5d90f1b5cb9bacc0ea8592bb321ff57573 47676 contrib/source/Sources.xz 6225e8292f73d39299cfbd47f3b4254ec305a88b2370deafaf1f8592eb52d124 486486404 main/Contents-amd64 b2775e7db3cc971e7df8b51fc12c0dec3ac241cb1f8664d11c04b6d0f546dba2 28024 main/Contents-amd64.diff/Index 098b4d2ddc6c0f72a0e0a326e1f3773df7f836a6aab1022631c2c69c3c4669d0 33741709 main/Contents-amd64.gz 4f2d4cba3efb61f71756101e3c70a74a0d77caf3c2b394dede1eafef6405c325 464406406 main/Contents-arm64 9b8ddf71ca6d99bb0704d498913c899a4e2c1806d08c5ffdecf74c87138fb057 28024 main/Contents-arm64.diff/Index 7e9e7275a41953053551debbf21da8c65679e314b4932f12057f81345872f8c0 32427484 main/Contents-arm64.gz 8230fa913e1e95058d2916706f7112a5847c93f881a217f5feb878360fadb74e 462714353 main/Contents-armel 6fa0fda7a0e268394e70af0b5cbf80402b68cc0782caabef8952cdc6175a061a 28024 main/Contents-armel.diff/Index c1f16fbf62d2e33ffaf7494e23dc48d39717d0c5d0eb90a41a0de45eff8599c5 32372160 main/Contents-armel.gz a1737895549660f27229c2e0d519ebb3e73409f0000a3524952793bcdb7a49cc 465331124 main/Contents-armhf d4ca75f069bd2a71ea4e9466bf9303504b5b97c0308e6f34c2324d325d5c9f68 28024 main/Contents-armhf.diff/Index 338ba9c6ba391348a0f27ab8221b79d80124fd45177a4778d98b4cd034e5b6a0 32551542 main/Contents-armhf.gz 12164d6ca2768ee4eada8214439ae38099507007ca58594fe3f26d5a8c7d17fb 437011535 main/Contents-hurd-i386 d7350f21962e0039b025a8617603732410c75833e3f3237d58816b0d02bdf1ca 28024 main/Contents-hurd-i386.diff/Index 42d44861b008f6db216c3a3ba6567998fceaadad5942242c731549a8da7df6f0 30282285 main/Contents-hurd-i386.gz 7539740c4b23e36bd0eaa28932fe789ee0b3da2655b2d70a92f564e5b17dd703 484515088 main/Contents-i386 b45ff8daf9be2d182cdba5f19a61e9e0f33a51fef65d38c73cd423892ec1e0aa 28024 main/Contents-i386.diff/Index 90263a4a8f933e0ac015a657c2f02398d003380248f2289e5c4ce5753e206a61 33649417 main/Contents-i386.gz bdcba9995f0782c9243f7f735172485c09719357cce8a15a243827ac3d3ae750 449136892 main/Contents-kfreebsd-amd64 49a32acaf40b0e20393786fda93abdce653df535f8b5cb76eed693912d4bde40 28024 main/Contents-kfreebsd-amd64.diff/Index de09420766148813556cbbad3871290a355a67a28b1ab63948ebbddaea6192b5 31341686 main/Contents-kfreebsd-amd64.gz 75ddb564e556de94bbea1bdfc368ff38011e49eec4f4d8834c6b99a722549e76 448627460 main/Contents-kfreebsd-i386 3976553adcaf1deeb11acd7007769542a27c88b557867eeaf7abf0714f22a80e 28024 main/Contents-kfreebsd-i386.diff/Index e932f6b3a78ce076c567e402ff94a6f9d760015ea3ded01c8ca191d94c745e0f 31305004 main/Contents-kfreebsd-i386.gz a15462d0ceb1767fa8ec6e417892435ac9aff42270aed4f368eeeb7a80f9159b 460036580 main/Contents-mips 321f2afa5a49320dd1eaecf1faeec7686d7a1770272be2ef5a8ce6265791812f 28024 main/Contents-mips.diff/Index f894fbe0c5b6a5b9b166faa760402debab16abc724e2449dfd13fe5023e3ca19 32236221 main/Contents-mips.gz 5198504cf1241a5f6ca4ae597b9a8b1c2cf8fa59385f50cd8afcb21034c66971 459438445 main/Contents-mips64el 47d5a126188417ed83fb74c096e3e503a9d0bc3756bc1f92144f7eaa5a515791 28024 main/Contents-mips64el.diff/Index 8ee370da3a1e6b9a3f7632a41e5d8c54a605268baf5431906e62edd4359e048d 32030589 main/Contents-mips64el.gz ca351edb83bbc6173e4f54329ea358d52fc7a8a5da14a29ececc87d58f960e19 465041084 main/Contents-mipsel 603df43e401e4a0eb1f4a2c71b111925d37604eabedb33742b756eb928a8414d 28024 main/Contents-mipsel.diff/Index 8bf4724562bb6da2a84c536a1c81215f60c2f3c824d44e23abe34b35b5f7c96d 32541643 main/Contents-mipsel.gz 9ace3630938979046fdd74a844369edcb1f3ef2189cba2b31802cb20a3f51ac6 465654055 main/Contents-powerpc 0715a66ffe4877045aea9fa219b383ae9f4885d8240f0c09eca075aa52c7facc 28024 main/Contents-powerpc.diff/Index d407db7ddf9defebff35daf0d7cdb8af7b3a09bf5ba250b00133dd9f1adcf38b 32538984 main/Contents-powerpc.gz 71f19e382c618b8718ec6e7b2ef852d9b9410879010d15f4e2dcc29e74ce5291 462882892 main/Contents-ppc64el a117a8b76916ae3f5abb0fbe4d782892ece677b9880e81c54f24b1231d89e26c 28024 main/Contents-ppc64el.diff/Index 6299082dabeeff4059fc802447ec43ef7cdae2f9072b7e83d9b503092aff8873 32352649 main/Contents-ppc64el.gz c96502c6d78173e932c1998414d753ecf19e4e903f273360834e4b981857722f 459302164 main/Contents-s390x ef600d65d3c84bbf294819c62d7e586b62ee84b3c65a3b3fdc1a9062e69abd28 28024 main/Contents-s390x.diff/Index bd69285fb34ace9eff6e059d94b4f5db47d2ff2c4c4b68deeb352797f476618a 32174741 main/Contents-s390x.gz 151abe91c34c7ba0f9d7ea77bf3d447712dfa558c6d903427b0b56c498067049 482453699 main/Contents-source ec81b1518bda86285179b3a39914fd23946572031b687206d63106b97b92f8df 28024 main/Contents-source.diff/Index 2f38e5ed5a80d3ab28db61cd585b957bf4a53937682dccfbfe76c076dde0a311 53745770 main/Contents-source.gz 87b1f789988db6646307ce219b6913aef080672629c6c838d3e7023123e7937e 484751 main/Contents-udeb-amd64 af8ee15ec674e995fd85d9b5cfbc781b00af18c3267f19a8c43d00b1f5710741 38535 main/Contents-udeb-amd64.gz 119c16a23678425b7d07faa91740d81c3cf33a7112f3574b34597319b7773a61 419372 main/Contents-udeb-arm64 c964c023c67a2557ccf91b163b6662e199b35ab4c553b11e1169df31da5460df 34115 main/Contents-udeb-arm64.gz 630cb57f948c63dcd57b173b1e587c3daf3f072b625efda5bd6e47314ab6f5d9 414623 main/Contents-udeb-armel 3fb372ba3c72606f9ba433c20a0f3be6279d86135b174965335101cee6a2515a 32491 main/Contents-udeb-armel.gz 2948576d7aff0f15b6d3c2b274661cad417c1ca5e86618a646d22df9b48bb8ed 504043 main/Contents-udeb-armhf 1a4f163635930d02305aa4f52125da4b28a2de5728efb542d674810e5e75d521 39965 main/Contents-udeb-armhf.gz b292917984474cf3a7d58273af21949eed87a1c2e88a0346a9315046b375dbad 266208 main/Contents-udeb-hurd-i386 f935fd65dd1de5de4a8d3b2cdc55d47fa27e70aaa44efc66252adad89d2a8a4a 22517 main/Contents-udeb-hurd-i386.gz 998617b62e2cd1cfc818d0c4d07e07b2c5942c1cedefb699569d779c6f95000f 710802 main/Contents-udeb-i386 7cc1a2777814d21705cac5f5bc4229b7dba2aaa69b29d088bc73d2619ee67a80 54082 main/Contents-udeb-i386.gz a948334097278469ad5bf3dc366f8f525d7ebddb0ffae8faecf3827ef67ef24d 289951 main/Contents-udeb-kfreebsd-amd64 55c62e343faa3f8cb5f5ff4df5085fa05feafdcdbb0ccecde2a4fb89b980ed35 24182 main/Contents-udeb-kfreebsd-amd64.gz a9e7d3a6342280744a286310ee0b9ae048f56b329896f2909f376a97a39f30ab 289869 main/Contents-udeb-kfreebsd-i386 740e1b1712b39d3dbbc2889708b0f933b5d728a61d8a5866217075769d907f87 24244 main/Contents-udeb-kfreebsd-i386.gz 5a49f76c89934dccc1b80ba58aad231bacfd7263f4be50de1b74c9db725275ad 577017 main/Contents-udeb-mips 928fa491edc0df94fab8430f5b268542f057b312c28c9487507b2dda68b43d41 44213 main/Contents-udeb-mips.gz 047c1d01fdd2a46d26731c277ed0bc1c7d5bf17846b02ad394ff0b5e47202425 723583 main/Contents-udeb-mips64el 501ebe45aa5e0b6868bfc10702d14b40b5c0fc7ae77e2aeaa28b78ea7ed62204 53323 main/Contents-udeb-mips64el.gz d11db1a1372711ac5b0cd4f100176b9cd6ed9f61165b108a674f1d44d7dc4119 1008989 main/Contents-udeb-mipsel baec12d4d9cc94be881b304becda72a351b5945360631c9dc548cb8a59e02a67 71028 main/Contents-udeb-mipsel.gz 3c2c2365976c953d4d3197688f0f93a3e5801930f17c3069d67e4f4c6d845459 583828 main/Contents-udeb-powerpc 05a72e24f60d033fa4129501b838382a1ed6f10e49bedc813430a6484d4305bd 43594 main/Contents-udeb-powerpc.gz 645c1ffb53890a79a889ce0620a26497db79ea26258d739a2c0a116ba482ac53 396155 main/Contents-udeb-ppc64el b978f738e7d73e9368a69eb28d6b316a985e65bc5d2b6e8c470b002604b397d2 31809 main/Contents-udeb-ppc64el.gz 2f25f40a1fc13ccd5facc9a6af3821ba04dce9eb4dadab15ac4764437bf83bd8 307502 main/Contents-udeb-s390x 825cead37f987b4432724197cd8cc95cbd33e86c5da6b18b3ad94fa7bf11c353 26131 main/Contents-udeb-s390x.gz a852b64970a8ffe3b26ea70c9672af5b8ad2f135ba404b88606509568d8b527e 17781690 main/binary-all/Packages d8d49a3ea4ffed502df05687f1aadb88f59810c5365bb3c2634d32b793278126 4391664 main/binary-all/Packages.gz fc36847abbd1e503068c1fd933490d4d2d1088e4ddca65f771ff5daec226c121 3337812 main/binary-all/Packages.xz 426d4a8e95369e5d97171b4e8e5dd73a1039231d256e35cfd223bf572be116ed 102 main/binary-all/Release e7a8f1dd5b54a1600a4bd3505e9f4ded10cd3017f47dd79e3aa0e4eb102b788f 39256459 main/binary-amd64/Packages cc5ec05a29811cac40dc601b31c926063061d985ae83bda66b27b4109ecc0d8d 27910 main/binary-amd64/Packages.diff/Index fa0a858a78634103379a48636ad32983e253bf693488519f59bd82d231d89e64 9576288 main/binary-amd64/Packages.gz 24f6cf9b24e3d5f10b297dcc25c12e323258130625e3ec7de51419b66d2350b4 7147528 main/binary-amd64/Packages.xz 07cb692d133dce21dabb9e42201b8e632a7c6a069b53f9ae8173a4ea24606da9 104 main/binary-amd64/Release aea1f7ba403d6f839517644d48f224cbfcc2124f14825f25b93bb7116ca46e57 37571520 main/binary-arm64/Packages 5ac35ae5d5d97ccd2c662fe3a52f16cf59703f534fe254f8185582827aad1ab7 27910 main/binary-arm64/Packages.diff/Index a24fa93bdd882e9b860957b7411b8230ad59030cb3e8a7a2ffe40fe20ff40e13 9227318 main/binary-arm64/Packages.gz ee29d1cce5817c25376ba3319d359d370c8122ec842518d4a78f7195c0887068 6884520 main/binary-arm64/Packages.xz 16b43705f8420470a43f96954eb7d00a75c4408a36c1b86ccf2bd6cfa4ebfd98 104 main/binary-arm64/Release 5518a06473c0cd4edd4ccf12fc4d56397571887e3817b3c86186a1a833579e16 37699046 main/binary-armel/Packages 63c0ef80d23c66ddf5cc78d76a0662b40b72cd379f163817b9aaaad6ee3f989f 27910 main/binary-armel/Packages.diff/Index 1aac32bbb9e5bf5f4cf7b99034611cd8ec1134a75ec4897fac529f29b30d933d 9275300 main/binary-armel/Packages.gz 94e5a90f20410fb4d87451bb5ad5fe363e418664324ffedd809b00db2deb30f3 6921736 main/binary-armel/Packages.xz 82b62056193684651c2bf9ceb2af527cc75d034576d4cb3bc9b2f95fabf93e8b 104 main/binary-armel/Release 5764f36e2223186bff0fc3306900ccad92e8e8b8b705b3d289f5d1c5898f2e37 37788052 main/binary-armhf/Packages fc39c2a13723ce15ed7bd1323c625eefe63708106d313d0c26bfe303c99bf4b4 27910 main/binary-armhf/Packages.diff/Index 73614cd887087b115a5d51f5d4d69a8f56f56b8a51d8e2231f8b03c8b7010a3c 9300334 main/binary-armhf/Packages.gz c11f25f4cf6b37b33ba284ee31a960c230a3213e557277e3cd2382a722791e02 6934520 main/binary-armhf/Packages.xz bde8b39b284457d38e4af1f04f9de8e73a8705795de39525a6def7d42e523396 104 main/binary-armhf/Release a4aabc159ce2439c60a0f595e8bb474303966d85bd37a880e4c93c86a47c07d1 33806130 main/binary-hurd-i386/Packages de4dc855e795cf6e73d8d9dc1bc6f9d6cf523bb9b653f6bbcdbea1fea25cadc0 27910 main/binary-hurd-i386/Packages.diff/Index a1812e288764d013f66c5b9baf3d9a895cfe1f6152c806ceb72d8394ae1d1ed0 8234995 main/binary-hurd-i386/Packages.gz 0ab729453f01a564261190614151fa3391261815ef771cc1c1278b9ea4986548 6163664 main/binary-hurd-i386/Packages.xz 141c84b75fc4356d505e86c6603dfe15875c7be89f23b4e99570b97822110b67 108 main/binary-hurd-i386/Release 96fdb290724544ab3ed6f34563d96b13054b3ec219ed84588ec32e233bb1b172 39044462 main/binary-i386/Packages d1a7007422124bf288ac478adfc7e32c6db4f4cf7e84cfb9e07aa035fe302c1a 27910 main/binary-i386/Packages.diff/Index 0424e96da42d89dc31b7a2e88ad7b34fb0d48c1943b109dcca3abeb148794d0f 9548697 main/binary-i386/Packages.gz f121096021939484b9f54001067af8a686291e31ad98968978ef7b54036762d6 7122292 main/binary-i386/Packages.xz fd5827463ef11fa7a4c0777a83766441d7a730109e012830e7ded5aa051042f6 103 main/binary-i386/Release f8ee3c831a70de7db68cb615ac5c886398a5025604ad4f58afd1f806e7c1eed9 35913320 main/binary-kfreebsd-amd64/Packages 5ea30ce949672beacb096f8dc16c99052448254c10dd459b7579dec03bf543a1 27910 main/binary-kfreebsd-amd64/Packages.diff/Index 2b9827f5561975831e609fd73aa6f305fe46848a6f8989fe951aa4cde84ec60f 8718641 main/binary-kfreebsd-amd64/Packages.gz 93a4d574ad7d1c7d430698b2fcbe7a390b663d9855339075d8b39f3fd03858a5 6512728 main/binary-kfreebsd-amd64/Packages.xz e9cb26effa2842679d46dea78abf7662b5a75320e0dcf15054164abee90df89e 113 main/binary-kfreebsd-amd64/Release f61f0a57cd6e3b90f29df78ba6fc5936fa930f58df4c44eecca39b5ee77fe844 35809649 main/binary-kfreebsd-i386/Packages 4c076575c95105c382338d83dbd5021c17b10123f2c25fd957a83109a1cf0c55 27910 main/binary-kfreebsd-i386/Packages.diff/Index 62ffaa506a24593028cfc049178c129316eec91611dec632e8dda7c6fbe00e38 8704036 main/binary-kfreebsd-i386/Packages.gz f5a48fe4984d5db55e638cd42474d5eaa1067b8bb124fd6e2564209258033c77 6502680 main/binary-kfreebsd-i386/Packages.xz 0f5494dc8749cf8141c7f288273c275f7ee50c1b37231418f1db376bb7478b83 112 main/binary-kfreebsd-i386/Release 410e9b3fa8a7b06d40de71111c99aba20c0aae3df9c4fc26adbfb9092ade2c87 37366754 main/binary-mips/Packages b1b87da67376841c606df73e2db8b4be71cd3b9b2ba7a84bf281e92ad7445604 27910 main/binary-mips/Packages.diff/Index d070dfa70e6aa55836fd4855d25999dd1056c3f59cd227fed0b11f2e8cf293b8 9210212 main/binary-mips/Packages.gz 7624f9dc42e9f09da0b34e7b41812f0f38ab8425c7ea746d08a3731dff3d8a4e 6873512 main/binary-mips/Packages.xz cfdd9ddbaed505370e4b7054ada267efee4b38acb2eede584efc776ca12ef90e 103 main/binary-mips/Release 15974180876dca292aa33a93bf4712c579e48a50842020fc9fbb5bf6185bebe6 37340330 main/binary-mips64el/Packages d7723c2e3a0d4931f22dcac54bbcedc697b22b5cf0bb690216acfefd4f69944b 27910 main/binary-mips64el/Packages.diff/Index 32dff20bc482f0a3a2e7117c9b31908985c7da4065a610327e9d04c3bebcc712 9156063 main/binary-mips64el/Packages.gz a5fe219c492a1d1af2e9e617c7a16c962f7fc8fdc99fadfd8e786349beac525f 6834608 main/binary-mips64el/Packages.xz a3135889946204b9cdb22c8f2f435e566f1fd941606a6666b0297adc3a8baae4 107 main/binary-mips64el/Release 40f282635f2bde397acaf3e76112306552adee959c01c804173dc81a6c4564c5 37668023 main/binary-mipsel/Packages f6614536a8dca5f0c5e181cf2e60739327693573ac5ea20f33805ca96409bfde 27910 main/binary-mipsel/Packages.diff/Index 2e03fbe6ff2d29d7c3e20ba5b50f1a172c5dbe708d1b9d5b3bb5ec7f49acad40 9262851 main/binary-mipsel/Packages.gz 1e5d0eed7c2582cd21a58d5db2a6bdde2c1ba62c5c88a68665a06a64e32fcdee 6912400 main/binary-mipsel/Packages.xz a4c0b9ba49c406bfdb0d564e7daaf8b2d4e0cf978ca37b2981fc970d68557dcb 105 main/binary-mipsel/Release 3e600b48258b911254f8e12d2eba516f0c86bb6cb66bd07afdb1a222dd6e59c2 37823973 main/binary-powerpc/Packages 8ddf615b5f1b079258803a4fafbaec237ea62a6f9095383a7092320b82bc43de 27910 main/binary-powerpc/Packages.diff/Index 43896af9beb88d69a64e1f97aed98f2b8a383ae8b102e23414afc6638a98427a 9292207 main/binary-powerpc/Packages.gz 6d69e6f6a2fd87ce8f704e3368f92a054a2b52c2457ec69cf363789e24626435 6929276 main/binary-powerpc/Packages.xz e58934ec639194e73fefb1d44b0bf5866adce147652d74126c5f401691241e6d 106 main/binary-powerpc/Release 43996548337ad75ef7a404d90fd1d32376882559ff019f78012e71b1ac64d83d 37859587 main/binary-ppc64el/Packages 3515efeaa92655d1c1a0ffe582b21db9fa288afc425b69a4e9f4c474817aa857 27910 main/binary-ppc64el/Packages.diff/Index df0bce791228021b5c44e31ec8256625627cf1ecab2d747e3fe1bc6877051b87 9277244 main/binary-ppc64el/Packages.gz 3c85bc4f712205f3445b4421068a98eb69f203abcd4601efc659eaa1bcb8416b 6921148 main/binary-ppc64el/Packages.xz 29eaee9b665539956ef860f083f3885b3d69145a8c9bd33dba701039a2c46e00 106 main/binary-ppc64el/Release 7e26f1ac0fc0a166e1db5702246cba50ae918855b5ec8fa7272ba231e018a5af 37432240 main/binary-s390x/Packages a76c3178898c41ff2d7a9e95a5a105bce1ee7bcf564824ced543abc06990ec23 27910 main/binary-s390x/Packages.diff/Index da4b84c571306ffd758aae45b48037168fc87f8d15842174970266bbdb807631 9217612 main/binary-s390x/Packages.gz 69d9a47f563df54153c018c0f2f705248a97bed26971c981aae34780de38d69f 6875604 main/binary-s390x/Packages.xz d9226f019e4de7c67111161156d98d7bff3eb72164e69532c0e9066b2a625f7c 104 main/binary-s390x/Release 19dcb29f8d2240f8e95dbd97b1dcd1ac1ed7429f906f4f2ac6c929fa4c42fb25 63066 main/debian-installer/binary-all/Packages ff8347481ac62bd1a14c89640b3c65e753b07d734b2747b2baa1350bdc066ed7 16829 main/debian-installer/binary-all/Packages.gz 2769d338207e4fbfaf84e9ad4ad2e39cb52940cb154529152295175e86f24034 14836 main/debian-installer/binary-all/Packages.xz 426d4a8e95369e5d97171b4e8e5dd73a1039231d256e35cfd223bf572be116ed 102 main/debian-installer/binary-all/Release eafb7d2b5be831f402929d6aadafd785db65240cd471782e08694c2b78b3b00e 258901 main/debian-installer/binary-amd64/Packages adf69b6aa09e4df452c71acca288349159c3cf8309912aaeb1886b784471f331 65129 main/debian-installer/binary-amd64/Packages.gz 055f2922bdddab4bc5c0e0b72c1835c6f657c2dcbae8add4f85fe5862e3557bd 54808 main/debian-installer/binary-amd64/Packages.xz 07cb692d133dce21dabb9e42201b8e632a7c6a069b53f9ae8173a4ea24606da9 104 main/debian-installer/binary-amd64/Release b831881dba6b4a110a45450f7e8f6ebae2d700db99fe3c5444674a1e4eda6de3 239565 main/debian-installer/binary-arm64/Packages 50b92d50e650200ccd609893a9718db46ff78c65ca4668450a7efbbe32fa795e 61678 main/debian-installer/binary-arm64/Packages.gz e936937fba91eeb63e66703cf817a8b9c65024f49e7fa86f25f7f4639b33d78d 52152 main/debian-installer/binary-arm64/Packages.xz 16b43705f8420470a43f96954eb7d00a75c4408a36c1b86ccf2bd6cfa4ebfd98 104 main/debian-installer/binary-arm64/Release ce5a93085e974dab2deb0da4cca6e5d0858d76bf310811dcf5370bef20883861 277695 main/debian-installer/binary-armel/Packages 5a44faeca117a93ab177834d891e43263345c213b8c08a822c08cb02151f8bcb 67415 main/debian-installer/binary-armel/Packages.gz db01006a7f689c09f0a451c73a15bc93a4c2d4278c7f5492e70691a02de7200c 56652 main/debian-installer/binary-armel/Packages.xz 82b62056193684651c2bf9ceb2af527cc75d034576d4cb3bc9b2f95fabf93e8b 104 main/debian-installer/binary-armel/Release d6d1f8df525dd572f9bf76d102e8c7e4e1e2ac1543662b277f4399f0817a8908 238490 main/debian-installer/binary-armhf/Packages b91d5f3c91a66cd7f55b640dec1c8a6b69379bf85eb1c98840d165289abf9bf5 61449 main/debian-installer/binary-armhf/Packages.gz e785f3eea00be33e3726b650b0f77458833d150e7ab64adb86596b21b1c39322 51968 main/debian-installer/binary-armhf/Packages.xz bde8b39b284457d38e4af1f04f9de8e73a8705795de39525a6def7d42e523396 104 main/debian-installer/binary-armhf/Release 52ac986b0e26d384e51c43a2bc59afcc5736405ae1aba47ea32aead426f47821 163351 main/debian-installer/binary-hurd-i386/Packages 60540e4dc290c9d578d0a6bde73f41394cf2deaaf57930b0e889b5d1d07dba5e 44433 main/debian-installer/binary-hurd-i386/Packages.gz 3c731885a85e3ad6b43256c3597922f64f4c3568cece7a928613d9cf2bfcc56e 37804 main/debian-installer/binary-hurd-i386/Packages.xz 141c84b75fc4356d505e86c6603dfe15875c7be89f23b4e99570b97822110b67 108 main/debian-installer/binary-hurd-i386/Release 3423ee13dd694bf8ae4e612b3776ef8db21dd79b19285f3cc11f47a93007e01a 326037 main/debian-installer/binary-i386/Packages 30faeea1e84e05189cf958c17a73c779cc20bbaad5efc372ba00f0a198c4ddbe 75140 main/debian-installer/binary-i386/Packages.gz ce7f378714cb417823c41c8eb45a4ef34e9363f35e743448dc6d902fca8770b7 63056 main/debian-installer/binary-i386/Packages.xz fd5827463ef11fa7a4c0777a83766441d7a730109e012830e7ded5aa051042f6 103 main/debian-installer/binary-i386/Release 8613a8e66cec2c8ea9ecc65439cc12f7378e3c3c106200dc7b890c4e97160f0d 199536 main/debian-installer/binary-kfreebsd-amd64/Packages dd6dbe97ddd9e1b3ffd98d7bd735d138f8272d2e8150e3889d95cda89401cf6e 52052 main/debian-installer/binary-kfreebsd-amd64/Packages.gz 498d55152dbf9e3a5ffd0e16fab7d9056252f6696fbaad0965bfa5328616aded 44020 main/debian-installer/binary-kfreebsd-amd64/Packages.xz e9cb26effa2842679d46dea78abf7662b5a75320e0dcf15054164abee90df89e 113 main/debian-installer/binary-kfreebsd-amd64/Release c568de8a747cdb5f9287b5a00e44363e97c8d4663fc45290b517a2a593978d7a 198799 main/debian-installer/binary-kfreebsd-i386/Packages 4d91b1cf5d13db9906a0db7346f6c5ea8f3faaf5ae843042804b1092801e8953 51848 main/debian-installer/binary-kfreebsd-i386/Packages.gz 88d289b4910806a472adf4d5d40a60e39753c3a5a42a4fca7e1c14ac5c5ebfec 43944 main/debian-installer/binary-kfreebsd-i386/Packages.xz 0f5494dc8749cf8141c7f288273c275f7ee50c1b37231418f1db376bb7478b83 112 main/debian-installer/binary-kfreebsd-i386/Release 3adcece3efd312cc584c57667b5699edb31c7d9feddead3724d789b32e4caed8 299343 main/debian-installer/binary-mips/Packages 80a6228f3e5e5c4378f3ca3c4a065f7eae13eb071714f58f54f71334acd7a022 70317 main/debian-installer/binary-mips/Packages.gz 762c50cd552ee6628629909f708b393f56a9be0ef84fe8658fb8f74f19e8d875 59428 main/debian-installer/binary-mips/Packages.xz cfdd9ddbaed505370e4b7054ada267efee4b38acb2eede584efc776ca12ef90e 103 main/debian-installer/binary-mips/Release 3588c6735e797fb410181a416097ba26ce3c2b0675f3037affb9ab93da0e12f1 360842 main/debian-installer/binary-mips64el/Packages 981fefa3728b93c4e3eda7521abac6989aad11c9090735bcece2a46de503e986 78622 main/debian-installer/binary-mips64el/Packages.gz fff6dd8faa5927586778322723ec384934c13a667e5f45f3cf1065b707747b20 66228 main/debian-installer/binary-mips64el/Packages.xz a3135889946204b9cdb22c8f2f435e566f1fd941606a6666b0297adc3a8baae4 107 main/debian-installer/binary-mips64el/Release e96f583adc4d9cae787b291996c74b740db483e5871ff8504e8fba9f4a975d87 479718 main/debian-installer/binary-mipsel/Packages 8cecff99689c71462f9fa5aed2e97a69ad62a692cfb607691fc853569ae5a8a2 94984 main/debian-installer/binary-mipsel/Packages.gz dd256973f6444cb4089638b8c5ab025c7ce404ddbb7029c7eadb3057b25d0231 80008 main/debian-installer/binary-mipsel/Packages.xz a4c0b9ba49c406bfdb0d564e7daaf8b2d4e0cf978ca37b2981fc970d68557dcb 105 main/debian-installer/binary-mipsel/Release a1b098e7265ddd02ead590a9bfbbc917b7605a0bb396095f389e4a08e99bec3c 310055 main/debian-installer/binary-powerpc/Packages ff2364ff0722a2948e83f377fabcd8561a39943c268e44f908d0e85bbf8a043d 72594 main/debian-installer/binary-powerpc/Packages.gz 56a27b1cab18acf6c50b1df79f299a2baba2ef7732c5fe290839b651096188e9 60748 main/debian-installer/binary-powerpc/Packages.xz e58934ec639194e73fefb1d44b0bf5866adce147652d74126c5f401691241e6d 106 main/debian-installer/binary-powerpc/Release 846c39a7755ffd13d3094e4de6ede4d5e06ff5ceb46a98ebd28eb53a1a5ffc12 241368 main/debian-installer/binary-ppc64el/Packages ba016a2b69af3eb52bed2e210009916e184ed2498cc2a1ea4377563dedad380d 61537 main/debian-installer/binary-ppc64el/Packages.gz 04480266634949d89e97fbc59afdb07962e8be21ca14e115c1926a556048c1f9 51868 main/debian-installer/binary-ppc64el/Packages.xz 29eaee9b665539956ef860f083f3885b3d69145a8c9bd33dba701039a2c46e00 106 main/debian-installer/binary-ppc64el/Release fbec413dc2c1fbe272e53fba5517a6d5075119217c938a5f4af5ce223e030421 217996 main/debian-installer/binary-s390x/Packages 2d636884fb244098b0d73a122feeaa340ab1ab05115e45e0b3c69c3ced77b667 58135 main/debian-installer/binary-s390x/Packages.gz 3c0555582da6a91b1751c5f5e631d5f07fc31d981c7034fcef0be6d259900439 48892 main/debian-installer/binary-s390x/Packages.xz d9226f019e4de7c67111161156d98d7bff3eb72164e69532c0e9066b2a625f7c 104 main/debian-installer/binary-s390x/Release 2b6add80d68dc7c65a6f404e2fa5ea35bd2b418b332c070ffacd554e73a9416b 7796441 main/dep11/Components-amd64.yml 2867e0d90c64568e2a25c371546c9c04a01dc14f549620525c9c3e9d1870b8bb 2854073 main/dep11/Components-amd64.yml.gz fa51b492af02b7540e084b9b363a0635e6387a54f6f4f9fe45992de0295dc6ab 1850876 main/dep11/Components-amd64.yml.xz a668a7255f4a68a8026dbe8c33dc662bf65fd7da79d44cf7e1517e34d5322293 7735559 main/dep11/Components-arm64.yml 744e29258913f71ec0cbc2090f6f0eeb819c7d19958778d7c585c23cb3002366 2835371 main/dep11/Components-arm64.yml.gz 19c48720dfda940ece0476a634f35df9372d69a590ee7675a39058aaa34cb6fe 1839368 main/dep11/Components-arm64.yml.xz 6920470671875e0f9a18363b95eef8863afba0d33497edfe0a3a9fb17d6f6550 7744621 main/dep11/Components-armel.yml 28ebd252d9e9b875e60dbe30a542111decd4fb6c043938d75f32bc7c3b802d08 2837621 main/dep11/Components-armel.yml.gz 04d403c5cc79c1333bc3db39b2becbe786d4257a25fdb3a795de2e92baca4ec8 1840304 main/dep11/Components-armel.yml.xz 6bf47cb36757a7b19972cd7151fc2b4c55c2eb077d30fc19103c08410184f19c 7753412 main/dep11/Components-armhf.yml 7aa5a9da1c7ea0a0009d83052bd37bc0f6c8741f4a83e9f956a71d8e8f4d1a04 2838544 main/dep11/Components-armhf.yml.gz c75953362cfc75ab475bbef98e717ad653380065bc1a7b015f39e5fd6ccdf6e1 1842132 main/dep11/Components-armhf.yml.xz 48ba32b1c10b41f4b28bad40de687da7fba63ebb782d580b656509d30aa3b969 7795011 main/dep11/Components-i386.yml ac60bd291efdc7cebb782189c0331586362b15f118c3f36662fa3b79d46354f5 2853321 main/dep11/Components-i386.yml.gz e565ee62f2ebb7a23e7f7de3d28332d254ef910f21b92ec8523ac0f498d016f8 1850760 main/dep11/Components-i386.yml.xz af46dc64347af15c1a48d605fd5d05438862ad606e5bf37c4cb594499787eb2d 7285280 main/dep11/Components-kfreebsd-amd64.yml eb85a70159fb91a20a5a102b644d8b08d24178b969eef1f5be4f688565ff530f 2669347 main/dep11/Components-kfreebsd-amd64.yml.gz 986203b8bca15bb92fbc61654cc7d0095f5a120a8210768dbc824061b98dbe0a 1732772 main/dep11/Components-kfreebsd-amd64.yml.xz 48aa792031f676e2948dbdd75619b5f445579b6fa4db1585f18bcfa55ce3a869 7747672 main/dep11/Components-powerpc.yml c97308a0897ca0035768fb7dd7c0ee2ef8982cc319572b6805524ff11ae0ad04 2838839 main/dep11/Components-powerpc.yml.gz a56885af1975a131836d7284158641efd0db99244080b31628e67a877d4342de 1841208 main/dep11/Components-powerpc.yml.xz ed7ab3be19dd7946d75807813d3d809fd597d7330da6229b92a8559c82d4e564 17068032 main/dep11/icons-128x128.tar 3926d53c6478a1144dae0c05fcf2655850f35cc10b17403a625021aa38a86448 15132129 main/dep11/icons-128x128.tar.gz 1284747ddbfad7092ee234d0f605d57ebe41c96b63e152f9da469d8ec738a5a5 8170496 main/dep11/icons-64x64.tar b39ed190c7e14931a1b5087d27b6caadeef5df7633691ef74920d13c45b54e65 6466066 main/dep11/icons-64x64.tar.gz 1e374870b76e80b982d14e26dfa0f8c6ee2aad8e39e50721cb0f9f8ad28a8a8d 8628 main/i18n/Translation-ca cd2cac5de40e25a1db1d4bbf9e43ce8a29923844b12383aa985b8db2cfbd7774 3543 main/i18n/Translation-ca.bz2 aa8e6e1d8e47de43b9469a8a3eff30a33559a20548949b40c352b420ecaade8c 1755527 main/i18n/Translation-cs 3d066b1c89a7ae5310f6e63824e58abc4b02988f11af134128d81e7be078ab29 456958 main/i18n/Translation-cs.bz2 2f278e7384dc57159e39fadbac82d94604b1a3ab8985c4da4d8cb3d2681c42df 14512 main/i18n/Translation-cs.diff/Index 3072be9daac7708d563f054e71759ab8653a793cb1b0e019548f29a0c5cf7ddf 17190796 main/i18n/Translation-da a07ae07160c3d3901b3866eb5dd02529ba9a03e225410e23b928d3e36c482915 3716728 main/i18n/Translation-da.bz2 918c8bb270ee2f667eb8c0e62aca9bef4fffe6afd6a40f959060219f7c134511 27910 main/i18n/Translation-da.diff/Index ceaec3203741359390d48d864486fab638196da908266e9e64558fc8b0c59ab9 7563488 main/i18n/Translation-de f117b1a1c2b3c7ee5dee0e5e618efb05edf354931966554bd38244312201fbea 1724541 main/i18n/Translation-de.bz2 9efc4bbcf325f3ca872b72bbb664bd682ffc7c7503f7ee42d2a5f7da34872dec 27304 main/i18n/Translation-de.diff/Index 284169348b8bd4e0de4cc5641eeb05577e80d2bd736452e454976c052cf3cbe2 1347 main/i18n/Translation-de_DE 481a435ad350105b74c4972859c44f447b7a8b5edea0d42f6dd635792e00a461 830 main/i18n/Translation-de_DE.bz2 9f9abe444c71a7c8c9541f626211c000e4f311c8ea0a9061ff125c53ce18a210 8287 main/i18n/Translation-el 8e4906ba999712818c7839947cfcd30d3575e7e6462d7c3c2a230f9ce84defc0 1896 main/i18n/Translation-el.bz2 973cac2647ea8e439c6f7b2b474d1d7b6c0847df2f261e96546a4f88337843db 27076273 main/i18n/Translation-en b04cd0855aaf3f70d76ce378b1c508a3d5945f4e630e78c5f4ce4c32408127d9 5427424 main/i18n/Translation-en.bz2 e422327bd3382f40cc0b29283106fb1d3eaf17df7b59dac808f63a654aef85db 27910 main/i18n/Translation-en.diff/Index 847690699f812c7d0e3f424aea63dbe176a0a5d4038a02e9aae5f27850b2ec81 2517 main/i18n/Translation-eo ac072915bffd0f6a205f28dea821a070c349af7bf4b65b4179aa0a915bbbf08b 1279 main/i18n/Translation-eo.bz2 4095465f33a6bd62ad5d5cc9c4f85b12fdf6530aa37dde9f2a974bc2258b8a1e 1326503 main/i18n/Translation-es 45a1921e999a7757933d302d99439ebf5a89f1027ab6cbf6fa1b6a36ff68093e 323379 main/i18n/Translation-es.bz2 3d5c2442fc311560e27bab4a69d8827573ef7864f9b6e038fdab7cb229918b48 17464 main/i18n/Translation-es.diff/Index 50f69276ee42d67f783d4ed8ab93fde3af4cd1b46cf826fbf39cc77b38ad919f 14383 main/i18n/Translation-eu 912db71591f0bfbddbb59bce6d757dd27d72ea3f56eb8e2397cb6c41607e2d72 5475 main/i18n/Translation-eu.bz2 18f79c4119f514ce2ed2a4af62bb547d249881125869202dc8454693e30c8c1e 736 main/i18n/Translation-eu.diff/Index 0bac49d589ae73bfeb86d62d3d633f2ee29b7d689828512781df171b86f970ab 386506 main/i18n/Translation-fi 7ba714ae1b6a2a0ac7c76727d01d598c0f7f3208ffb558e7cb2a7d92ce2152f6 106618 main/i18n/Translation-fi.bz2 c88bc10f4d96bb82cdc9f48940e177f3bffc81af2f0229a557f5424556a838a7 11068 main/i18n/Translation-fi.diff/Index ac779bed66162a7bab1d95c119a7e849043f9b38bffe19ac30f92c8b8e2ce16a 7401641 main/i18n/Translation-fr 948c03e5e34aa7ce88caeadc5874b9bc76666d4cb88d30955a802afe287be488 1518856 main/i18n/Translation-fr.bz2 a3ebcdc75aaed293be328248934f9d26797e7171768c6a1b1d07a227d9992fe6 27796 main/i18n/Translation-fr.diff/Index 5ab03ce3f511cbb5a361ea64ce89e7ba49e01f651fa2e1d4af3a0b10021c3689 21899 main/i18n/Translation-hr 033b8b7b0246dc1d6c75df3abcb236960df570f8c04b7bf3a29e748a3de0434c 5692 main/i18n/Translation-hr.bz2 ab2f5608641965715b677ac5fbc57bc42031154632b02954947f9aed996821bc 2212 main/i18n/Translation-hr.diff/Index 89bfe0cad2885305d5d1af0c78a1b7f18994185b8f60480c0107d81ce1f34438 96810 main/i18n/Translation-hu c3716593bdb1f60488da35fe48e786423e02a24f782aae023e65fbd3154112b7 31851 main/i18n/Translation-hu.bz2 c8bafbe9e089ff8a94b0e86da15b485ac71235a068fd9ba628d89916b8348da9 1720 main/i18n/Translation-hu.diff/Index 0aaf5db67afdf995c5a728194e9715d26a2044ebd50cc5fa022c2371a4d85513 7124 main/i18n/Translation-id 6fcfabedeec60499786b609af2e7154c20307931e976cd2cda4bbf909ea1c6f8 2899 main/i18n/Translation-id.bz2 46d5a128baa65a889e5cb5a43fdb837fa612cc1240c381b57b1deb578aae7e68 21789753 main/i18n/Translation-it b01eb20d0522d2d1e23c493af214975ea598d9ad4d08a019d0715e2b31dcc822 4268806 main/i18n/Translation-it.bz2 c7c1db21789f0d522822accc7759a421cfee797db9ff8cbc92514ed5b85d8079 27910 main/i18n/Translation-it.diff/Index d774a6cf0f8d3506b605cfb21737bdb7fb8d03aa5defde3ea8a0f91f5eeecfd0 5075815 main/i18n/Translation-ja e4ffc9dbe6437620f8f2311920cd8f6b1a2e00d1aaca64ed4437d7ef12e090c6 913629 main/i18n/Translation-ja.bz2 650a491c720acf9cce36a88819e589e9900b550804c592de03b7e4ec75e08ab4 27304 main/i18n/Translation-ja.diff/Index c4f1d19666e0750e947499a33d16ca76049d21d460e309ef232172b336217c14 19077 main/i18n/Translation-km 69632d98e66c1a7056d963ca6a018984ddc28d430c04988900198c48396aa73a 3407 main/i18n/Translation-km.bz2 5960f70ed862adfc8184ac6717bdd0c309fb88dfc2f515624c7c55379baf9fb9 1720 main/i18n/Translation-km.diff/Index b6ef66cf3a04d86c92f8a626603c49a1b8bbbc0510947f397c6e61a28dccc0f6 1631004 main/i18n/Translation-ko 5b29de7d451e521a6d34e58f9cf1260fc911036dc899df721ecc08ed69b141d2 348522 main/i18n/Translation-ko.bz2 da026ac56e180d0d6a5cc209f973c4f79307d7c600d1e25be818ebe61c6accfb 22876 main/i18n/Translation-ko.diff/Index e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/i18n/Translation-ml d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 main/i18n/Translation-ml.bz2 917a62d4d2bd8fb889d77caae2e4ff39abb23410919ecdc553e8aa4f1854da36 2325 main/i18n/Translation-nb 487379711cdb467cfdc7ff412a0a844796bc8371252b051fc1b325f9ac90832d 1302 main/i18n/Translation-nb.bz2 55cd046521e8af3db8dc99c8ed1efaa7d90ebf901c2a507eb0fe59d3a7cf815f 264671 main/i18n/Translation-nl b0b66f8e47c921744974f857f50710f749872a89e60c7f5be62dcbcc255b6ebe 71487 main/i18n/Translation-nl.bz2 1e2c3990e5a3e7bd37bd6e0d14acd1cbd967e4823efb8cbea827283c68d5d95e 9100 main/i18n/Translation-nl.diff/Index 3d3304429734bb7fc3a6c2d2ae96c18fab0915085f91029526101d4e37a7c7ce 2458040 main/i18n/Translation-pl 9f70c5b1bb9fa6dd8134fc38347e4fcf87397974fd1fa04ba30cfc4934554954 569778 main/i18n/Translation-pl.bz2 1e65e5ec83def88ece96e0874fd7e4804786bc865ea350c1cd5c00871aae2e3f 26812 main/i18n/Translation-pl.diff/Index 5be72155cee0b61542ef0adef00ccee2cb5c00094f5651f9e56971586aedd27c 1564381 main/i18n/Translation-pt 8780f320a4ee72a1b3e39026ac64de1568be9999afbb796e72fe513f0ade6a81 386197 main/i18n/Translation-pt.bz2 a1d7fbb28d9e029a7d57db8f8f86b5b9aa826f4ff63cba6ae74b2333cf13a02f 15988 main/i18n/Translation-pt.diff/Index 9d6de6b8b961fe637efee092cf10ca8d92afe0b3571b037fa48689d690451a8c 3548094 main/i18n/Translation-pt_BR aa3dcdadc481f7151650509242ee91aa57d8d8197fed2a30c8e8723d9e5774ef 846754 main/i18n/Translation-pt_BR.bz2 86afd411cd5d8f3aa32e4cecdc7cdc40b8ec23070f0f772ee37d491d7c54b2ec 26812 main/i18n/Translation-pt_BR.diff/Index e7ab43bbc80d3424f08ec3e14ec6219b7bbf86a201b3bf719393a578ab064ea9 2710 main/i18n/Translation-ro cbc7eed8d01c12cb9bb5b551548780739136155c80f26a09b26ec2ced71a387f 1374 main/i18n/Translation-ro.bz2 2c7e2ecb4b65ac45271c34fac0f83eb1c0da0c5393ea202d54e548a7bc37828d 2655478 main/i18n/Translation-ru a19a3c21f9de6314f40158d9b073b0a18ad3c31c30e6e8fc605f14c588314001 431543 main/i18n/Translation-ru.bz2 50ccac5b40920038d458dda1344d5dcc1889471768ae04deeac0d9c5d85f7e38 21400 main/i18n/Translation-ru.diff/Index 8c84a061195c975076d6eac43cbddae7534f6a62b0347cd7c73b504117e02072 3819007 main/i18n/Translation-sk 47a5c04cd83b4a89abcdfdb6eb3b76c934c6a7316959f1b8cbc7b460ee65344a 843783 main/i18n/Translation-sk.bz2 d25e6d70fa015847a12e46074f4b3977a85f8d179062677010c166868284ef35 27796 main/i18n/Translation-sk.diff/Index 5b666d5bc1f0f15eb08f62db37b2f53094aaf566f428b8ada5dd82ee51b05a7b 441964 main/i18n/Translation-sr d6d2bc3af088d4e71653201d6aa27a519775151c3f4faa24f222115b8461370a 78350 main/i18n/Translation-sr.bz2 0179ee9a35260e64b728ad2a09fa20896873ab9bcdadec86b5afb49a905d3347 8608 main/i18n/Translation-sr.diff/Index e4ec2a2b1d90acddb652afe05eb48a53f0661d7238d5e28e2f4fae32c87c4dd8 117165 main/i18n/Translation-sv a5d616f8d207af0da3891952bbee93079daf1f1b3c447724959724000501618b 36066 main/i18n/Translation-sv.bz2 92d7c8c8e4acfcca816e1c1272b17540d051f51a5d42c4219e31cabaa01621e1 3196 main/i18n/Translation-sv.diff/Index 926b4a85b107381c03f2ae77007d2a9e628b1da209a654ad190d720adfbdd546 902 main/i18n/Translation-tr b53b66bec94d548702370107afb1cec937abbd7b5b29ed47e5f64c1f7416414d 530 main/i18n/Translation-tr.bz2 409e387d4dc4cc0bc2f9d74e5d2bef262521425a3f2e4a07e549e92c1bdda5d9 4277439 main/i18n/Translation-uk c90f7bdb85fc0ad529b8865e71fa1da6118ea6eef51622132a90dc51ba6e8c24 669553 main/i18n/Translation-uk.bz2 346220e4eafd56d14d3b4485fd16d050d3799628425a00c97e67c97f93eaec44 22384 main/i18n/Translation-uk.diff/Index d7d16a522f46156ee04a92b716ba33dbb79c95bba4f349cad33ca2766c938b5a 30443 main/i18n/Translation-vi c9cd3fbdc76e5a9ea0b8d77cc54e952041301240158bab996c3fb66b4ae23b86 8837 main/i18n/Translation-vi.bz2 7d2c34fba22297c19cf1b9d7787ae5ff29afa74285df315d6b7f7a19c92f6ff0 2212 main/i18n/Translation-vi.diff/Index ba5bf385ecf4799de85e8ab873c233fe0478bbdd30611baf528aea51ae2b97d2 2799 main/i18n/Translation-zh ebb2e1910c3096f393f46f4fb6b1316e571d805d654c2d97d0ea92ddb2229424 1526 main/i18n/Translation-zh.bz2 300fa6cb66272ff4249883cd05c5435b9a72bb741a30bb3c12e21f4122b08844 334563 main/i18n/Translation-zh_CN a186649a78aa62e308065744c3c1a26ee237eaa7ae92357aed5786dbbd6ff1f3 92636 main/i18n/Translation-zh_CN.bz2 f88dde02c66fa5c190e86d2a8281c5978405ed8de1615baa00d0fa0ea0890577 7132 main/i18n/Translation-zh_CN.diff/Index 52f67312742d998a09bb592592ccf88ef07fbf75c9fef0b4aca794dc8a59249b 57122 main/i18n/Translation-zh_TW 768d5096de38af7c27a732e3d2bb562c3fb510ea4069fcfea01d92698eeee378 20289 main/i18n/Translation-zh_TW.bz2 3693b3534f95bc8809d67a670cdd0d6a6efd1319de1cb1b14215a20807970940 3196 main/i18n/Translation-zh_TW.diff/Index 782fc37c049313ea581c76d8a06d10d894532edd954b2ca85bd0197b3c30dad1 53815 main/installer-amd64/20150422/images/MD5SUMS 17abd745a3e8d79d4bc22dd9f5bded89b1841b7b991eaaf4a105bcfcc835294d 72131 main/installer-amd64/20150422/images/SHA256SUMS e6cfbb7c170fdb53bffe819a90dc0fde4dc2728b37448aac3d3beb595dfc8cb5 53815 main/installer-amd64/20150718/images/MD5SUMS dcfdf871d5d2eb5802c7a9dd2a6dfa09988be610970117c0bb807e4aad0e2478 72131 main/installer-amd64/20150718/images/SHA256SUMS 2dad7cc647954c125cb01aefe3ea660864b1ddc0bd02a3cca240bd7cec3f827b 53815 main/installer-amd64/20150813/images/MD5SUMS 8abab8a8180c02ca3225cc5cdafade4536555759b2b349bd04be1343e9b9a572 72131 main/installer-amd64/20150813/images/SHA256SUMS 09d6149410e83610406431483b528548a58ffe55eef35e416aa2d6c645eb48f5 53635 main/installer-amd64/20150828/images/MD5SUMS 15bebe8a9eadbad640a1fe31f92132b1410e982cf36710ff5b4c4788105f404a 71887 main/installer-amd64/20150828/images/SHA256SUMS 00fba993c2b9a0495fe010cd6519960c271d7a21b424b4b57677bf23b3173719 53635 main/installer-amd64/20150911/images/MD5SUMS 6564ab007ba57e368cf4e27ceb35903eb4e001f7a13fa41ce7fa3c2d21881bec 71887 main/installer-amd64/20150911/images/SHA256SUMS 1982d9bcd3e4df5c4a0ddb8b8a3be4a79cd85ff8379eb78eaf92c0000fd8a643 53727 main/installer-amd64/20151023/images/MD5SUMS 439922d81867c0a1326d2b4918e9e63cf3e083c8eee8e619eafefc1430863c9e 72011 main/installer-amd64/20151023/images/SHA256SUMS 719797fd52641baa145e96b9d68f796d6059e9f5917fb86cebe8ea813d92c6e1 53635 main/installer-amd64/20160101/images/MD5SUMS 9912410703383968f58d0039777da36a822541e5e298b3d62d4027155f04523a 71887 main/installer-amd64/20160101/images/SHA256SUMS 86e5b841fefc66d27c24b28df158d4563adac62d4a03803be9f8176c3c506ba8 53727 main/installer-amd64/20160106/images/MD5SUMS 5a3c5789d8e81f650063aeb81ea01c00c22ab092bde8cadf5cd1e5f57fc76802 72011 main/installer-amd64/20160106/images/SHA256SUMS 2ac430162d2a2633671508abf149f28b1a0dbe122aff8bd1f167e18f2d63a8cb 53727 main/installer-amd64/20160516+b1/images/MD5SUMS 2cca615ea20b9a6422585c9d4954285b41f33b095570ab7fdde096d767059113 72011 main/installer-amd64/20160516+b1/images/SHA256SUMS 23d12f98b74561addd40c415c2a4b63e55eb9ec2d1966c3a544844f5522f1f3a 53727 main/installer-amd64/20160516/images/MD5SUMS d4fee70a41229548ffb8a521bdec7a9427de2f1747d575a434590ee6eada0830 72011 main/installer-amd64/20160516/images/SHA256SUMS 9ba4360e882bd35893b6051cc04da1ca941f599e2c75f350ee22f0c6243d66ca 53727 main/installer-amd64/20160630/images/MD5SUMS 84019019358117e95eb8506e7926f133f3fab4a5b6c171dace6120ac903700dc 72011 main/installer-amd64/20160630/images/SHA256SUMS 9ba4360e882bd35893b6051cc04da1ca941f599e2c75f350ee22f0c6243d66ca 53727 main/installer-amd64/current/images/MD5SUMS 84019019358117e95eb8506e7926f133f3fab4a5b6c171dace6120ac903700dc 72011 main/installer-amd64/current/images/SHA256SUMS 47a2f89ca82421b20984d38888d48cd5200dd676ca51a9342476bc62c812379c 19148 main/installer-arm64/20150422/images/MD5SUMS 08af7315fd23120f3e8ad1af11ac2730a455d6d3b6ed673f427ee003068adf34 25912 main/installer-arm64/20150422/images/SHA256SUMS 4abd1bda3c9a9e8f978f78624e12af33abfe4a63cdccbd5ff30a9f1776fae9ce 19221 main/installer-arm64/20150718/images/MD5SUMS 703133bbbcc9cffdb51279826bd8e9fc3a7f287e44ab95c4587d2b264ee6c905 26017 main/installer-arm64/20150718/images/SHA256SUMS 01d66d6968bdabdeedd0e81c61c0fc812c9e2ac7c216872b69f264c928c75b5e 19221 main/installer-arm64/20150813/images/MD5SUMS ddfe031101b249b46717cb1ddefc90ca1894d9940241aa001b0a62bafa984977 26017 main/installer-arm64/20150813/images/SHA256SUMS c7da876058755ab6c76e702ff0411fed9c463458986511d732d8013efb98fced 19221 main/installer-arm64/20150828/images/MD5SUMS 04aba7cb700da431614fc3cd2f6b0d9c0178c41012824a2ee868055eb3062438 26017 main/installer-arm64/20150828/images/SHA256SUMS 4854c9a6530782e68e11b1ad5feadb5a021342573aea8ca44b73f676d7f167d3 19221 main/installer-arm64/20150911/images/MD5SUMS 5ff60a0ee800fc22cfdf3d24d0776c8eb65a603ea3d2a91cf7626779218924e3 26017 main/installer-arm64/20150911/images/SHA256SUMS 28d6fb70d3135d473ad1910d1e1862b0c651cba341a44555e8517580a80163a5 19366 main/installer-arm64/20151023/images/MD5SUMS 91da9120ed291b45eee2ed227e1714050037296517a3bc7f0f5848ff2a495b41 26226 main/installer-arm64/20151023/images/SHA256SUMS d05fa6e633f69d6e5e67486c86af96c6637816a9f03449c45f231ad408d9c497 19366 main/installer-arm64/20160101/images/MD5SUMS 0ee86a9f4304295a463499cb81ad326b7a308fe3f15ed567db5455afa746fd74 26226 main/installer-arm64/20160101/images/SHA256SUMS 40219645a6c166f14761286fefebeeeeef077dfeccc375f6d703d4b9810b753e 19366 main/installer-arm64/20160106/images/MD5SUMS dada57574e0389c9a2688ecae6bdd5631d1afea4962415af9f774abd1e15a71f 26226 main/installer-arm64/20160106/images/SHA256SUMS b77a665b60cc6c563629839821b36d1d74f11c8bca932b2de2297bbbe986f601 19788 main/installer-arm64/20160516+b1/images/MD5SUMS a8dfaf96ea5fa2d086bfb2ad1308feb1a69dc99e440748c73872d1489d237abb 26840 main/installer-arm64/20160516+b1/images/SHA256SUMS 7543ef2b46b7339c2c2d654fe80753d71c67be4303cebc6068cbeecee6f0134b 19788 main/installer-arm64/20160516/images/MD5SUMS 60d321071941462dd9e2155a6b6754dd035c7c411d830f6d6181a2809c8af693 26840 main/installer-arm64/20160516/images/SHA256SUMS 19d318ff132aa6098e0eb9786a445565e64c425fd69bb1999363dd7cd9c99838 20520 main/installer-arm64/20160630/images/MD5SUMS 9ba172874b238fe47b6ced472f43a36d69727ae0acb6408c17da79f9abc03734 27892 main/installer-arm64/20160630/images/SHA256SUMS 19d318ff132aa6098e0eb9786a445565e64c425fd69bb1999363dd7cd9c99838 20520 main/installer-arm64/current/images/MD5SUMS 9ba172874b238fe47b6ced472f43a36d69727ae0acb6408c17da79f9abc03734 27892 main/installer-arm64/current/images/SHA256SUMS fdbf3f1cd1be6e4192103e2af7e0d7df528af5c3786fd04f5ccf4ed9dc47529a 8985 main/installer-armel/20150422/images/MD5SUMS 8a5e9f96a5574c8082ff1949b77a49b97593274fb4ee9d188609b2c849d534c1 12645 main/installer-armel/20150422/images/SHA256SUMS 248531e71eb0a18d89923b0374550811453fa5cef8779fc49cc8b0d86b3563b7 10136 main/installer-armel/20150718/images/MD5SUMS ea5743f8661e7273e111597ea888031b6532c83a6649bd69ed6f165f392a039e 14244 main/installer-armel/20150718/images/SHA256SUMS 2dc35a8a631ecf115d01255353b932b8fd82aa6605683685c46e1ff49ff5840e 10214 main/installer-armel/20150813/images/MD5SUMS 5967ca0fba0bb9558193009f7d4778c13cc3fc47b5a55b0589185b7b55a92e21 14354 main/installer-armel/20150813/images/SHA256SUMS d28c0cbb8fa24ea558f1072bd690b4a3dcbc053ab565aed9bd28aa83a36fbafa 10214 main/installer-armel/20150828/images/MD5SUMS 7d08a5c7d823980aeee48df7ec5e69af33b32da758e71b45d18b4e2bf4a88946 14354 main/installer-armel/20150828/images/SHA256SUMS 1ed3e386fb662287f943c27bedf0befca540507b4654948705e036c48d53b6c6 10214 main/installer-armel/20150911/images/MD5SUMS 8ebf74083a551d294208263250f232ca13f38b13ed9e60fc4234d7b06653dbb5 14354 main/installer-armel/20150911/images/SHA256SUMS 968a647886f71779e844ff141d6ad19d4ff800080842deac1b0cbb1e8688e1be 10214 main/installer-armel/20151023/images/MD5SUMS fb9e14b2d95861331c561b450126182d892322d35a9b1b0bf9cfe53c760693c1 14354 main/installer-armel/20151023/images/SHA256SUMS e732991de54c567276f722af50167bca737f3a6bce69155779109826bb7dd231 11320 main/installer-armel/20160101/images/MD5SUMS 109999ccf0974ea3ecce3b0b4d3100dc9a4617cef6df278ccb52f64198e2d6b5 15908 main/installer-armel/20160101/images/SHA256SUMS eb3941390e4aaafe57e88ecd25081d3995de940fe14988e8ea49c3a769961516 11320 main/installer-armel/20160106/images/MD5SUMS 469bcb1a044fc4b58f19d1e44cfdfea0f3a723f7ee2c8d3ec23ee5177d86ced6 15908 main/installer-armel/20160106/images/SHA256SUMS e25e5c6603f7e3e279957c7fa029ab00a574143a7e85dcc1a15250dd5c3e74bd 21918 main/installer-armel/20160516+b1/images/MD5SUMS 9fea0634b1dbdbc167a16234d9ad980f2675c70a3fb0c69670c2e5a9ee516a2a 30634 main/installer-armel/20160516+b1/images/SHA256SUMS a35016c77831e5757c8f56634ba1cf04eb317fd927f4d9303f509ca9f2aa2799 21918 main/installer-armel/20160516/images/MD5SUMS 8c317f5898b8af5b21d17f33945d629c5a5c9d9a00f7d7620421591afa89f572 30634 main/installer-armel/20160516/images/SHA256SUMS 7876a5c20d292f8b54d42398a85ccec97984f9c84cb159684f641719ce37a57a 22683 main/installer-armel/20160630/images/MD5SUMS e611bef6f697250244aaa2d99f915b287e2316e048a011234723f9e93f5aff4a 31687 main/installer-armel/20160630/images/SHA256SUMS 7876a5c20d292f8b54d42398a85ccec97984f9c84cb159684f641719ce37a57a 22683 main/installer-armel/current/images/MD5SUMS e611bef6f697250244aaa2d99f915b287e2316e048a011234723f9e93f5aff4a 31687 main/installer-armel/current/images/SHA256SUMS 8ed796ad892478a3d68d7d6e5ab0d4f0106a45a48a1bbd0c812474f029fde7b7 19599 main/installer-armhf/20150422/images/MD5SUMS 983d76be48052b2f1bf31cb984ec93f47468da67cbc4284288a0ed2a9a6c93ac 28379 main/installer-armhf/20150422/images/SHA256SUMS d662fd2ec159ea1b4fa3b7f2a87920434c62a5104e17a43723612f028915e615 21631 main/installer-armhf/20150718/images/MD5SUMS 130104d66e55c1da44c0a2ab3f161c72e32d81a59074987585fe67eba733199e 31339 main/installer-armhf/20150718/images/SHA256SUMS abc279bc67b5a501e02b49d224072d429132e1fd662c66d1fbb69838465afb5c 21993 main/installer-armhf/20150813/images/MD5SUMS 9dcc502decab968d2a197b16e0548d98770945b22cc2460a36ea849e821e85b9 31861 main/installer-armhf/20150813/images/SHA256SUMS 24416e54c70cf47a3833465cdc620dcd8c2981c1339fd516731f2cfb6b64e48f 21993 main/installer-armhf/20150828/images/MD5SUMS 28fb5018ad1dbc59e25f853dbc4dff064e709f1a3c61630ff73de8bd669ae05a 31861 main/installer-armhf/20150828/images/SHA256SUMS 634ba7496493b44b315e69f305cb9428101d91cd2e83a3966ea0c7fede87c277 21993 main/installer-armhf/20150911/images/MD5SUMS e739d9e0664935fefbd047ce6269aad77f08fa69442e0582353540dfb831c1e1 31861 main/installer-armhf/20150911/images/SHA256SUMS 1301b0f13d404b76ff764e1feeccafbdfe1e3c90e952374e31db294cd81c8f99 23951 main/installer-armhf/20151023/images/MD5SUMS c691189cea644d6c808ea6ebd40be096879ddea3be429ce1e8a0389f654e13fd 34683 main/installer-armhf/20151023/images/SHA256SUMS a4a46442da62a087bda4af0683aebda23e3f09ba0a0b3ea31bf0393ebc7452c1 25780 main/installer-armhf/20160101/images/MD5SUMS df3eff2db5c56ff2cca5c28f92abcd81d0ea9e934b931df5c72e37b48ee6d1ac 37312 main/installer-armhf/20160101/images/SHA256SUMS ccdb7eef05d624d0b1a35f814e9e6bf0974719fadb9de3207c0b473d0d367aa7 25780 main/installer-armhf/20160106/images/MD5SUMS b6ecd8200600151feb3ff913414b86de89a82e5c5fe16008d71bca4d57c89729 37312 main/installer-armhf/20160106/images/SHA256SUMS 59ebd429a2981cc6b5c2b4c7ac9be9a95c7c49daf4726000f351cc13612512d4 32094 main/installer-armhf/20160516+b1/images/MD5SUMS 73a2e3d2ab212e4fcb99d01bb33b4e124356abb96f668a8c094da544188fd2fd 46346 main/installer-armhf/20160516+b1/images/SHA256SUMS 36b4665344f237a0a8bd23bf02c25bb931b4690b9cbcd030ad933db204d46b99 32094 main/installer-armhf/20160516/images/MD5SUMS a1607ebc68eea6a17ff10ec23101334a6492415e78afa7daae2bbe9719c2ab63 46346 main/installer-armhf/20160516/images/SHA256SUMS 6a16f9fe1409e46adaaf6e0b5826d34e8b0ab923e64e152b5952ac980b645a06 33942 main/installer-armhf/20160630/images/MD5SUMS fb9ff4edbef6f1e57aab7d6440610665d296fdc4a94347cd5d05ad1b8f8cf7bc 49026 main/installer-armhf/20160630/images/SHA256SUMS 6a16f9fe1409e46adaaf6e0b5826d34e8b0ab923e64e152b5952ac980b645a06 33942 main/installer-armhf/current/images/MD5SUMS fb9ff4edbef6f1e57aab7d6440610665d296fdc4a94347cd5d05ad1b8f8cf7bc 49026 main/installer-armhf/current/images/SHA256SUMS 0c1498d49c3cbba310a31b29563273272237c49effb30bbb57a3e618e03bd212 52495 main/installer-i386/20150422/images/MD5SUMS 8ab3f5b9dbf85cd95ca3fdd96e47f5145f9119b2501bdb4774e9da644e74c8e6 70875 main/installer-i386/20150422/images/SHA256SUMS b9e06e30940c32ad209d1a0887f9ed81026c19eef61764a4655ceb385d88f83a 52495 main/installer-i386/20150718/images/MD5SUMS 8222f2d6c49b2d0095989b568f431358ededaeaa40bf5732139a62e4709e0e42 70875 main/installer-i386/20150718/images/SHA256SUMS bf07440ac55d9bfc367c11c5c2a3c8896a3459d9405492c197c80f4c883de294 52495 main/installer-i386/20150813/images/MD5SUMS 1574b953b136ba24ca92ee02217e14d6a8bf44e7be4034dd8abefcfeb1c0b891 70875 main/installer-i386/20150813/images/SHA256SUMS 5ceba692b7179e2ec1d230f54b8d4090f2d9e5bb6240e7ee17f64f3e72bd46e0 52317 main/installer-i386/20150828/images/MD5SUMS 27009193915c55e9f85cc49efcddfcf1c07cd2907105f499affd2555791ea927 70633 main/installer-i386/20150828/images/SHA256SUMS 860b23452d3ae00802e70f601680cf315203b7f9613869b315e74334f8286220 52317 main/installer-i386/20150911/images/MD5SUMS 1a8ed126619384b1cc1b17f4491770f4fad122879048079787cad8ea580a05e1 70633 main/installer-i386/20150911/images/SHA256SUMS a953c5bf53c29404e0bc7cd7f2e160dcc608a22d2b1e6ff88affdf23b269a419 52317 main/installer-i386/20151023/images/MD5SUMS 24f172b036aa1b8e8f7bed471fc26dea0f170bd7845e9cee82f8ad85bb42cf88 70633 main/installer-i386/20151023/images/SHA256SUMS d2cba4e3610c1370f90b98b571d18804bb0d02aa947f376b75a0a6ba48088bc8 52317 main/installer-i386/20160101/images/MD5SUMS 03d1176a02a38acb945a91acbdb1acaa689d67d2fa6068d8fd987138eb6e4cd6 70633 main/installer-i386/20160101/images/SHA256SUMS 984247e818959ef73b492040dbc086a436f0cabe200f1a29082531fe0968f4fd 52317 main/installer-i386/20160106/images/MD5SUMS 186667f8de974f70672f4996fbf2da8a5aa77d25ae513634deeb6c45d7d42637 70633 main/installer-i386/20160106/images/SHA256SUMS 3e764aaeb4af7a232ae9fbdca8494a27db81b1d03f17245e50857dbd6a63f378 52317 main/installer-i386/20160516+b1/images/MD5SUMS 240d44a903ff446d26ed77c5ca80bfc0d85e9e1d4ad7d948f639c9ede532f025 70633 main/installer-i386/20160516+b1/images/SHA256SUMS cefee1ec3c91a3c230033876786d886e6adb2bad1c91c35ca889b9c3e7b8316e 52317 main/installer-i386/20160516/images/MD5SUMS 5143b9694aa02cee9150fb01163407182775a3fb0623c910a6d77ac8380a3653 70633 main/installer-i386/20160516/images/SHA256SUMS 97fc44214ab2e0c1a347e8c8e068fffb7e5f0ba5b3f4aa4dc20f8370df3ecdd2 52317 main/installer-i386/20160630/images/MD5SUMS f0c62829db0a69164a50a4523ce82725956c8fb8092b8afe6d72db92f227204d 70633 main/installer-i386/20160630/images/SHA256SUMS 97fc44214ab2e0c1a347e8c8e068fffb7e5f0ba5b3f4aa4dc20f8370df3ecdd2 52317 main/installer-i386/current/images/MD5SUMS f0c62829db0a69164a50a4523ce82725956c8fb8092b8afe6d72db92f227204d 70633 main/installer-i386/current/images/SHA256SUMS ca6b0d3105ed0e01eaa9c71d869964866af7d1a74e0e550dadc50914a6f0e440 2147 main/installer-kfreebsd-amd64/20150422/images/MD5SUMS a9708f827bd440a9d50e70b67bf62a883028f4e7b5f3fd39d7a1b0bc608cb80b 3183 main/installer-kfreebsd-amd64/20150422/images/SHA256SUMS ca6b0d3105ed0e01eaa9c71d869964866af7d1a74e0e550dadc50914a6f0e440 2147 main/installer-kfreebsd-amd64/current/images/MD5SUMS a9708f827bd440a9d50e70b67bf62a883028f4e7b5f3fd39d7a1b0bc608cb80b 3183 main/installer-kfreebsd-amd64/current/images/SHA256SUMS 09dd35d20e440d715ca8d86f8da943339bdadcb00c0e3d39c6ff01cc0d12b115 1209 main/installer-kfreebsd-i386/20150422/images/MD5SUMS 3944b83d2056f3eabe1280e5dd736b3ed463dfd516fe97fbae88f1738472e787 1861 main/installer-kfreebsd-i386/20150422/images/SHA256SUMS 09dd35d20e440d715ca8d86f8da943339bdadcb00c0e3d39c6ff01cc0d12b115 1209 main/installer-kfreebsd-i386/current/images/MD5SUMS 3944b83d2056f3eabe1280e5dd736b3ed463dfd516fe97fbae88f1738472e787 1861 main/installer-kfreebsd-i386/current/images/SHA256SUMS 0ba9585385f7e4d35f74db8d234e14e0ad97de6c2b1e58f4335b9cb61cbf4d24 940 main/installer-mips/20150422/images/MD5SUMS 637d65df2638cb80ca893ba306af0478db47bd105b8a56a0f2fbf49c8e8b1e1d 1496 main/installer-mips/20150422/images/SHA256SUMS 479cf7017fa0df7b298f3f4d5b0b767be740418344d3c82be760fe06b2852b6e 937 main/installer-mips/20150718/images/MD5SUMS 18c2dde11a8f5b9fcba1b1f31430e3f620aad095822acbdd2b94039fda945856 1493 main/installer-mips/20150718/images/SHA256SUMS 08caa4c6d8ff9ff182df3745341244ec73db6166c5b54b2f7e2ee500781c2c67 937 main/installer-mips/20150813/images/MD5SUMS 244c15ab80d120e5d1a85dfbd6c1d8602ad4347bc0ffb5b6109cdfe052633805 1493 main/installer-mips/20150813/images/SHA256SUMS cb33e93a77e6c679ccb600961c57ec4dd6dd763ef7fedf16832b3239add292bf 413 main/installer-mips/20150828/images/MD5SUMS 9eca0151d107d37fd2de77bcdcb9e110846d34bf0d6213074ec4da1eb45a7cc9 713 main/installer-mips/20150828/images/SHA256SUMS f399f9fb84ce08f7f18caddcadd4f1400d2bcb163553b80b3ff55d71a5c383b9 413 main/installer-mips/20150911/images/MD5SUMS 096ddfc60e44386285637acf52ce251d5fbb3047423d9fa913db38fea1c3af85 713 main/installer-mips/20150911/images/SHA256SUMS 413aebd444439523c581a25a7154f43d4293e2760921d030749693f23b7d19f4 413 main/installer-mips/20151023/images/MD5SUMS 5582cb89eaca93ecef34ec67bc4c9113c2e71d2c1e561870a3affb59f9a44690 713 main/installer-mips/20151023/images/SHA256SUMS 56a3061ad3d38094cfbd8ab010dc5288b8c2a1e566cd2f8707628723341747d1 413 main/installer-mips/20160101/images/MD5SUMS eb10b5016362e98c97c32861c319ddd1f30c798a9a6376adaa940823efeacafc 713 main/installer-mips/20160101/images/SHA256SUMS b262d6441038dccd17fac968788c435503a0cef2d5a956332b094079f24f9c1a 413 main/installer-mips/20160106/images/MD5SUMS 4b4b0574f5740f643ccd799e8559fb1e85083492a4a6a7f90e8fd6b2cf99541b 713 main/installer-mips/20160106/images/SHA256SUMS 3b38f9cebd18e24de6de48c657dd775d8666a7920429db1ad8177287005574d9 413 main/installer-mips/20160516+b1/images/MD5SUMS 8599f38cae8b1723f00800d685091a85cff66b5ea146f425e62eae80a11447d6 713 main/installer-mips/20160516+b1/images/SHA256SUMS 8dc96ed1d3b977955c44afe016701e843d96d87a075e347ee9b80a054fd80723 413 main/installer-mips/20160516/images/MD5SUMS 52763e668941ee05d0ce51a1d5a5f6dbf17b0da9d4a583916a7c8aa988af1226 713 main/installer-mips/20160516/images/SHA256SUMS f91ba0adcc3852b90560336e6a189b39804c137b530ce381d0876037a732a42b 413 main/installer-mips/20160630/images/MD5SUMS 9abd37590aac598ad7baf857a0bdf8fd6313a3e9211531ffaf2bec4e818a58b8 713 main/installer-mips/20160630/images/SHA256SUMS f91ba0adcc3852b90560336e6a189b39804c137b530ce381d0876037a732a42b 413 main/installer-mips/current/images/MD5SUMS 9abd37590aac598ad7baf857a0bdf8fd6313a3e9211531ffaf2bec4e818a58b8 713 main/installer-mips/current/images/SHA256SUMS d6c364dc4ef14cc10ff0db0f30665f597417574549444c2bda4ddde8e6ba49ca 1213 main/installer-mipsel/20150422/images/MD5SUMS 07340180253b71ca03bd55ae026fdc5959741054f030a11f5646d06429211bf1 1865 main/installer-mipsel/20150422/images/SHA256SUMS 715ba5f08d71ec0b1ace75b00b8b8407649305ffaf1921ed76687b993766ed72 1208 main/installer-mipsel/20150718/images/MD5SUMS 7d28e99e99b93fdee802df82d63e78b21cca67961b3c16d9c3eda65c7e993e70 1860 main/installer-mipsel/20150718/images/SHA256SUMS 99bce7bd7b99c5d00ab6824cadd5e32c6df10e5a427bb9112b135989cc565710 1208 main/installer-mipsel/20150813/images/MD5SUMS b31e9342f01944b283d58331210f3eb88c25ff49ec5a29bfaec5ce71b9a5b187 1860 main/installer-mipsel/20150813/images/SHA256SUMS 13fb3fd0b55747d66ec2bd61ba74e2d662e8fb2edd52a56b984092467fd1649f 919 main/installer-mipsel/20150828/images/MD5SUMS 3545a766cb1aefeaa0d435d0cdd5fa9af1cc23aeb5a395f4e55888c080001c57 1443 main/installer-mipsel/20150828/images/SHA256SUMS 924c013e1d964352c4eb1d12cfd81e01908cae55ae073d4aa50eccddf2e8d654 919 main/installer-mipsel/20150911/images/MD5SUMS 7f1c0ffb58fa56aa5f2b258a3e0f21aac04fa8ae71bb860f5a528896faa44adb 1443 main/installer-mipsel/20150911/images/SHA256SUMS 32ed73342a1326d815ccfb4e0f473c49cd335270e367aeedf6ac7bd39776217d 919 main/installer-mipsel/20151023/images/MD5SUMS fcb192cb44f88b090cf57f6e71a32a8507a9bce8e78dd38121f1b38d821833f6 1443 main/installer-mipsel/20151023/images/SHA256SUMS a7887d4f550161290282cd13b44be58a4b913a5d6751bb073b5c5b5ceb01d309 919 main/installer-mipsel/20160101/images/MD5SUMS cec2bf1b643fa79f4350a1c77440bb37111fd322d017bc575ecb82a3fa822303 1443 main/installer-mipsel/20160101/images/SHA256SUMS 8027d271f23fed524c7913d7216330ec2ce8751bc1e9cdb5186a0f304ad49a2b 919 main/installer-mipsel/20160106/images/MD5SUMS cfe92ddc67c87c25fe72be025c4d7ebf7cba60ece28cc86d7f6288cfa4abb062 1443 main/installer-mipsel/20160106/images/SHA256SUMS 6ce5f3702a1d270859611405b959ea29eb46e1e6610cfc18f6ba9ad5c0536059 919 main/installer-mipsel/20160516+b1/images/MD5SUMS 089e9d9a497f28fa9338377263d311a661683f3bd914f7aab3be6a86d786dc49 1443 main/installer-mipsel/20160516+b1/images/SHA256SUMS f835b0ba7e61444a3e21b6129b7c0d4bb54194581860bd46f7a709153e377914 919 main/installer-mipsel/20160516/images/MD5SUMS a5535e9140f0d0eb03a72068afcd14bfd9dbaa5c6fe599649082f105de7b5af1 1443 main/installer-mipsel/20160516/images/SHA256SUMS d7ea52e86fa59cae164b43cd3690e70ebd55d7eddd9d2148719f704d7b5cf7ee 624 main/installer-mipsel/20160630/images/MD5SUMS 05e71fd18cfaa1a23f5da37d9e65c1fd7666dc2856b23a51b8cbed86573b8206 1020 main/installer-mipsel/20160630/images/SHA256SUMS d7ea52e86fa59cae164b43cd3690e70ebd55d7eddd9d2148719f704d7b5cf7ee 624 main/installer-mipsel/current/images/MD5SUMS 05e71fd18cfaa1a23f5da37d9e65c1fd7666dc2856b23a51b8cbed86573b8206 1020 main/installer-mipsel/current/images/SHA256SUMS db19e3daaca03a155831e9b588810291489f0e82be200fab0c4cbd36d3294efa 2128 main/installer-powerpc/20150422/images/MD5SUMS 581181933b42af5df34ae56e335184a8677aa4e4bd6d2f5a6f2566b1e84fdc4a 3292 main/installer-powerpc/20150422/images/SHA256SUMS a67e0d2e4e3c1b112c89a33936bf9b8a3d20e3c6388704986f2eb62dad1f010d 2128 main/installer-powerpc/20150718/images/MD5SUMS 32cdfecc91e1a0dc1f431165f5f7586f29e55ca86f92b178509ad9bef9939213 3292 main/installer-powerpc/20150718/images/SHA256SUMS 03490e0d4bb7ce94852f8625824fec2c5665bf2d02c9c9650ce114a118287aa8 2128 main/installer-powerpc/20150813/images/MD5SUMS cffa2b9f0f2a94155ac678c76ecbb45b29dd629e849052350b0abf696b2c07ab 3292 main/installer-powerpc/20150813/images/SHA256SUMS 1261f0d22f33c517158e013993951a21245d2e4db13af75e996f1a97c6bde862 2128 main/installer-powerpc/20150828/images/MD5SUMS 391b99eac85294f33cc51ad12950ef0096034b1e23660842212fb1b84efd93b9 3292 main/installer-powerpc/20150828/images/SHA256SUMS d7ac606026dcec95d44ebd6e9bbed7362240c89269a197936c2b2cf61893cfc7 2128 main/installer-powerpc/20150911/images/MD5SUMS c876db3c13bc9e5854c95417d33d17e5ac98539359ca903c959481a48bd9672c 3292 main/installer-powerpc/20150911/images/SHA256SUMS 2cec803c37d98d6e183f058299d974b8d60668f215766a6b8318826673327c80 2128 main/installer-powerpc/20151023/images/MD5SUMS d3e29265cacfd970d940b966920b091e8b4931b068bfa18ed442dd0e0d8cf1ba 3292 main/installer-powerpc/20151023/images/SHA256SUMS 8eccc52f6c72f4b20447b6cde843c92f7b040b5c58d45aa29c97898e1ff89943 2128 main/installer-powerpc/20160101/images/MD5SUMS fdb5399c64e383faa6aa6dc3dc2b338ec424c8550419be90e601dbcbbe4e9048 3292 main/installer-powerpc/20160101/images/SHA256SUMS 8dc188bad6d7f37ddfa5d44b356f20b80ff6fff6815a6c5c9ce2d407f79d03e0 2128 main/installer-powerpc/20160106/images/MD5SUMS 080ce2cb26e47953ff05f8ccadd35a2bbdbbbeeaba7343e144b4f2b785098e5c 3292 main/installer-powerpc/20160106/images/SHA256SUMS f7f15e409260256e009cb0e6740b66164c13c0bbfdbed6f4237bc40094ad4833 2128 main/installer-powerpc/20160516+b1/images/MD5SUMS 0c3a8b1f8b5e8793c815a00315bf2e25e122e470322d855f0658fa481b11a180 3292 main/installer-powerpc/20160516+b1/images/SHA256SUMS 3b8996b0e5085dbe4408ca3d31f95b62e52815263a2a1929a0582bc50591d8d4 2128 main/installer-powerpc/20160516/images/MD5SUMS 7b033f34b23c3bf95c247478c5bb0fcc8ef21eca01fa0558cad2932eae6e7d82 3292 main/installer-powerpc/20160516/images/SHA256SUMS ba2e8ce31f4123a4b6b87dba18ac37d559ea1334890e1220111358cff63ebcaa 2128 main/installer-powerpc/20160630/images/MD5SUMS c590b5725804fbcebd681d749722590475810f81941c485439c7d39408e7de23 3292 main/installer-powerpc/20160630/images/SHA256SUMS ba2e8ce31f4123a4b6b87dba18ac37d559ea1334890e1220111358cff63ebcaa 2128 main/installer-powerpc/current/images/MD5SUMS c590b5725804fbcebd681d749722590475810f81941c485439c7d39408e7de23 3292 main/installer-powerpc/current/images/SHA256SUMS 1ddb9a9a243149f93822c8b14e1f1ed2ef69c46c79c2cc2a01c20647346f86c6 576 main/installer-ppc64el/20150422/images/MD5SUMS 914330f644847bb5401fa3160cfe2d79e50c02e3df91bffb1085081a51db23ae 972 main/installer-ppc64el/20150422/images/SHA256SUMS 53efb5fabeba2ce336785cba4b8766744c24b23791222763a4e521a6452782b6 576 main/installer-ppc64el/20150718/images/MD5SUMS 9d9c8c331935fbfd2cd239bfc5ab3faaf7287fa0d7e8b6af6f93ee537c9d085a 972 main/installer-ppc64el/20150718/images/SHA256SUMS ee2cb4908cff721076b5b788b2899a4a6fd08fc726918ee5eacb1789a388af81 576 main/installer-ppc64el/20150813/images/MD5SUMS 721ab0e17a3ba49fc1d4a7ea787353dfb25d6695a5d18456e9638570ec08bafc 972 main/installer-ppc64el/20150813/images/SHA256SUMS 952dc2697724e20b465a9c4ec4518a530cb02153b8cf56f0e14e88dee71b523f 576 main/installer-ppc64el/20150828/images/MD5SUMS 16447ed5c7b09a2c08a7c190c071c9aaa8cb2e20b6eb99cd79aa76a3288d6f68 972 main/installer-ppc64el/20150828/images/SHA256SUMS 569ea20b8badd750fdb9b1aea748db5f2d7a8c7ea75e21714dc69b8e62c3c98a 576 main/installer-ppc64el/20150911/images/MD5SUMS 7cfeffe55a40f3af38916f147cac0c7fe70a301ebd7a7ba3308f8eb86fa7baa6 972 main/installer-ppc64el/20150911/images/SHA256SUMS 50305f8f6a5ee7553e25f596d500c4fc6854d44897392ac00b6c03ee06780f31 576 main/installer-ppc64el/20151023/images/MD5SUMS 8296ee34584c6853b4718d68b1aca930466470613af840f26604876ad57093e2 972 main/installer-ppc64el/20151023/images/SHA256SUMS d0ff8c8255691028757777e5918e49581c2229962a9c74a35205b5720e9e369e 576 main/installer-ppc64el/20160101/images/MD5SUMS 7f59376f89488f4ba2cde49900a3770c3e36b18dce0b49ac76e47a9709d7f336 972 main/installer-ppc64el/20160101/images/SHA256SUMS 73f1744d28d9397dfeba4e644a6d8a5d5d8fd19c0ce2336063f2df4b966ea89e 576 main/installer-ppc64el/20160106/images/MD5SUMS b789bbaf626fa52e84a76d714b2af3cbdfb8fb3b9addd3a8b79d7473b6d86531 972 main/installer-ppc64el/20160106/images/SHA256SUMS 3b247fcbff136c542c34ed117423ff0a9fc4ba2d4925de62feacf13c1852e8ea 576 main/installer-ppc64el/20160516+b1/images/MD5SUMS 56ca9e6483a3e3e45d8521b0f25c114a0b165cd76f99340bf0d52a5d70b24039 972 main/installer-ppc64el/20160516+b1/images/SHA256SUMS 6c536a156954646d5a9b4613f319a2af5d22ad6133c28812f5fcbd711f624fb3 576 main/installer-ppc64el/20160516/images/MD5SUMS c4af2e8671e2a2e973e51cee17d82a7950e22d617d990c096e9db7a4e12a6975 972 main/installer-ppc64el/20160516/images/SHA256SUMS 2d3f61a311bcbfa4d628424fc1743b028e22f53f83d96baea4e083187880ad66 576 main/installer-ppc64el/20160630/images/MD5SUMS 976439ed51ae8ea69470581dd1ef4ee88c4fa305821530786d1be8bcce70a1de 972 main/installer-ppc64el/20160630/images/SHA256SUMS 2d3f61a311bcbfa4d628424fc1743b028e22f53f83d96baea4e083187880ad66 576 main/installer-ppc64el/current/images/MD5SUMS 976439ed51ae8ea69470581dd1ef4ee88c4fa305821530786d1be8bcce70a1de 972 main/installer-ppc64el/current/images/SHA256SUMS 376bfb191054107631fe8de2dcedcefa14b35a6e957a4210fe8b76a79a16471e 374 main/installer-s390x/20150422/images/MD5SUMS f8a4b1aaab7b502945db265c1560a47a641609753b77ed61587d68b3e0fd7218 674 main/installer-s390x/20150422/images/SHA256SUMS f5842aaa240147dd778c8db3b3bf957c76cbf7548bd2149582a11d1209561493 374 main/installer-s390x/20150718/images/MD5SUMS c28d1deedafad179325baaffa0c0f78a24b8a21bc0ea4dfcea01b54b89a0fd00 674 main/installer-s390x/20150718/images/SHA256SUMS 907f84b1f28a76b5fdb470f3e55f0571b3dcda80150d3dba7b74b198b641e1ff 374 main/installer-s390x/20150813/images/MD5SUMS cd0dd8b682a2902dabd5f9877670aa36a4bc054da19d22922cda7ddbd9bd334f 674 main/installer-s390x/20150813/images/SHA256SUMS 5a8f72684269197e6bf762a4358d29376df6f647019a566bf16c033a42320188 374 main/installer-s390x/20150828/images/MD5SUMS 32df3dc845ac26560573ac3aab101431e9951f531c53a6b03919146217067090 674 main/installer-s390x/20150828/images/SHA256SUMS 4a17edc1997b125da90951bbf25b1eacd48a1858864fa6d2de2bfc5b7862dfc6 374 main/installer-s390x/20150911/images/MD5SUMS 0d26154d6cb88e72612b3c5563202829fec6fe5c1812bdfdd12ad86819a35b20 674 main/installer-s390x/20150911/images/SHA256SUMS b490b3d5b51e989886c85cd99be29d09c19b58e754d7626cfd352d3141e988ae 374 main/installer-s390x/20151023/images/MD5SUMS e61867b0825e98dec6f4db39175bb1c76468697964ab430fceb27118bbd87abe 674 main/installer-s390x/20151023/images/SHA256SUMS d162662305450e61311ba4e674967ffdb475b909c5e2ed47fa41b1489cc79a6e 374 main/installer-s390x/20160101/images/MD5SUMS dd2b2f8c0c4fb2fc7fc19868d42da2342d21ae1952c24756f386277a57987677 674 main/installer-s390x/20160101/images/SHA256SUMS 58883dfbdd5b7941c6b4601da6f0fd27dc5f951a78a0782380e1551c08abaa56 374 main/installer-s390x/20160106/images/MD5SUMS f55533695eb1843741aee9dad3a9d9974870e49702929c88b148d692f774a0c4 674 main/installer-s390x/20160106/images/SHA256SUMS e298da017ad8fbfb0e2e8bf59c5fcd8d7ca825bd178eba110f89f5ca6044b92a 374 main/installer-s390x/20160516+b1/images/MD5SUMS cc731f7010dd8d07e572ee172c6cb9fb89a2f95876aae2eedb73e19a5bce1648 674 main/installer-s390x/20160516+b1/images/SHA256SUMS 4a37149f2de23ca21bb4d84c6c12624f24de81d37d57ec72f88600ce162ff861 374 main/installer-s390x/20160516/images/MD5SUMS f941bc95a52be3e8eb44a41c286e8b7dab0d6689f9471bd6336a0589fc5f7ce6 674 main/installer-s390x/20160516/images/SHA256SUMS b74a77a31313079a9e30f4d6c9240d623c417ca9bc28f997cc4e1715f4189ad0 374 main/installer-s390x/20160630/images/MD5SUMS f1bf9df36eec7aeadd4a945385f7961bc7245edc3b4a7926e4e836f16edeb6d6 674 main/installer-s390x/20160630/images/SHA256SUMS b74a77a31313079a9e30f4d6c9240d623c417ca9bc28f997cc4e1715f4189ad0 374 main/installer-s390x/current/images/MD5SUMS f1bf9df36eec7aeadd4a945385f7961bc7245edc3b4a7926e4e836f16edeb6d6 674 main/installer-s390x/current/images/SHA256SUMS 9476f882e92de56c32d5c1057241d2a23013a66384680922d4e65c68fbab3980 105 main/source/Release 8b72060cc35f189f55a1bf7f01d34f2a02f82ab0d21ac2e5dbd1e4e7e79f0207 35489733 main/source/Sources 5595454f6e710e828f13f69e994d5e2569b95c7082f8918f6b83e38b183514e4 27910 main/source/Sources.diff/Index 2ca55dff8b5bcd53c99457c9a6ab7089302025f78638bc5b9d25dbf1b832806f 9072408 main/source/Sources.gz da0be89f252019f12c2772b76f24e1d947b9f5ba8520fbe34ec2a8ddb8ae12be 6901228 main/source/Sources.xz 7b466e0293b45758508a090f33579a16584b0add1ec7fb9d7f553add26cfa3d7 14518404 non-free/Contents-amd64 d1e066e9180d9636424f01cf58f7183d0363a7bbd8c2524a8a005809f808ec3c 27416 non-free/Contents-amd64.diff/Index dfe9b4bf525d3b8c21a0ebc0fa8124b9ba89a21afec947fe60bcd88cedc0aaf1 812504 non-free/Contents-amd64.gz df9868d137f7f7a3e43e0e53bcfef8d9d7a9dcfaa962d0db53ae9407f9e3f92a 13465086 non-free/Contents-arm64 dcdd00f148198438d030a6cffa28b0ea35c125f9d5b5fbb5c683c28eefae79a9 19512 non-free/Contents-arm64.diff/Index 6cee186816f294b289c7f232a96bb4cb096c6467a09ff353d5901c8444efb8b7 731747 non-free/Contents-arm64.gz a6edf4cda478e2538b314b8dcfc6a6b834950fe9b378aba0e4974eb664a3def4 13478582 non-free/Contents-armel db82b93155c372f6d4aeaa641ddbf531a1c1c9d1b3c7da8bfbbc86bce5b42d39 20500 non-free/Contents-armel.diff/Index 4174fdd16f9cd2805b95b5b01fa67e887befc93cebb3914f6cfd74c1597244ad 733675 non-free/Contents-armel.gz 8e04746c8f7c7aa698e7895941d8bc4dcfc979862ee711fd1a17e35a52fa9cb7 13539201 non-free/Contents-armhf a5dc03511b0a5de95e1b2cbd5b6ad9ae9e0d9d172e90139c414157c8e89427ba 22476 non-free/Contents-armhf.diff/Index 827f9ae64d2e67734efa5a3fb8676ba4f3fb44663cc0c1d9ec109bf71f2c0d40 738525 non-free/Contents-armhf.gz 1ff8793a36b1a0db90af6efc38f80bd0758b225d03248eebfee9365cceb5ee0f 13374121 non-free/Contents-hurd-i386 1f136ee18b3ae63a19ac273dd3211b394a07e3ace9be04bd057de4be28d7b1bc 19018 non-free/Contents-hurd-i386.diff/Index e0e69f2ef25fd9c06208c580006d22d85da1090813dfa0805d36dd49434c68ad 724269 non-free/Contents-hurd-i386.gz 3febd7606c2fefad92049e6bc02e765faeaf282c62b9e63d0d7fa2845a5f0dc5 13755254 non-free/Contents-i386 0303a773478fc46a173f687bc8a6147d89b430f7e52e6581fc059b421918735b 23464 non-free/Contents-i386.diff/Index 6adbd1789477a4c9539d8353ae8b38e7764c293873dbae846a2e10ad0fc2774f 757556 non-free/Contents-i386.gz 741fab572d6cf9d1e34852082e8066ba7167b6a0cd98d0500406ebec34d8062c 13514206 non-free/Contents-kfreebsd-amd64 f5247cf283bd746f19283ba79cf8a42ca6f530710bd3e162e51f3286acdc2db4 19018 non-free/Contents-kfreebsd-amd64.diff/Index 9817601dd5d7429bd65c53e28fa5b9163ba17d02285d94d1a3de0c10aac9c024 736421 non-free/Contents-kfreebsd-amd64.gz e79b0a36ba3aad7f395611bf4fbde17e4441ae199b633684ff9e43b76cd9cba1 13515299 non-free/Contents-kfreebsd-i386 793e1143ca9af2a6f7adfca75a65aafab853a095ee0d6ba017f71ccba16c284a 19018 non-free/Contents-kfreebsd-i386.diff/Index 657b0c86081b1be93cc4b451e44da35cc534297aa0647191ee65756e37acd652 736493 non-free/Contents-kfreebsd-i386.gz 0d142d9255a6a6a9b9a3254e2a31050a97625208be9c680e810d84df7934be0f 13468449 non-free/Contents-mips 4084a98e2ab431fe43d73083bd8714c704a674ea2f9066341179fba5d8912801 19512 non-free/Contents-mips.diff/Index 03ce409381d582d0875d3deda19b066abb088bac5bd66cd17bd57210b176ed3c 732565 non-free/Contents-mips.gz 46215efab8e670ec09231851a47b21c30debc9763af9fde2289622583a971e0d 13465548 non-free/Contents-mips64el 50b2e568440f4ddab88577c6f04bd04f092861fc199e94453fccf4e06cc7bbd8 18030 non-free/Contents-mips64el.diff/Index d93ec2ef0dedbfc462dec55408b15ebb551c9e207ef0b84a97536083b128cc62 731477 non-free/Contents-mips64el.gz 73a05e461adcac92170eb10d519733fba7227a113121a5169cbb422ffd5e81d4 13479076 non-free/Contents-mipsel 95933911348a5bea482e3cf29c4bf7d9ddbf544fd10663febe10b9ffef05c7be 19512 non-free/Contents-mipsel.diff/Index bcaca5dc11a0a1f65a11cc878bf2a01249e0db0148d27f107c557363ec64d96f 733446 non-free/Contents-mipsel.gz f478ed38da7ba626a3db6ff70f354985fbbfa5bbc34bda70d1eae1481c9c397e 13467317 non-free/Contents-powerpc cd723abef7ccedb7fc32f97eaadb33c9f1432df55aa248adb50c8644f356b1aa 19512 non-free/Contents-powerpc.diff/Index 071e2b38e011051d2491aea3c99a58ee18ad58cb7b00b56daa39d839483e1124 732439 non-free/Contents-powerpc.gz 574b80334ffd304164db448054ef01cfe68ef960c2ae70c9b4c03b8890c63330 14067773 non-free/Contents-ppc64el e5f45bd5029265a6b2b04bb2185487af47c6f22d44db626d38708c3bbc24192c 19512 non-free/Contents-ppc64el.diff/Index 69c3ea08813c490034200d261814fd33b6864ffb9e2da0deae89b2069d541fdb 774292 non-free/Contents-ppc64el.gz 704ba4ff87dac7a8da5b76b50ff8755f7d480f28758f161d1e6194f5b2b03122 13475399 non-free/Contents-s390x 931eb1d031be7c97d082e89d5a62ba4ae7529a79c7c5819ad69c4d354889810e 20006 non-free/Contents-s390x.diff/Index a6958dffa25b6d2eb985d2aa7282e3bc486ed7f4f33f70728f8c7a1b78b84de5 733090 non-free/Contents-s390x.gz a210ee4eb96e6a1c0920c7c1305c2a04c3123098acef8ab203af955d35be8d85 8203337 non-free/Contents-source 5a61286838c2f021fccd6d2d8e8f6117f5ccf73cf8f11e206d29d94b0cf7fc35 27796 non-free/Contents-source.diff/Index 29daf88e88cc7a443818455ac3432505441681c64eee8fb0079c1e55b7382ce6 878132 non-free/Contents-source.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-amd64 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-amd64.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-arm64 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-arm64.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-armel 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-armel.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-armhf 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-armhf.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-hurd-i386 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-hurd-i386.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-i386 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-i386.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-kfreebsd-amd64 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-kfreebsd-amd64.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-kfreebsd-i386 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-kfreebsd-i386.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-mips 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-mips.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-mips64el 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-mips64el.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-mipsel 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-mipsel.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-powerpc 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-powerpc.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-ppc64el 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-ppc64el.gz 46cd40224a17d73f2128f9165814d5a8050dc3cf3b4a659581adf2148c2404cb 451 non-free/Contents-udeb-s390x 0cff97371dfefacdd754bb45d9e55f0c494158613fc46202ada07a93386a56ff 271 non-free/Contents-udeb-s390x.gz 4100d97c75adeb569fcdfff7aec147bc292062bd168a365703b06bc93f63ecbd 194055 non-free/binary-all/Packages 7d2aecac95ab2512ac91d40d97cb85e22cdb0fe7871b52aceaf459a8b20c74f9 52716 non-free/binary-all/Packages.gz 9dad80df850007b43edace6084987334801b5da517266b05b7c0ebd4177668fa 44872 non-free/binary-all/Packages.xz 51d50056fc0cb0e4394cad60c703bdac7c1a8643df381e7d011347be4b3ba13b 106 non-free/binary-all/Release 31afb60582d59780b4ce929dffe3e5b22533b981952304c97c8129428a485f4b 372384 non-free/binary-amd64/Packages ef49a05ceb3651f1ed050e8b0fa0a5cbe4646e86fd460b62d557493a6631e704 27796 non-free/binary-amd64/Packages.diff/Index 2475be32ba60366fb15d0d679e5bdd5d89211d153f967d8ec23921915be0bfe6 94091 non-free/binary-amd64/Packages.gz b9b76765cb3c02bbf69fb16d9f752419f95d9ede61983933a640df1697811942 78516 non-free/binary-amd64/Packages.xz d60bd5af7b9989c00aa24c2afa89d251f7ff5fc0bf9fa15e47afcea91adcd051 108 non-free/binary-amd64/Release ffddc03eec49bfbc2363a5ced1256c678cae4f4bad6dd515d8cb1eae66db093f 228434 non-free/binary-arm64/Packages f98c6d2889149a5e15080cd98c3e7c74b6d9791292fb6ba988f5d8deda19b58c 27796 non-free/binary-arm64/Packages.diff/Index 86368efe466ebde79711c8a2e002ba724d2727f5c5ae17534dc4bcaa6496343c 62112 non-free/binary-arm64/Packages.gz dec2a87e49d875553356c79c0a6a21ac30f78832afaeca6b2b40661c0f66aec1 52564 non-free/binary-arm64/Packages.xz 946c20f0df385f2d7233b2ce90460bd0894394e8b1bb6b2b00293223813d27b3 108 non-free/binary-arm64/Release 528cde6f11e2bf0572911b4efbe9ee21886d32e89646f8fb82a6da6a536c3da7 235109 non-free/binary-armel/Packages 5102efe50ebe046d3cc463031dff318ab14a3c7b6fde6c9aafef5077eede6f0f 27796 non-free/binary-armel/Packages.diff/Index d045de146a61d117a0aafae0cd404cbfbeb53bf84d90c6be493cfacfc33a8d8a 64174 non-free/binary-armel/Packages.gz c102d5df5a15505d3951895feb43d410bdef34d9394ed02ea4d0ea1c7781a000 54144 non-free/binary-armel/Packages.xz 9984e12504679bfbd364ebfef3ba6406aa0445b71b5b98f52567ca60b23dc656 108 non-free/binary-armel/Release c9a83eceb9f6007530993da64cd2bf00f22db724b3ee76889dd6268b5b8ccd49 275515 non-free/binary-armhf/Packages c28a57071ed9c7696f4e534ded5ae872706a85aec0013d32ab516599a54de91b 27796 non-free/binary-armhf/Packages.diff/Index cbefcdf70d1465762c62fc6aacd3ea3e652349b91fa1fc8e6e07fd4cc788231d 71048 non-free/binary-armhf/Packages.gz f9ddf6b29f02ff4142190106e3999fd756aa995cfedf2f95c6082a18923ccada 59664 non-free/binary-armhf/Packages.xz 4a0d4b3cce6c96e15e0111d7c66a6db69d4bf177a54553b7dc4e024f780bc2f1 108 non-free/binary-armhf/Release 5cd60593a1d15255b9424e3525ba5b08608ec8bd20f17d958ad1c80d40fce683 232894 non-free/binary-hurd-i386/Packages be52bd25199806cc2f88441a8ffff1726b6653c4aea91618ea6fa1d10bf4b203 27796 non-free/binary-hurd-i386/Packages.diff/Index 00860357838ea3b12f64e49125042d0cd1cb324951db980a99c3c8e0ce093626 62661 non-free/binary-hurd-i386/Packages.gz eb79813dc8bef64e642ad912e52d70e8dadd60cc27caf9b261b3eef1fd201dcc 53192 non-free/binary-hurd-i386/Packages.xz 881c495ebeb1c4eb5a95f394574a6114c2bc0f79b8730ca7c94b1a5d152211ac 112 non-free/binary-hurd-i386/Release c6ecce76604875f59e5eccd093540302b2d8365ebed9fed4d4c6f3804a3e29ac 337604 non-free/binary-i386/Packages d28bed36917ca34b84e41a180f438d0335902b370f02d73ab280ca1800b47ebd 27796 non-free/binary-i386/Packages.diff/Index c88d2c07afa95c9bd9ffcd7a037c6941a3686f6ff16980cd7f23e04b09f0167d 85001 non-free/binary-i386/Packages.gz 05e517cab2aa1eb7b475c7042df113ad58f7429eb8046bf3ab0577b21ea233b6 70972 non-free/binary-i386/Packages.xz 8dfb106459eb8e64fb45eb64cc9149575f3ede58182df83611126833f8d0e1ca 107 non-free/binary-i386/Release e782729775b3b87aab318a890433857faf6912531b05eca842e4fc6068023273 238159 non-free/binary-kfreebsd-amd64/Packages 0e5182638df8382789f119c448cba6a90256a7b930acde2d55ff4786714f4d25 27796 non-free/binary-kfreebsd-amd64/Packages.diff/Index fd9d8e7caa7d8ddc86c0c045be22bcb977e2fed06252fcfe5871e85f6553f215 64110 non-free/binary-kfreebsd-amd64/Packages.gz 802fe13e3c4efe2d508fe54a667388a6e253de46bc06065f9cc759a685f71ee7 54172 non-free/binary-kfreebsd-amd64/Packages.xz cf14ba1aa77c9bab4034aa54aa6b65c064aab1e21e83efd216b550e5dfb2f8ba 117 non-free/binary-kfreebsd-amd64/Release 2d101483ae233c09f62f1e81dcf3af439afd3f19e72510e232f0f92017912cb1 238774 non-free/binary-kfreebsd-i386/Packages e3a2c5555f233eaa81cc4b8e445f70a1c4628f9f6a8ece1a306a62e3f19f75de 27796 non-free/binary-kfreebsd-i386/Packages.diff/Index b413bc3cfd68b6a50b494d6ecc46ec68548e649b6a19d63ab022f3ac7dcb0b50 64166 non-free/binary-kfreebsd-i386/Packages.gz 8eff574bdf29ea4d897add8d8cddcad50ab393e8522dd4425c3bd48647be2c4f 54388 non-free/binary-kfreebsd-i386/Packages.xz 6d5ef61e0b5cd01d118249fe98aa27b6bde15b8d7a13c0ce0faf90f75a891270 116 non-free/binary-kfreebsd-i386/Release ce1873dbb986fc660704a90ccf32f9e7246f17b81116bdb8d7af6885d9c7412e 232072 non-free/binary-mips/Packages c55b6b1c99ed805832225c45b2a0f61030d4245b1ad326d0da1da4aa45f8f762 27796 non-free/binary-mips/Packages.diff/Index a531514c26f99ccce30136e76851be3575aef7a1f9b946f1d50c719c9c4cb1a0 62918 non-free/binary-mips/Packages.gz 439e13ad8a83d4d5ca579093d4b282b30d53783635a2e1bc07c421cdb36a26ea 53340 non-free/binary-mips/Packages.xz 143ca71226997dd2873eee4118fe26f3443f036fead6ff149c614e2db5f22202 107 non-free/binary-mips/Release b90d0bbb7c1a0f2d8d4b98be86dcaea60b31fc0cf478cb7da04cfe1c7a4765c5 229428 non-free/binary-mips64el/Packages d8732d7d6877521b04c90616980349bcfaf86a7d9bf687c297ca5083de1cac4d 27796 non-free/binary-mips64el/Packages.diff/Index 90eda650543dc7e647337ab44720c8c49a2ec851b4011d6d57a342eefb34c428 62084 non-free/binary-mips64el/Packages.gz 14d9dfa46ea0f85c0b933b7b21aee9dc40c5e0726692a6f2529c3f2cf609c3ed 52544 non-free/binary-mips64el/Packages.xz 8fbe42254ca8ac1b019a4fda52d912da4b93e95ae0c9eb04c7f4748d0bc50057 111 non-free/binary-mips64el/Release f804875d9c4efc3a5ae31f28aaf73c89eb30802d0949e8cc878482ef9ae7865f 235706 non-free/binary-mipsel/Packages 7386fde81da292b36599adea45e373aa519313f397140fa32a5f7bc641c9e825 27796 non-free/binary-mipsel/Packages.diff/Index f79838a5e4e4d2cf79abebb6c27cd77695373e78015ed1306d8a716c56921772 63921 non-free/binary-mipsel/Packages.gz e35ec9bf1969deb8f81a14b8859cafc090d9ead6e416ad2d088af42acce53171 54036 non-free/binary-mipsel/Packages.xz 32e889dd01e87d5bc03c0ef9bfc68601c2da6e777369be839f6833faf7586c05 109 non-free/binary-mipsel/Release 14a04b696178a51762c32a2e0e17c8b22686c1d334b15141f4255f7ddc42700f 231104 non-free/binary-powerpc/Packages 0443dcea310ea31f428851d281f4664cfc62263056dfc16b110cdbc749e141d0 27796 non-free/binary-powerpc/Packages.diff/Index 00c1c45b5ffab213413f780d808163dfde5573e8b1fd30453043bb23bc2d0dea 62821 non-free/binary-powerpc/Packages.gz 079d8ee2b944ba6a0407627933ef4b049a15e3c03e0bb3f37df816fc8d62db7d 53196 non-free/binary-powerpc/Packages.xz 2f69def9d89a278cfef84fd0e096d9f1ba890c128f9b6a40c9b6b71fe968af8b 110 non-free/binary-powerpc/Release 131dad26e72283d10abe76613438ace2bf68ea1c20eaa0e39e3576ba61ea39c1 255982 non-free/binary-ppc64el/Packages 2353851e1467160d0ecda93e72f892b2d1009797757e8feb6ee8014104e9983c 27796 non-free/binary-ppc64el/Packages.diff/Index 642496bde584ac91353c4e18870f21dd2c1bed5fbd8c73ccc32b3084130b5659 68178 non-free/binary-ppc64el/Packages.gz 380f53e5feb50fa52ae904a4b6f8480141706bc9dfcfe7ffbe938372a7d7cd4a 57476 non-free/binary-ppc64el/Packages.xz 970ba8cf785da4cdf0e7026d87608221d0aa6b0b4661de668a45bcab3a6bb9af 110 non-free/binary-ppc64el/Release 76c0086e000bb9708e4e0d307d307b6a1b98c8b75f1a1d50789b8c2421a06ebe 232449 non-free/binary-s390x/Packages 55bcfc0566e33955b96cc779c40837d5e40349b2c01b18133dcd6e120b7eb4e0 27796 non-free/binary-s390x/Packages.diff/Index 27ba2a8e2f933fe69b5301a048f98f046e29558ac03288a43f50c25018d54317 63088 non-free/binary-s390x/Packages.gz 986836565285be47ca418f6e4f1e6f9cc9a99fddc28ee407afe524b48e1d6e64 53436 non-free/binary-s390x/Packages.xz 952da0e4c619530fb52fb6a84da94ab5d9b10e103ff00929787bb521e338a194 108 non-free/binary-s390x/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-all/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-all/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-all/Packages.xz 51d50056fc0cb0e4394cad60c703bdac7c1a8643df381e7d011347be4b3ba13b 106 non-free/debian-installer/binary-all/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-amd64/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-amd64/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-amd64/Packages.xz d60bd5af7b9989c00aa24c2afa89d251f7ff5fc0bf9fa15e47afcea91adcd051 108 non-free/debian-installer/binary-amd64/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-arm64/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-arm64/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-arm64/Packages.xz 946c20f0df385f2d7233b2ce90460bd0894394e8b1bb6b2b00293223813d27b3 108 non-free/debian-installer/binary-arm64/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-armel/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-armel/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-armel/Packages.xz 9984e12504679bfbd364ebfef3ba6406aa0445b71b5b98f52567ca60b23dc656 108 non-free/debian-installer/binary-armel/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-armhf/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-armhf/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-armhf/Packages.xz 4a0d4b3cce6c96e15e0111d7c66a6db69d4bf177a54553b7dc4e024f780bc2f1 108 non-free/debian-installer/binary-armhf/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-hurd-i386/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-hurd-i386/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-hurd-i386/Packages.xz 881c495ebeb1c4eb5a95f394574a6114c2bc0f79b8730ca7c94b1a5d152211ac 112 non-free/debian-installer/binary-hurd-i386/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-i386/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-i386/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-i386/Packages.xz 8dfb106459eb8e64fb45eb64cc9149575f3ede58182df83611126833f8d0e1ca 107 non-free/debian-installer/binary-i386/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-kfreebsd-amd64/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-kfreebsd-amd64/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-kfreebsd-amd64/Packages.xz cf14ba1aa77c9bab4034aa54aa6b65c064aab1e21e83efd216b550e5dfb2f8ba 117 non-free/debian-installer/binary-kfreebsd-amd64/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-kfreebsd-i386/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-kfreebsd-i386/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-kfreebsd-i386/Packages.xz 6d5ef61e0b5cd01d118249fe98aa27b6bde15b8d7a13c0ce0faf90f75a891270 116 non-free/debian-installer/binary-kfreebsd-i386/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-mips/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-mips/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-mips/Packages.xz 143ca71226997dd2873eee4118fe26f3443f036fead6ff149c614e2db5f22202 107 non-free/debian-installer/binary-mips/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-mips64el/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-mips64el/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-mips64el/Packages.xz 8fbe42254ca8ac1b019a4fda52d912da4b93e95ae0c9eb04c7f4748d0bc50057 111 non-free/debian-installer/binary-mips64el/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-mipsel/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-mipsel/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-mipsel/Packages.xz 32e889dd01e87d5bc03c0ef9bfc68601c2da6e777369be839f6833faf7586c05 109 non-free/debian-installer/binary-mipsel/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-powerpc/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-powerpc/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-powerpc/Packages.xz 2f69def9d89a278cfef84fd0e096d9f1ba890c128f9b6a40c9b6b71fe968af8b 110 non-free/debian-installer/binary-powerpc/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-ppc64el/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-ppc64el/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-ppc64el/Packages.xz 970ba8cf785da4cdf0e7026d87608221d0aa6b0b4661de668a45bcab3a6bb9af 110 non-free/debian-installer/binary-ppc64el/Release e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 non-free/debian-installer/binary-s390x/Packages f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 non-free/debian-installer/binary-s390x/Packages.gz 0040f94d11d0039505328a90b2ff48968db873e9e7967307631bf40ef5679275 32 non-free/debian-installer/binary-s390x/Packages.xz 952da0e4c619530fb52fb6a84da94ab5d9b10e103ff00929787bb521e338a194 108 non-free/debian-installer/binary-s390x/Release d0f99061985c22fc085c6ae8eb3c1b49faeda978248b6dc9231251e07fc55d38 47445 non-free/dep11/Components-amd64.yml dec450f8372f0ba0292e6f59114d10d04de78aa3c1d0f905cdb60c31eb525cdb 7189 non-free/dep11/Components-amd64.yml.gz e91a6c938db083f3c72448e7d6a14588a8d8372026ec9da858918f05d2998c24 6624 non-free/dep11/Components-amd64.yml.xz 60c08380e7da73997e7fefef47aeae1cdf0d06eac41b8813c44afe23cbebf7f8 45122 non-free/dep11/Components-arm64.yml 947899ac45c47b076521e6390a29cb0703cd14e691ece7768823a2320bdf209b 6383 non-free/dep11/Components-arm64.yml.gz 8370528f52c99e8dad9505e8e0cb3051d86e01778501a5f7b522243296844894 5860 non-free/dep11/Components-arm64.yml.xz 8b7cc63f3c1a7f5138e2c13535c860ddab680f6321481c9a1d225b64e9ca6014 45122 non-free/dep11/Components-armel.yml 6d0000443aa725f3d62662c96049d7d96c67bd0cf132430e5c4e48d6a29143e2 6421 non-free/dep11/Components-armel.yml.gz aff035929971ae4118bca9cc2ecf87951270665db88be40545bf916d46feb627 5868 non-free/dep11/Components-armel.yml.xz 7c8f94dfed05924a841405c4e54638fa83befb07377ed52ac3f799db65f25808 45122 non-free/dep11/Components-armhf.yml 5f1f7c50a2457f181e7d6381cf9a99e5a0bea573610ee586e402eae2f911ab52 6463 non-free/dep11/Components-armhf.yml.gz d2ceb81fdd755aa22f5b2a32e82e4b4adabb48377d05875ad2c98db0b15dca1e 5884 non-free/dep11/Components-armhf.yml.xz fd3364d4ca33f527b0814355ff6d95bb3eeb1e56caf65c8c60017de8f46cea0e 47903 non-free/dep11/Components-i386.yml 707b8503d1752b4d82d09478590494c5b2d78591fbf75834ec9f8477292412bd 7512 non-free/dep11/Components-i386.yml.gz c8d50634540d21764ed49285171e58d31717dfc57849bce6ed1acd42a0d00837 6904 non-free/dep11/Components-i386.yml.xz 06e3abb8226856dd6655d842288920851c52cad5b644c885502157121d83e7e0 46201 non-free/dep11/Components-kfreebsd-amd64.yml af142b0fd62c51b911642891d4eaf203ac36022c99f5d05b03437b65dd233908 6927 non-free/dep11/Components-kfreebsd-amd64.yml.gz b8337441fc10262af8b3f68eabc24ced512cd2933a53404ea1e67ec8196647b9 6344 non-free/dep11/Components-kfreebsd-amd64.yml.xz c8fcfe26fa32623a4ade65c2fb740185cd488ed9f0b7eb26939901d0da0f1cd0 45122 non-free/dep11/Components-powerpc.yml 845f19e2cd67e13e23656fdfeb91e7353de66aa3e097bd40f87e3f14adbdfac7 6441 non-free/dep11/Components-powerpc.yml.gz 0a35a6fbabeac70a8a915e18946feea69d47a9c562598aac613dcbe2b1dd0739 5896 non-free/dep11/Components-powerpc.yml.xz 29e1afd7d3ecee0ca8926f94c1ef267374309fbea0315ce481cc94165d3b5790 30720 non-free/dep11/icons-128x128.tar 9aa465bbd41ea479173c3d49a216abdd64d7a7e945315d2d7316cb6cd21789de 20291 non-free/dep11/icons-128x128.tar.gz 3b7328ed6d22dccff7d283a2d72292dcce91b6f27d10d78033f37fb006359ce9 17920 non-free/dep11/icons-64x64.tar ec4198e6389982d54d353d63f7390352651d59acebadeb80b237d86400774e63 10022 non-free/dep11/icons-64x64.tar.gz 6fa6ff9f2b38fe64c81201a8fb73122f9725b00f3f43d553753cba01f7a3d5bc 374836 non-free/i18n/Translation-en 5f2db90b88c5c50154b27faf9cf308eb798268ae3cc38ef15beff9e2412e020c 79968 non-free/i18n/Translation-en.bz2 698511b9e33017225786d93c58b1fc531c4a1e3e615532617ed2d11e10c87656 25336 non-free/i18n/Translation-en.diff/Index 1fb81c024b1b640be3fdc17b17e9103eb7f7f712db540eea6ca59c24193ce8d6 109 non-free/source/Release 1f280627c948f7394f7aafded1aef1d8c0b3ebb78708928d6c4fc04c2e44fee0 344837 non-free/source/Sources a79903aaa72a8fc8f62f8813a44b1bccaa0bf690f17e30fb99b7975eab3d9e9f 27796 non-free/source/Sources.diff/Index 7719c8e965535f0df1f01a4c6bef2d05911ce17f3f5cc1a20b7f1c304ffa5934 98981 non-free/source/Sources.gz fab9e25c7264aff82e37039291a582d882550b2daa36881a572dfea00e56c0f5 82868 non-free/source/Sources.xz -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAEBCAAGBQJXmdEeAAoJEItIrWJGklVTTpkQALfUWL/coZ8EAVgE/YjDmGqQ o1wfpd5ffQFESPXVIFR4uJMlM/lmuST2+UB0DZB86qXESPHo56zXngPj3tDRV1tU xHbMtmZl/gLDmzISSDJ52+i+Ma8hAXUp3a1t+8QjSUf31OUPb40+mj6ft2aSJH4X 8juskoA/1v4VUCuGDiE1LD9EtrxJlGDAbh7h3xkbrp/ISwy9NC43sgsFOoEKip3m ZxkjgZ5VHFoKcSMgpjkkF/6kBhWHh7s4H8UA2LKpLPzIzuNNepLTQvWeFm/PMdcR k5kVtMTy3Vnx73wSbVblGZ6fNroO22AgAUCqk5u7oKGOn/A4cQqKxrpCvWVog1Fw wPicGkOELP1v9IsBiB24W4cBMN7vciHgssL8pgKCEn2SZVRT4SyofbwXgFckXYF5 VY11FFTXsfQXHn2o2E03sJkJ+nJIL1i6iiZXPrGfmg0lews9NXfloH3oLGobJpEd +T9X4NOWIFk8UyAvEuCQQZfkp9eQcXvhKR+Lmp4Zlc2+GcOZC+00vBlXf27hhhFF MdjwMGiTGxMFj+/CSTEMY9/86hk4W+HkDhCRFxsEd8HcCFEaPIjeEVSFi8RXbu8g YORq1kwuT4JlYA9GO1d2fMeNN2J1GJOunJeuvjXJ+sR1m7ybhxcMBYr30GsX/yQ8 XTd3OeBH1rfyPJIT8JFGiQIcBAEBCAAGBQJXmdEeAAoJEHY40EQrkNAQTpkQAJio 4zB+74XQpNoLpKSaebOmjW/GqlBbbex9j1t/84dgl34Gy7xeZ0PMTugCiYb0IipK fXYFoqij0g2n4/NWpyxNEFI0X+EWu+MgIAPAfFgZIbpJourKSc9NQPlDc3z4vLvW ylIZ0jxYfmwT/gaDw6c0Eq2eLcoi46UDnj1YdByrGfN47BXSzl2eMJbHwFuc9f9q 5XUSOMCPqcPqELUlCOmlp/858dE1ePCnaO8/IP8Dr48jpJhzaUtaJMdYxZ3mbog5 2mrCs6a9tn8QmkKTHZ+X5HwA7vgp9Hxt7rB2371ZnEdhUj6J3GsFSnQ52tzU9nKY 4AJbm+oK6o49dT3cO8EXFmH6z6F8lZXvAq112sA+5E6mLjhcQOMBuTZIB2l8Xav7 nkCtZFkDaHs8h1Tafs3xneMFU1N0zjirLWQ1DLakYZf74L2oyis2BLwFnKWyD7tn ns9VtiqgrWhCAAWDcLKCHOEVUuYV/7VgxvZHRiyK/SdUm3ajHYR2WqDckuuedcTe BHoUOiCQ9nVbhugYZBntFX8wKOHix3Tmp+JCnM4SWtwGNu/lwjZMqwIxvKY93+CR /D8cCVNvWplKPgmPz6jGRg31/R5Oq2HuMM5iByQOjk5dnXfy9oKBjtD0kB2Fa7Pe rWqOLXoUsaEsByI9NqinUBxI8duCg83YIrXZDY3u =+BFo -----END PGP SIGNATURE----- appstream-generator-0.8.6/tests/samples/rpmmd/000077500000000000000000000000001416066706400214445ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/rpmmd/26/000077500000000000000000000000001416066706400216735ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/rpmmd/26/Workstation/000077500000000000000000000000001416066706400242175ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/rpmmd/26/Workstation/x86_64/000077500000000000000000000000001416066706400251555ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/rpmmd/26/Workstation/x86_64/os/000077500000000000000000000000001416066706400255765ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/rpmmd/26/Workstation/x86_64/os/repodata/000077500000000000000000000000001416066706400273755ustar00rootroot00000000000000359c4a0d13731581be86f571f3a672e82fe52f914f00929243f17c16da820326-filelists.xml.gz000066400000000000000000000101341416066706400426160ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/rpmmd/26/Workstation/x86_64/os/repodata[w:SxCjά4m愒 9/gQ%I#نpKRHc[۶_qT[z٪׀yܧ,j AD5}=R?',}lN Dgs}oIBiuTZK!/C ng ݠw 7vaoxmu{`kNE0a{4ND*$n@EF? VP 7ҹ?f^w1hN7'q)s`>d\[SOǧ'4ݔF~g]츃_\osZІA F}7v;~[l sxwC.Ƃ2%?%g'%ZPG\4bhPc3~8.%qBDŽc϶ޑ(Yq<_gU."6Ԥz2E4Rg*RTyW)h\F楖{rz=LP(͒OOǃ_^>N/PȽߟ.@>5fAKT0vC%oCWϒt2<7Җm:6r] J[X{+'uSs )׹?ޑ{=n&f?c?%|z3="d+ג9w㯳:Xq|v#۽;~mwZ?jۆΰKFx^;zꕆfd ;X8 [%ym9vxeg: _Y-7Jn7l+' #H47U\/n%01 1$v[ /@ioyP?|Ply~m&|z{楊F"a $f $cU8(S a+/͐-̿5M:^YXxtU@0Ÿ5i#Tp[#ΔY@*hiYBG7 p1za3tu8FľKH +vԱ9S=!W rx!6(Sd/ _ h}VEY+Q`5 Nzz0drqE6 切exs js=CN-~ic>[Dĝ鱖+rM\ْuA%&͓̿O̻խ`Vg ƸN(QRJP7U JĩnB'h|$|mhc({̎OpFA ,B?3j`CB &hPύ=LU9 0cwH3=iPAFsd1Jze;u a̔ӶΑb.*U7{k qؠ4([dS̱6t\`$ӓRjWGzň> ЃުqZ b*q<lMaM. pn fHn`0<艆jBMvR2X"Ogy>,Wc9Y.j 8Ⱥ9UaƽeУfb5'"=ϝ.*M& aNQ2ga].cަOJt0T~q3/=@?\u:Rkn(^tQa20c2@B~a:Bg%( )N9`=ϙHIR:j:s{N1|'؄?8[bNf W"3EP8n1pF&L{D5߉-eѼ4- d >68k=H#J>ric2IZC#Pֈ5 y^ g,"vFȟv֌AYX}bʟ\7~xPA#3[&!>0x 5 ( )-{ҫ0% VƩE^p~Y>ʭLAT#,}l,]>8^|-ՒeH:{$vnְ C7@kx@^wкF݁;jk&Ⲯ[if^2zYg\3Ӛ. 'BeݼD,3ug]ʜ\3ߦk% f\h_ag `t^Aג "ԥybfk1^O~;\,^6f+|/9W_.Owz|a=a|u37qffڒg'>;YG8n|57N{NbU9g5ws=oGL 7ÿO(>8.b߹ {ѠtAKoAv|~0r=uA]\yirdSG5{I^8;gw1봑f1fhNy˚shy7&$'<kzaCՇAuag}hI*==L4iQ6܈ZZ#oT=I|i#r% 0KGa=YUs+y$K\Vj騬ي b`omplu[t|reK QABmcmlc(o\&cǚA~Y<,ʄK[ySoiC*٬>T*>kO׆eMUHd#;zH=Aӆg,&ǞE 6-Y87ZgOhjx%<4_DXsȕ/\ {OЖ7DvYs-dre5yHpR|Z`! ڶ0me&j@lV}DTE\Ƶ担<4vWs~Bz*i볐ó;wi6ꦊ je~\4H8WN|>wʷLnjKmօ/kp>q>n%;z#ӻISkgiϺL͖m.evL ,h +fʬdt}@v=T鞺ݿ891148a3ce31e0695aa00ee72a7730e6978b049357956c81a605396453cc200a-other.xml.gz000066400000000000000000000013511416066706400416600ustar00rootroot00000000000000appstream-generator-0.8.6/tests/samples/rpmmd/26/Workstation/x86_64/os/repodataKo6 a%I5^'-P=99ԪDA\a#}SVho¼ti<ѧЍxo._\|9ڏ˱8<}SU}7weXoVdYU}A&o\ h/ rJ zm"H6j)5UK*U-Qvсhdt 7MK F]Nt,[lB.UҶ/x/u) 2CdE?OnlO-qk>%4\O~H>mvp]_4lkr EOy`L֖ n)-.DZz/Xȩ:Y\#A(&Inq90VLIiF/4+ 墔Z)2eBD8>TE% Cc)75}ׯ@̈3!V:gtHL3fGI ^wh19i'4|,HN$vwY>,K&eVrzp GL`*C0on57Ls>>m-WЂ*s',Oӻu|? aE0I-ajak,)Ja{-.jCkTVBJ8!m petRT٦qWݣ^o$kGW VMbUݰ>!ZZ-2i'%+Zyb~8>3+U9p؋%kv Hq{깤Ѵ,D4a7(M};NLq , }}8\\-iI6ƗۅM5V,ctt򚬵O.T=OE%+:kt~MD#S O.CL5r-? !tqDzXۓ m PIdajP"Ў[gCꩻtƹW5LQ2r9{dh j*a:/aϿtLwKAb1)fR W KED6R'ƅCDYCآU( κ ,en&3Hk(wca IKb1 X 'UD(g?TusZ.h#UcCceU|b/998٧owdP.+n/#*Yd>k%lGҒ?]4f7M+3T$M)N<ؙ'~Dij ]+H+GM1=/"-zBZ-m!-5<j]y{S*"c70'?E뜖sI ^kVE8S~n 7$ub?W4ÙClVͶk;V<ͭwﻣͬbw};zv1>-HݐKV.'pHҹӣR}B{2}wQLy'&;ZC5XF^kp{^b 5unf5;\ٛ:+p.Y:e9ړ (@.z,O°-o@vr1$h'+s~c^0:_9ޡYyySGP}; U-LK/m{ ɚ>}$O\_) | Y N3sON!?{qݻK״W(CG20abEEKǘ;hC_Nr騚_&/3X3n(it]x uLa S,]'増s;ڞޛLIN8p'|+ 0~ o|=\ %QISߣn=igNVϯ3L{Ïá Z8BϷ"8B-0F{访F2z ޸Ԣ~PlTyo 49A%appstream-generator-0.8.6/tests/samples/rpmmd/26/Workstation/x86_64/os/repodata/repomd.xml000066400000000000000000000030171416066706400314060ustar00rootroot00000000000000 1498315710 9e6f400df7895e52984c987d7fea543cc9ca9a87f43d5652a51f44241aaeb03a 98e300b6683be9c9e959ece52969ee32c0171cc9d922325c57af58ca2c9e4a65 1498315710 2285 9537 359c4a0d13731581be86f571f3a672e82fe52f914f00929243f17c16da820326 37006a808d524721a0e42ac402cffe6ef9c503478985569ec52bf0ed4315695f 1498315710 4188 49117 891148a3ce31e0695aa00ee72a7730e6978b049357956c81a605396453cc200a 84e64f9167328898522844619599d962dc33c3334c529b5bb512e85e850a0f1b 1498315710 745 2499 appstream-generator-0.8.6/tests/samples/sample-video-credit.txt000066400000000000000000000003201416066706400247160ustar00rootroot00000000000000The "Dawn City" footage was authored by Dmitry Tolsty under a royalty-free, free-for-commercial, no-attribution license. See https://pixabay.com/users/badzhoo-5607787/ for more information about the creator. appstream-generator-0.8.6/tests/samples/sample-video.mkv000066400000000000000000007410731416066706400234450ustar00rootroot00000000000000Eߣ#BBBBBmatroskaBBSgMt@BEYMSIfSMSTkS%DSo] e;-NRAI-,Fc 2a6s<| AN0Ǭ]*K+Y,*#5'Kf B X$mb)@P$-)zhڳƑr4(W:ǎVX2@$qY:l=욮WK9(D69EHͥ5-kpk+a?;S\!oYʇ̎Aefߢ`ß+\zb6Mx4b[Qe#$W.E6]iTN\[^U^=+$‹8@GqE?_e7_w rf '5 bJή*KEt[>nq7A>~h7ɿϬQ'myd-(9wMWz1`C7d\DlrryhC1Gi.e[Y,Wn%ҰJMPJi~=ޕvBascf%"AX1I66)YT|3v4LfUpSQj=61imr&"wy:0Ue}*l?ӵU N{S"Ii8gBE-*fĔ XVlEa$ _,Ӊ^҈dB $ܴ3*,&J)VRc*Z?MYαUz 'z'ῑl'Dcr2V-=>2[Z9 ɳy&yS ѵCV),+ ıa#'_(ehT:4p;$a4kI 7*wT^ԾP@=BQ֬[`JKb %~ 7# 5JG|bV`@lS<ɉefn5'}<"./Vc$.aCebx*vPnfPC){k5o`P _YJ#c[Iaƪ&<_Bfṕ6b .,Qr՟U{7bɎ :@#Ayh[͘Dӡj nC" \ّHoW:*RGHI!&(;K{N(2/( kT9T<'nCD%22\YYf_LVmg_+6&.|HRo߽7RY._QAspcȬmV&JOMAY_2-mYB^xkaFdwÌv*C̄O^M=#kz@uξZϵʬ7#&JzaN`ZU0jw,V2C>r%\ )iVo rXzɥsB'.z0Z$_z2P%U*ɣ.r7&rGsǛ\~lE..nL / ,\ֲDdk"##h?AfT3˩Z>L 9Z:mI\ɏ P Գ0y3Xzk(+H fT1VJ~\Tr<nA`?3G"ʆR(aPB* ɰV_L=?R6ANC' ҤaRUΰ9tg*b.7;,4V{3_R$Eda,}3S$M"u_y@?}(ZVoU ۪-q#p0=fiV$38:eRGqT_ ߽!8y]#"d8 U@MΗ 3^lȣ+6~72n! JibM~bM9B 8uʓ!.c"1Ҥlm k@1bJ_ HHB;,F≲45Ј6(MFxqy%Iq4={6(A`5 w`^@䔲Ue3)BrV?!dl:b[Ux"Di}ಠN}_yHE;t0Eѻ]Usײ|rx%;)_"e ;u- B7=& _,_K'`U>tYb!GuS){sOc mէBVD$VȌQ?x$z,M%6a{ry:۠1мy{hsv;o0)ET%9?skIv_T^7ZTZo+~ְhZ4KKv528&uƱ"ֈ3S61 n,*iղݯ(c oASm*GT}cʃTFhT φHVj΂[AqkC@qTQ~uF3N w€u7,GOLo[jҳg_hIr&nVt- g#W5j}YXE[ȚDAem1c,fGNlGBr,9c>kp8ae˾Pi\Cl4m nhӜLvtedvt'!(clhcdVх Yϳ T,}{OnYjbgqIWt<ǴT-p^2Aݶʚ$t)sſ#VTݑC N}6uWO}y5UVIpŵScKCZviWI{ˈX4%++Gx(Oˋ񄟤ZIGv9֊ ӣ/TxOF.dX' 0pE(\{/*\*:}S`REYoS+x 'E>ڦԥ8ʷZZg0h>LX9ʊrŶBIB}%~F<nVCbHw-UĬ|K#!}TBh`xK@Y$FgCv f!rſVXӾը K/ӎMBQJQ[krw\ϾYgr&9@ Wǯmr97w | M>Z^ˈx[od[f?+ ]~zV̷[D Nx&r&1zr=vl t44sn3QW#y+ŵ`ϘHQReBR IzhV{YL+ߵj,γCzz$Vn *+@5ukCCix+[iPۺR/9^?jR9?jdžJ#Tm g۠VkE;kF:!}=/Ih !Z9= ;tz@ī/*Tp{˲@Ғuh>'>ćhXp$t`[Lxw.I0sP$\\`@q_ ;ÄZƌyc۾4GdY@O 4@:[u 4Y|ߡ /Πo|K': \pc<sә񳓀<jR+hq\量z;H/L!ח>i*R6hI_\+L(Ic Cqmo2F8Yh ٝ:.Ȳ\Tco6-|9iVz˙>S8p usJ[Qۓ8P(#0Cؓ'\*:vK2B!Qk͖$W:~xI5+&h"w>ԩ "M\"6R9TlK]Umࡆic0R4S鬃P&L)~hFeiVS(4@ńw%r/S n:Oٱq :(!dUb@JK>5dEա1 K5&l~1ihU_$9{7KR[>S%R٭a:ALMm᫏7~$xt;m)˖hY\K (6* xn WRfqqj];wMoD*ejW(8 EUX%Z#lħHvx+˜;RKbܶj'cv'js-Vf=/>N|c_f3tptL\O|I'5!D(g 1 -)dZmK7VhŹQ=hEr1K%AfiA} OZQ2"F K '/k3>Kc |_̿y0QǓLz"7 F95;ԖJ \!S Ȃk 1qrwHHÅ 3S[oun F~ӟ_Lt0+@K$teq%e\Q'I=YA*lp#aƲKmàt$ZKjaf4@a C|=ՆǷLrԠ;l åNIY%!N~'*M&̙S-Iqfao.²%.'gc_ËnR1Xjiyx=Ǣ!3g~5\76A"Nd]vhKwV]]0qgLͦ0 Fg3ri8É|Vk 2{0s"(j8T50)ՇAi Ga.tBGo#6EɆv(Nu68<.g>#=$*weU?5{d`ؼ%wO;dѺʴψ5yQ4 X~w|XuYoECMD+Nv1=1$EЈBu'e\ի[od5b '}|d#rCɋT B^ I5rnZIFe$Uh+o/" ld]BAPa~Ŝ!T"Jw[tB`jKWO#v*ڛrnp[8\TwPAR,l @"z=h̘(k=`@VR>^IMMmUm!).K F4ֈ,VU45[TNt ,JpK"-^e~S%~C5/mpeY;ii?` ֥ dlIN?G>kV"N*nW9L 7Ql|Uuy]./FH"(" ̋1~ү2Lc A¸[f< 2a~<&r-xu9a4cM.q2D5B^٨!zgHcZTU$"@Q|l\;~Ďmfz̨ge-^V¼Ӭ?P 'JJɧԴGjpȈ;;*!XKpD۳;@AZ8}kD< 6V>S$v__u;-i0Q{rgz`dRSpzV,%<cwN;ISOUɐ~WH0RQ{NQ/^ jg[ ~%̫Mi^ A96@݇kZ5>Ԁ |~g =ub\ +D!YɱF!Azm;pIM ͐CK| m:C s 9\`)3 Wct (8=I%j壱A3He'mý7>dVw#O(~ĒPO. ?T')[|0#MTE*# #5rI8W%yx75;V (]Y6D;b顤[R p{t٧OA"UCﮑZ2ptUI: ߙ3Jw_GqP)[q_w CeȼJ63:чG!6J?`d&ߟ{^FWRR6whd>MQGOS?!ZA]YʠzA kl2Cg״KVHC S22D ?@@0 VK#B׷ϿJ%j_様ND-60)wZKbyܓ' EjJ, ^УU%a6A>jc~:@m8Mt u_3SRU3Oi\^ J#f")L?[&OB?¢nj"Fzp2Mx4n;:*4ĪIGڸ tօWqhSRXFrDI;: ㆮڼ[)a3 4x_)*2dyU(ɤ RR֒x AءOn2ا'فdv'&XTpĒ\m.W meP+ ĸ Ωثb X'ǔ:AP ;5ɴTˣBՁ22S?0B v G1N 82lշP֫串5~Y2v #( !X뇑r˒[dpȿPmdB9o=Ō f߿-}c'eh2ϲkXJC449-RiN߬tRrF%fHY-<j.t݆FH"OtX_V΂`ҟ6 Q"{c^*Bi罚,>ڹ!W8PIҡpqLBvU޽%` ~%!\Ib4! {B  f]܄b"A22"?@ Q B$FZT^$pCg.и_fTDm8``ޝ{$biFb|b`?Q]*0,ΎX&`"=tA6YrЊ p `צ]qG@1L+&.ޠ7kTtO r5Ql)knف !f-H|7]`&{ݿ(͡T@I$ wNϊ/c6o>G>>0 y}p{TX'`NH?oS/azD|%G@V7TPܽYIcxK >;B?F4xJ@m3 /h73wPϏc<;t!oאg5q-tLDbeh,cJT6&H l.H-ɫOPIo{]\jr8W"@=t\ ᢤije7…G 14]OA.P7ZY8ʭv Ep!oҔ*:~SҦ _̘m }!0IJbRx뙏Paڨ3' lu^hk&nF5R*>vEǯH6m>Sd[ r$&VFۜ\# ;bP|8{w~D(P&4oPbʵ Kmiac`AaA݂%08Ǎ/:9br_(Iil-pٔwToڪb*eӶP w2!J߆ ˆ a @23y· ',/\QUdNj|ًbg^OWs } bq-0 2dYgs{" gfu󺋉۴gm Sm72 HrR %Rt(f"cX -jI2Rǥvb~d0oei0e4WyCzϊM<`mRf]c} CS,W郦GT!D.GvYc=:Q~vùǂgW;6YH~C <0r Nq/r R,ldgiKJ2P{$a7tvǏ"۩IԽ#`u57Ny7Z{n\(r.W ' EʃaƙoWbX7|#B)nj 4Qv"*K 3NQ&gULk\b4bLs`hS? p;um‰{ϼX!~ %*jݐD.jqR&%12+lI 8r4Hߠp7"xcƹ.}38n{Hp%)5Oa (TҊw-Pˆ l@-`Y!&P>@+in!Gd u#uׁPA!}(m&t/a􉴝\nx=8:윫i$4wږ@o;K @_#Q}8Nq!~qFrPT;8ϫtq4{:SS:5{Z3'|dj*z ɫ+q, .$dBx?5%('IPYNC;kjiLqqN>2.S:as1R .,Bpqsy*( nQu/zT跼k=ƙg%yUX cĦ+ݎt|$H b(Bu,"h(-{9E)cM+ 0yf73q/AZ=%^WolW\"흦Aݟ m﷒t[mxjlZ**]ߍe//=XR =m6#ͅQ1o??#]nr\l 3sד\Q: >s"죫v2&|J.z!1-WL0^[}z!(E7e[&?EyjuCI OS,(ǽ M|NޥJh/ϥ\pL/+]3oD"Dv({].k8@7 )X=qQED`5V }: iUu 7B9oE鳷i=WpJ-OBˎ(ksM)SYe,K0e\E;xEY ۨ}'tgefP媂ֿ6An>p9\ =wqZg,C9ê-p3b]A'sAK3% _"v0խ,)}AT2!?7<,u+[;u2(>rEh!_)$:t| uu 䝪>NJ{L^MZ&D2:Z=\%`ɴǝd8 lb1! Hv;BAȍ~5tyEؚNN'o ڇ;ޅ[̑ODu9tB!rLWy2&$P kjq,U+vK;M)gGj>sAWJjF+{|D'Qx<1w3]5͝2$Q*4p[mR*z0Sb&1\I בyUTGs>Z?w܇ZQp26c*U əM֔]nyPl=sĜAM 7q3JQUuHbu0ELWzs,:h ITV[oKI~;ϝ5G9AUQ@B"lAL `Ll? H!aTKr51wo㼌9q<^P\- ޹֚WrG&dðB Vy'7\t"lYc5+\-)**;%չ_0L!{vԖcח>b]xiȿ NDQH% ֺIcN o+{ԖNM(p? hԌrF["!oh3kitSk?~*Tqgmua!=n,7ri-e>52@v'>T-']3g2JhHYUT3@H< $phoPMⳘλ 5{v _7@m^X(OE%+6ԨDʤ+_=L[0C(Í-VYe#+d4^ ׷ezܗ*,9y+5gO8&%"acdvCڴTv`TXVLцwV+|3߳Gqïo^MlwFBKMC8gz+6GHę[㻤>^PPwMH巩|B;G* fھIk.Q}W261|Xstq}T\Ys(@i2l^|FJ).iă=d՞/tM`~^p85Ӑ_<_%!Ȧ-ˋ6g]";b*5OrWXOg@ P`(G׌DCGW/ʶQ *37;G|c-N}ďll1xW/]Z[%3L H͡ka0q l ~jF0Hp- J MV[6<S?WxUtxYwQgx ,#=,q1#b:9;DZ7&>_v,mh(,b$fR}NQWc `.A%Xfğ2P98'bZZ%bN"AgK D^AV0s۾Tĩ2C p+M=RHeaUvcڥtWdao.-;K+ y騢_̈yM;46ƹyj/J/e&/ =qNНU=hnmQ6w7P'x. tžl$C?4e Ds22 "@(qB[@ `Ž&8Hrbht_$ oF e0Ɔ%oP49S)Ik#GRCהOD=OWgaȻ 2nh~P[ԇ"fcz@yӋ.C=ٝ62Vbxh@@8'{%<VT/Qa>7= j+zCj6|C|h)U \ݣ(IbYܞsY+YSvÅ6mx6ݕ{U  󛘰 c9CYwڳҾ!w$9dHۨy]]I U0M)_J\;m#q^%wd2G~۬#@N&Ж!vv?ZL}dF5(NgV62?_qpVS~_1jnva5 l7mf2v7ӝ:`{GS OQZ3dkBL,jQ$4-7>!$6jJVR|Ic:zݧo] 5~ \)2D+meشWEUI=U CSV&/H-LoyS#~iJ 4QtڐtE9pV7w$hv*ڥn#mB^vk4B22 V8QE[h MUD3NFCE[N"KQ7YDT]/֙3 {Ѥ,Ԡ㛀u%@(K`ߺd\U&|4MOXGKwak2Y!)tp+ϵtI(Q~Y;uvl(rʯgR 3/rEQQ\|WK~ش'Lv:?RܙC(+t ܰ d+@/bGhIT%TCC2rf9̀jͩ[㏂?屜L̥u62'_3͆+!mcd2h4!6w,9Mq`ȭ`k@ 2 60įf{'S39"5/[t˿:=@A6hËOT Tsƅ=&=2d7x% qc,6h\6p\Iu 4# }G3  :c$p+st~saԣD222 DAGB(bi!qgAy`t̳$RROW桬TA'6ܲy|& `NZ3^M.u.&K)~M:S;P/aJ#9ϓbj q&1-]NQ+t ȋ4ژp.,`|_/^ RB3eYJذIٞQ`P܂4YpyŚJL& 4m6 ; c]쥂:j\ kC>i&+M2SȾ83 pD=s*T!<2>|qLFa+NuJTcyxnsEo_)nÅEǟ?P4-ų:%ÉN,EmflYCUMCbRc/ @ѹ7jjORD5&D>ʿJ2$b< NvQ8xMqUJ &l^Nsd4=K*HТ7|,ġ#ガG`=Zf騹#C#L&SSs%I.IhEPxv. i&g B22 "$aBR.dĉ`ؾ?3eW[jsP/k %? ( pү.Ʉa약~IlSQ. Н &_4l$EzB!zC3Cf7kbP.*=fTx.+ B7–yׅ{s͌ UI>C+"Ay} cVig>N셌&j³gpZyw֠F*ⴘnS|b! (Nb%†W?v4s[Z+9Sۊ aGroLCxe:1RHɉ\ xB$TZq!H1c=2 OնO*=Z)COxxz;I'uuqoQDmIҰ9H~?#f?籱/78#X)PDEO {fjӕӎ"tG`CN%,2i:_s0#6:'orh1CT_G 00i\c{it?N~j.jj0?Jq:jy1SQ$CC>J}v:l||^Ywה#fJgN >/hbFm~qRAv1gZG ڱ8cNn-2!V5)Zl's ybNg;L}$ٗ>,&`:RJ#6\"b4.2QN1هhVv֥tZ{]u>6i7ƈnb4c&Zm}O5r n i^^.NG  GH!:xejǻ5n3<B22a@ B@_j+y%3"X01+fGOM_@ @6j%`>CO44J2晰Ɂ+." 7 ]l0RW2݊K+]c=(dr#2tR*]k̭(/ Cg Z|_54T@d4WQ@|w>JʈD\ vnT2!0 D^\m3$9_-Q!  >#xRMPȬW160׷1b!|ןx?4v4{LxR@ႥG06&NyH5om%(6m9NdjwF؅6B$ɳz%C/Bxu Q4uAHl٘!±DaIsؓ290KJM v]L8Y-=!-^I^ݿOdd."cbgL|KMi1Zp['1 ',]2Hi%~Ez1I}/(z:'':V$)j d+Q#^!.ڢ}ks{DT+g5h+hhayZO) Cu?CI 콄x.qfyZXfU]Z@$Mzl@I}!0߸gk W64MK/*qW(qy{l@!d9T$%8T}c>3닰\:k{ dwoDmLUdz*u1T/h!B؟<~> v7<h=gڣ+ ڱfmdM<+,x{đbZ#6+:ޱJrG!ݜS\ѓ"D0Sa?s5GLk5ƩF.Yc#{3.VgS EPI5YOAcb@ϮJ(t] sYGϠ#G&t[ga>/ >VLڷ32/p5D QOnC5$8f̐嶝[h&f$`> EQ:~ ׋VGmZ}\l$'TڙF3(✌@h)$5{6Ӧɕ-fϭQ+h*"#eMmoHcc쯫yA5*%ڶnmAo9zCX])i{aͭ*sI&EȆ_mXChkу wH=&!V6x.\7l ; = X\:@i[Pt cEV]]pـز^`L̵]X%r ǀa7"DKQ9"(m.њeI0',i C49NAeB~vn$V/!z|Lh94Cx7~"1tv_B~xc|&yGmgzjC[c`!cK1F6-4 'r%< gЛ;W3> ѡ1# ۹&wE{O߰2%Tse1l~x|4u< eD4}]b{{xc f_&r傔?LyGsmVx/%\w >]80/4WFDwVEf'lla>>{m.>ۼAԖp sQZkk'C&+_Z7hb! =^j9_!ȑ")K +QrH)n2[‡&?+l\pp#pmȔWX( 8ʴZqh)&wf ;`5Drڏ(:*zr 5dXmNlf}y0Ib18+QZ[pxF@}#/9utò4(D/Ç<^i`Q?}IA6dTGYE?Md_p#E(ZLxޒW|o. ADF-9N) _8RN3(—V{}8 4otiEwCQycJj?,z< ƄgM@)ĭY\fكԈ'?8#VV9R/B//@vYfQQK/:XDz=UG"Y.av6ïU֌oޙ$&UR ^&mu8TǕX:fBa4 j7 4!$;W:;mJć>wP[ZM(qyZ &|1-oePhN`rb\f&] E5E->O&S~癜mՂ<e'CRWi>FrVr<ˈ E+ծn5Q 0GxV#E动&ʼ W( BՍӇ6 e]?6", G)TȲ› T!bx`Bs<ܖ9@2 x*9#6q]- {Cw#kI inkOl`kG>|؍-fW.?vЪ&&yvǐWfufn]+A҉]3 r Z{]=pk7TlFEȥ閭}SS(=d?3of@5a"]܊SXpb3K_y *L }KD'22S?@ aY fɎ†kʇ4܅ut2i{Cn=/6&\oQ61o~qaߔhϸm-k+!;".'1 _/_ct*,HxIs9 n4:t$JenbˤNKa5o~gu5fԒ]AoNbzPwz Gؙ9pdhp~(c}dK{4zϼNC7aGtr1% f;="La @fOjK!V"S?.R?@%iy;&^LˤVMRmo2n&EVD>k־]1*|ŕpX<W?c2ZqbŘQTwwkٻ˿:h1F\lzV2T1.U E=c+t2Z4mMnκ5"7nTfInnJY%cژJp51>1:G0#f*LBy6h}S䵥.XXhx=%';Һ?bzĉC}ˊzDm =T䌳0e7=KXx//thW'ln>Kqp@!*.ِc3.V#\^&WfG9W"x-W=*5%,5S!$ RS'[x<M赧 ,D+T&L45G1cΗp nMB(d؝pzq/ zv#!qAn; F ZY̏ϖ.m`o$]9\^o# dRE4IK8!1M2/-M(Ё,ԃ\:kx!g l[0ptq5&hvUyWznlݺ c@@70ɛ+?|MvN:?;g@q`qvev&O~$2VVWoZcň̸I3@A22D ?(0ebT4`{ڛl¡1N}GXєp5+i=u)NDl`{pH1F+oXL:ZAj1p5~se n6 & ?,%?rry>$x/Le !C7cg*YBN:BS&6_w] jTs)+T\Vt[^pe"0}pc5QWWhaէiŒ KTW*(a CsT# 徰 pt?Bi#[iL1r]A#q< )tq(^a?7gKfmTT1DݢPE>^J޸  ~XyE2; pȃE_DB2 2"?ADf`*| 8 fvJ8=Ƭ8]?(v {hXoV7ڣUܙ n5]YZzoX+IIN<<6Ց"`]HBFi_? D3ЈcI#Uꌖ:jL\B S<w:&\Dj>K֥^k`Ԕlf) .};M5}d_+YҞijot|ڧ3P%i|ZV _2ݨ#s[UسH8wΣYrbT U¸ /ݚz,U& z}Z< !2}OA+TP_挳F X$"hC%+CQ<< n||/}|BEpSikm%cz Jz[wP-!'JtgtiQ^G@a_3|m4'vXͨ|MxLZuB)m]elx:1 /_U1N&\x9 Wt\3FF|fV$̱{< t)CܶUh(g#"=caQ>>exq(Vq3D<0tu+G5[6k46L# I7t1@j- $B){ɹAR_4!m_D`G|t$#xDs}ˠVHl!o!|^8E{T8H~ǍH^MR ]8iN+`PO\oCz]t\jOEE{N1vwC 7uC;xh8OSyWA38>x#4ڼ `HM8A(i?SLY>O8L5Y"u%lXDٿjA x)LK\UҊO, `?&Vu v}q>O&xě\ 6F 6e5[g"ux[&KZ:IUZ6ʧ2J R!5sĎܣ:E˷ W6B|ҘAzh*|E*.UkniK 0MV b$Zj"&Q>a8Mpa4L~lKCR?c3}ѴFz2^+' WZ^!Y!$ L3FbPI,Gc(I;\!.U0OuW2jP>ёM틼`5;zniƤdqȉ2Hz)n+r{9"hiv%8 9X(]@²WXtgeCkm&tyFom%I~p"ɺ~`/ i eRd]&B) ȴҔ(YJCk/! kj{9R)w3ɉt+/zaDy } nzkWrP t܎4ZC(LdazktEůj@ٙ[Na杦q!t'1HgqrVVL!^"K%Ař2Qï0J~(p 4X&ERV L%(lq@22"?0`pG:ۉW@T_IZNf-|\Y!}KܹL΁ցA=kQFvL)9_1JH1AXٮub@6HdKϨl]gaPs;\JTmAmh|V27o3`ؾ9Ng3ŏnz 7$vl3@6F–6ӄy/l7fq > ^ }3עT;PU'+"p,zc.?D\XH\LL>$uvXF: &G5yTrAbM@ gW+GXЖhM؃LKFt'LΚwz5\|ҞfuQe<]-H8}_` ưEL <ϑL-qBp%C{% pS;ۙαV$c%E>RX(=abMK_ DD$VŤ }\"HZ)8^b{hfK-QWgZS8C 8) P),owuc{T/KQ. IJ֮Z9~mCʷEG=ELTm/&aQH:Qk2! +J߆a0 8]M&R"QmbN(rrp[>yPό]m̆O<]nZx pݍ׾(67cT6ٌ{Xov{ESKRg=2 o,ZMu̓ cg#uxH8fXWq҉t<輯k&o)j'' '@oL,K<۟?$Lݺ2IH݀/z^c0NL51-8Ph?oP{bQKMx EB#dUy@bRL:`1 |iOqD'mz<ÎG:6ItATm/M50p#b¸4 7|z"E#unvkwtLg51(W}:QH9g5nU.6ꀃ25x (=p(̃Y*28|`WZ!W\A0bxa)+Kpc+7y%mlQ嫧|O=bIыlJ穢vmHsze對&Wm-pJ]߬([8 iUƨ6ebaj[%Fx*\2\,&u:g6'OSIf=G!zgGܬ08~!apn6&".p,p~Se-[.'1hq1y ܪdw3U30W{Dyљ]vR1#1![ qN\YOPk Ÿ8Xкzq3j m4Ȭ>X%z`ST>sUUpuvZ⺏ؚ6kBN9*x`,PO[s m?܆9/v@@] (F0rܒ'$ImVWs>PQJ"N1~= Y\yS0F%RjvTp"9p%b@Hi-[Qj 8*)Be=4 u~̩}J{>hlobQ!6~72Ȁ_<}ѷn41m čGNbJa@O(٥#ϩc4%iF9w_+`Px 3PZ!qw>4=s<vR]zE+>' UJq>O W=k*fCzKOkw.Gu6lH8\@դ3=m`fBDD d bd -gY `Wػ'dxC5q:b.|(qC]L|,QK+9+^/#Z5Z$fnSq^?(҃n 7/w< z%㩒׃aG%Gټ{Y0ũOH}Mn ǏzxPwZwzd+}mx:x(i#g#jg}^+D{XDE%0S?o4.L[w!Yj~׮4%ꧬ Ն3X.sjk*|L͸A*j:;Cd+{#lS<~AvCwS~9 7MJN|!KiV {=ݍJnvxڜj8،:_xe3{2Ʒ;'Giڠe:hLj|UnO΄سװڶ5#Uap)b)]phGDR:1-i`+WojT[mvw:*[SK7w甅xIm~ǰUpE\5h J_-q⿎B+cM`(l0η5g#]Ex$8b]ix(Pu}Px Fc4 x5X}ļ\τhȷa3-4W)PE3ڐ.l0z *! fY UMK47d33 XVC(E38K:,;>x`ޡKa_K|ipju_f:>M@|2)D_=Մ_ -@LO*!lhQIFUpz/45;fDawF:"eXRp)驡"D h8aå f%x(WID[/#c Py2R]-$Kq$W"L樮iءa3Cf+_|28Kc=֏ѻH"(k2{o.9 "+R` rs+Eec#+>QzqF Q~@'4.uQylgF#{֦[^ PUш{PV ?\RM>H |@22V0&79ONNT0ƟݍSь;^M2 ܟ)tݳjKoyT)h>!O5,yW1 f/W3vど'Ipӿ\8I-^Pһm/r #y `/tV<BgJR|憥6OÚM*e¥5j.?EqNuB]ϧuBCq0 rdӆw=(p\%OIө>.:(*u Dq%hJ/c)&#"řӀ_&rmB,'K "T*4hnseS1(# 67yҩ "ͧ!, al,{fF*<cNccX{lr^{/,/\tͧ[MϽK6,a%l*ג/03P\̓9' Brǖ;z2"ώ`)b惠!{UN?LNB̡H3o\mY{5S'A_D,<22D@$q8`b$@$ kсʓg;lfL~~Hi-s'iҲ~K:5B(%)Ե0gfT޵qU}@'ƨ|>eW%Buxt\,ٷPyeB 8qy`c30T{UPK%[RķCFe|ߠ:7q%i]߾E5!ق1 zgFA[*hT HerPJz/X%ѿZoQ:DpQڬA˔bC=P|@Ӛ p{P!^Lc( =a )OGWOGg9VW4\ѷxLu3܏ዴa2=8;~8g3屯vS[? K/ucb'A#|iJ Ww&/剗BP qg}UZ"uЍǤk~?AWPrtw0:=Jg@\t .w U_y(( N[pt gل-bMWY}OHۍ/ k;;)-_*L3[ۡG&\ZfXz'%mM97$tS+-|! kDzSJfRoÃ,ŅvӒI$q *KƴKͬWDˋJ>>v.h[KCkŃCj B.4 \eo*Q59p+V^KOʾ.Ԛg2 ǰ?|vݵ 7)::NAoM2,"š;p!l*xlڽBq.cfHEcfsdcdik*Gٟ+ t~/'?ll!"m%c΍wr:VI'>ۘ}:J֊}R"fAہf22",`H>GxOްkviK,[79$r0G GVvV۶n}l}i\h('X B@ìRq%Ň-fC%DX8_{'o7لJR5HLh$^9W$y+01+~p` 0ON SC?O2*N"/NTs`: }YwiS ) 9vXo/ d4'df+g?`E\\ ..SW.$EaT֝K-_ 9Q.:<l(CbRJeB^ax/AQSӄ2빘Z &" 8Qy^֩9+ ߥ2$,g,:TAW:ņzyţD2 2V1B )CbWr g%=J`l}pրH3zU, }?^'1IȔɬ[n)c|eGo{g AxW~؀쭜=CzɞRY!Êg`S\ҢiP/ ?&bDѳ14˾Z tjū 곹JWEnij[L=Lz ƹ*yS*¢=_xպ-Йw ؤiSw.W*x U'Tڬwjvꔾ%'oX/Be D)f|tUч֦b55ÃVe|AD3$'7.e$PC;P* 鷖ۋ`K7l 1ӭGnqUq>PhMڼuł x GG l'gW^)ᛌ@Z״>? ㋬qb =hޟ9B(g{P@}˴+^a*FV{TK.k$3Fo} VJapBgKH*˻){ĸ\R/P>l^6 7 3 ʶIޱlc$d/z;j/yucNq<ΙtFI]+at){S)B)3ֵˌ.#EMBR@sa 7K}k}n3v+ũ/<5M[8U׈ps)[:>BAS. OK$_^arXo4g4,oZ4P66]xY@XHܩژ@tPл]]$& %Y.V-.Za>T7{lIܘf)O Fx{l?(E ɲ$gUrr$GnD7K@@fR(5Ͽ[ ] CjZ x+ÿj\E{ҁ<qs @JyKod 3:V!/^$5%fC[>kBɟ0>:eŘ>‘D$d\Rc,+p}?%#i(ߐ=xc]&;ip5RYk[ h}56Ȧ 8{7ӀpOKӰ?_7q~1rbSNEE>az|y&~9T U;ǖ` ?.?7B,"S kbF eO uD+.eD):x6N.LYyqYbu $ȢrzC=22"@ hfyVMqec",GDk}h}rqLU̘꘾8յ͙4#MXCV.Ҟ"9y r_|4s5CpQbwl.D3̀aQ[^IzOa np9ay2l0*|-xK"ĘqDGJcS JȭuS\F7V 8oFА-F_ΰD-Ƹbr]fgj;/-l^t{} $.aĮk+J}U={7k#qc?b/W@YV`s@I|AxgF.7(5,CKbq|Wk',bl 2& QX>qFW9gI0w +_vh)l=_PzkԷ<u'uQ71{Cj7AJr(=h u'1ս)cVڨ"-=Z_s,|{:l~3; K}~MҔ+¬>x[s`6%cWy!ISsAvr"L̸(Q?uG;4^uQ .MUMz6(%BZ85|da&,"S,t=q^!'+_/5ĶзDC f1 Jɺ_xܴ%qQ=[hNXlWN`kOӋǓ?nk6'='.igP42@ 22V$M`_m;Kj ,bV}2iD0?˼7¤u@rh QyxHIpE8ߺ=_l#6pÝ`R&IEq:l[s]V'X2yCx722 a@$ @ aJā@7w7&P+H>S Ǐjw{*pm o46PX T}JD| /_+قgˢ$K;ec71ϑ 7 sKa)7F^/'LAg@G3{>8gGdW EOk͐lF=m6n/<cKSRgLwXk,Y wmg 0إN<A&9*f(s)UjՇ~9+-1 2Fk6:OjU6ۅx,_}k@,BKOӛQ6%-s q=*oU #xN+##@Hul?bG$j/`8.D))Ư_-gP~=a F)^£;7nס(J \oQ& Y%\c"g!io2>'4x_읇 ;=d[[x+!)ͤ"=+zM3qy*T> 1/zgPBS"TҁO+zkQnja{+L, 9:gѭ):XVWD qV]Q׮Ye*9PLDZì!QP/dFr?a||`m]!]5]D벏4R94mSY@m; E7q"N/u;BW`D te;uZXSMpoORцz ³,^㵕HIsww;k~c,0>ȿ!S[ʬ$$*Ch g{}S`2#!˟  0 0pG ]2Z6+j  1j1r0|6^9SOF?ۅ4e+;\`NnrCX HDT%7!x {w誆Ik#^EbD7ۻT&N³ڍRx /ml7Oo(:KX&Fr&@JkivtV๫!Ǯ;o$"lGn:[0e-폄@^\DG"O ò}XbȢzo7w~@1 #-;Ob:G}CJ^A`e$Sn8)8ލJOqLq<(})Oڀ2~ *@Yc~x,w ; AU$2@=&m;|pw7]`&'F!c]$:-+7P&hQ˷2V; cCrbf`,`.T/";,ǔ/ KF2zCHIbѸr>Y/BKRA/׋/*R.ӎ],EޱDqJfj["#T8ڌN>r8kLS’ɵU8FwIE`,`־o*o.wˋ͕:li^? i0VypG:Pa܆,} o$@|.q+;Vmu1' ɛgMԋ5Q0GqmJOdЍ3G$2J}ax;U eV{9D 3H$NpѮ%hxo&r[T0>3 bC5~T6pX^ԻV ~ ?;n eWET+̈́ m*XDT5ҤùXJkH Rt(4c7͚"k[մPd_8٘SOņxe%H?az S웖H1eeɏ;TAJ"xl Kl'npҌ/ ۥPmOr@X sB_))Ÿp_eǴVrO\ނ!ܥO29dbjH={kc725KR_*J|r>h:B9@er>BT8`hmz?nDP;`{!s)wW1W/wWTRu{X$%>Fys`XK~!j gVXx62#zJisv!3PRלu֡B4t=U {ɺp,a!;2zY,tI XHh>NύO|u @W~p;D<%Hx4?3sXpNJf|*yqˌ«G8{UkawZ== GN/lx0ͪI(Fn0T_!۹v1 ]w2ϒd%^pF̔*?-b4=6@YM_i@tX0Ʌ{ U_Q& )ĻJYPK,:^gqYgV %F*' [*6mJbeaןC&lmp)NRrwDQ s?`PtMxeE; E4muW\O7nE il]pB H$ BV?0=mTC?~qu 'Tjq:V x%,32q-LPIs=kȲ$4zjzS24yDxVy=|4W̙H>pM]ٻs k6n/4v)`*HVAƊFfw2 ᬙA#:y@X JJHsx. tTRe꓂4;{X(fՙ}֝º=HYeLf]ܡntQ 0~ JbU3mO#*H,5SK;k&;Bg)lTӐkz:|}/ԛF gy* "L?xfb,WcO+/(ߵ Q)ܫI8`Y+7rt$^o^%OHYfw/LsA퓦B<ԭ:R,v4 ԣfԧ7N s~-~ <S' oz W[efLk:Jp|= 5BY+ T=c R6 rKzzB?ѐ5#]絍i`.y"1O񯻹wsxUmRO!G?A(omh\tH{Mm?D^ke]>J+L*Rhz]ɺ:1x%H'L6R )uCuޔ0+k@kyLJ,G'!8Fr6VE3IݼCp:X<ߤfZ Q"ʍzK8WU4T@ߓ nrϑaS v^7w[J5LCo&h10^w/'C H3|amU爕zW{H@A J&c)tyCjEoQX-C0PIxT~¢j~fa壐'--NƼ767O}KV494WBsfjz==/t "&{r]"o nk9Z&z1!_>~H*ioJw cA,NoPWnFc R!+|;PR@VpLzKtcl!2hjΌMafGci#IwQ= Z{MJW%KqtH_0Ȅ'Qۋ]7zUth8}ݺ] *Tjdx ;w7%n~ÁǛvT]QƦqJVWJ*n$Je*2y2`]%tq#ꯅl~^pjߕ=U}q2j黡S4E'[/ X:n;_*nZJ*{W6epiQpl_f G'LjGY|Mb} YE !EeF5ZdmnME9/wL7w:w|y޲Z%{S۪ԿT]ҔR{\xeT݌G`Kg"i}7߲}3$ š"XkOu!NAͳZܠK+Z7yOLM`w,\Gɥi -xW`75a)MY`D9[|(s($h%22!D ?$0XByejJ]ƙƳϒ=Ǿ(oH,a*W_.ZMk>I6(ĮыgHLFK*e|a_\Uh kmAHщ( cNCfO5¥jKzlk5m[Y!dݜ.}ufN'RU~,tZUgμÜfRϓ9S <1/xbN &y2h4.8[inBN]+) əyrCWa.;IP@XN0ƣݲ5 2HTB͇uMi\4RRf(]߰*M@'uU=tp=k\cv[O+%m#ƶ1!w 1KG X 'NÜ)Mx#M@K+ɋ/Wq?eպ}c}%P&ˇ:dވOX_XņXnr]0S WMX,[AE<2 2""?@(DI "bTta8xN!yfːvRaZ8Ay+<}>B~ aI>`wEǓZtӫD;95_e:oFV+'r9/JKOfOjϻmJ쭂eŽݾɖ3uX6|^ZІcwkVkY!IWEcRrbx11䗟Լs@7Sc8|jɁmM%>}lK+?.B3D !Ey%J? {4 VZog$dvЇ-!Tej;uuu:zrw~E6 deA =N/2:O7׾9@j{?įOjzQ=0]!#"߯4Q5r-`nHivb6TUPF7r{/8d#jxUf Pg#)چv!U4t!f PS~bàD5:c/P(Y>'WLQI"`kMo`#枅sK~E@`>f\$4Zx?*N asQV풺gfq-}|2ty#,Q0J&lvc8D35/W Kiƾ~=jʊlgg59P~t!~cR6iT{+jc6abx !]O_E:g֜7=fX\r-6.U ]cVcxbIȄ|HrdڰeU޶tt. |O ZDbM9%8{HDC0g*UmȐnwSOVOGvn4;56=C$3*K;4#' !ȒQD, c0kPC⢶Us2%Qt> d3lND78m XqrC<+Ի `jD> I7Q) $׻Iơx%~dyd N}%0fB$$L&6@;uuEx6Fv/v`5qO,zOESca+gP Ż>臏h΃ 3TGI9Ȝq#7D=ibvx?ho)dk6-0>¼ W,3pzYf@B22#S?(GZG8 9^!*gowe89c`; ѨJ{!hcpXƮF_K*x3n3,!ɋBwyٽӝ#,ݱ&Cy!R2W,ċfhA ԊK 414Qɨ(y>S``/NԷ 45w&u653$ ɩݭţ42clFB2 2$D ?Pe`7[uWCeW@do7GQ}FTx^,jW9w^td~&NFZ!2GwGJr9DUǻ%ɶ:z~QSeUE tn5c%8@MɐG*aX[GW6*gU6.bYq5qQ7C̱Q@1R_({6j4Y(S?1*vCX']̫qGQ)+ fpy Z=NR7!øo6o ߅_qQz"S5\O^*Csu-mX@:9ŚU[C 9H8C~4ץƼ3vj$Nw6k <TrR7gJ!mZuOSZs}!~H4u0)ĶD|ms1J!ıTXfwg3f/qw"D}WUF@rշD-?ql&:l ֫0zo\ˊ]+ȫ}iY9HIUeqfÎ cަFl],B>?nTNnďf[KfO'l_=N+.TШ.Q՘Ay F#tȰ[CFKn^Y*N"3IŒMa\C./V'O F @/rz9 %I#20 A+{> ^dޠ z.u^i$gGЌ"I6Jqk|-7DঘR@rGL"$ )Qv2V>;𖂋S"r|9oi"pDZzMcVB:B;^s$sա&4ڽenb3XRoR8AմE6j*|Aw̅7uC}W3 a5sΞad|ڴ =mƝb*mJ|D`~t=6VbHl;f4y>=ǬEo@%mذҢg4&VQ{#B:jnG}/K3m'1GH8ԸL`˼J}rHc?$%m-h T>rY F4yO%F|ck0J7bUFaiggYX"$IQfOqf Mjo|ג9zSm&o8]K>wɚB22%"?Db) WMfc+iXvGT֔^̳|3v6h'p_>ֱs$uX[ M@ypi Vp"a`˙6_3sI-.WI= A3y|hQ2ת-d0V$WÊl^/|Awn.8ѴV{0xOq#6n#*)A$Sthb5ƚjRȻIx-Hq^Gr!cDejo~1I`-]&?X+X& ] `D12 2&S?@$`CP(0DۄHyUUԧWFjAZ s8X6<`0dѣ%=ƩO"u;㥋[ =X$l-ZxzmD Q[X$  Q\?S y^kޙ2TԣN'U :A1osbxk\gn^!?kK\ nDXRq$ 3"Wc3-͗ r)ϼ?\ TvM۪lTjJ=JmO@ se+P5{{(9Fª ^iuLPO=eMk)Z܅hKHD@רq@e{G'hj5)$rOH>Y(7.0q jC=)s ##4h6',;q=5u^P2@_igL<%k @A{(~ՂJHCH"oR^.y -6n\ep0C;\6>..q8O*[4WϜ//b,r.LCajv)%CXpOZz>=jtEqz㊧vTG'>Xj=U^<h*?gg5ޮqr]q6dEzi%7zeq{bMyZ@jnp kO_ERО`wx!Iw/"Ή_M:_'e ܐ޹Rz65^/(CXZW=N t)Qt[e{~1HL^9= 4y_1'rw{+n%,Lv $BT>dՃNY:h-[~uZg, "_p6f iZ܏ijq6K,1;` U- =ƶmVyJWDqٓLc4PKL;!O}C'lIQ3B4qd@Ͳ AV2S-=Xgz!N`1&UX2ʟVY\ij BfgܖD`&>BaDa;/l>m/ʠa;Ag[22'D ?@q`G9w(N=/BR̙+ WpS&)6H+P)TT5n M-ue}jZUo`o!xpxtNU ]:7*<SXM՛15( 8k*P,im(dSP mW^l(w;f51je UZ:W9"a#zk n{%U'&|p6jS'pϋLPʆ5~Q DEH\ ?Zڄlj FU2 2(h?@aD#A(p!@(ݛPm *N-Ǟ{ 7"- Ȃ2? =/OaEd״Jz~hiy* Bx׿@IS\`k++;dc50i59 R:vg`II؃#QhVe9Aq5-U牃@i XUELhKpes{r0˧q#{WW&FH]pZ7N;=<>iq&+a sbz0/=On҆_;QȎcV7-HMH.'2-ed.nPFK>8;/Ňw878%9dabj9l4J5'}D.0:˂^rP`@J61oOKlH8)?{ #:GBqP*lhsU*vi⿩W|J)7RYuԣEd7ŧWC@GeG~{F&X*YܘRtY ФPԑ2X S6$ :K)U=ge@oɉ KWL K[fd+64ylF KݫĢWzps/UEG4gvx]jϳtj{N- A&\5Π iqjT*17RG,G]"wiTkzkQF|"s#} eQ(CTqa n*?G4 j8RJBD-XnD[:J#se? PO%<烌(CR 0+G3CPuuwsZW}k}(䒶 %6o'sRT!͒Uvfl3A0+81N93= :$ҹ[&t]w &:l CiObi; HyӐ62|]zlQyP۠P"5V۹!J wsd[*ZXsЮC Ɵuz7%nYxPiyz-z3,pQ~O 3/Sxhܘc̻y71H=TODJÅȔo-}yO}Szr)[~EMگCbu'/g3!zz)iAB>y"d SXu,ûWN"3Zf輌ۂ-csZ-sb/: M!gZӔ 2Nܛi"P3:F|80QH¥%j;Ī?-hL&eI27&;X**ftjqyUlRZ8۫_ǀW+2'!CJ߆ G CSxxK/ 0G?ͱqwHݑ^c:-OWJ5GbIGDm1>a>uf \cYR\:Gj楕.l8l͹]:ؚ*JP BNqaEo䊑ߤsSY ¥.g-|$S0UǹuH(>DRu|uR]:L.dV5? l.IlDf{O&?7?nhvgL@OOCb޻]}`_ mHЇ^=ẑ6ȽP(6Wt]ʅjrHNN6N3Bbq(3@󚃏Fi b s #GK&CH mSR/nNd* nަæpM=@ں@5üOER]/4ue 15{wɕ,& 8dDX=1{HN"-jDi&jL/S{vNjV҇wMz}qRS.a.IZsϱ :Г*V9un-nJ&!6,X Ѧ;3p͛jZ JVVMDHܓm\[̬)̺ke $ 'P*W⪰oYCZx/;I(-W/N/CG7V6L@"\3]$uR%"Rm5 W2Jl3 U6c q` 1>35i _C8N4iV2XnY|KS}qC }}1z.J+}af#ȍtC$!_$u&`0ߟ;[:|bE(J[hA} :);D/mwl,pԎ\0r7a=8n@\r|tbJ^-߳Sܘ[C#uBp SfuڤT>Ta%WUbYYyw<gBysdۗqzEK>C4ZzD du6^7r dMD ;?~,p#  ejDBp ZT WH4ZI ,Y]^hn@>$){6l>\;ycy.w{QlPw(9BbC֡=|, 8"+YTn/L%(!"W {U>[[‘B|)}bah&MGlulhCWx0΅,1;;H,Pum(lY<άO >A-rj!V7퀦ۏ-ˊ' *զ:Xd_OcK9ZJ{(_^4:ODˀJ?6P-wO-b8-k%FSĒntH:tDt%؀OPnxi-JuN):3jيEKZ'im~ { (VW Mzk;x20 L*銰F!)5kBl menx?_ Cf|{2d+rz~d_Gp.}`f|:s\ ,p6 4C-J1V~ӮVnlM">]dpTuҜO#*K[ĠX`gI #*C  i"T@SQڅbv381GJ)M:l.]=ZA UV<"a& 8UyYӮ.㡉FoivtéKқC<`nTk@m??YA&w74dayRZ0it&G(ߨ @ {wWR4gh_e/-vY͙clެdhNbԖ|޸-/؉JcI}nLaY+$RE쾧^$h>(y1j~QRtK/ecu8#~Rч'V v_'6Ѓ}/׋E27^獔l n r[?(1*%3? cfv3~Z᭙R`YYni$r5P#"& 8he[6 zVuo,oNշL΅))?`R*<;?{:PR@f9|y'l݉C ae 00 -uUbSGvp6tx >Ա!ƌQz5G4HJ^2h`OEVE'{NVRLQyItnI"jRl(wWHd_΃ S<'~D.Q{0e-kp3ߠa]g@~{"d2kc3$`3΅N~Tpނ!1v5NyZmBQߐKu1RMM}SVZ$UNAխo9 2ls% z_^%56^u8-jr)rL]}iNǺ*U A $Qm?RC5>| IH{#(|dC^|Q^( )-=Oĸ,Vٌ^M\XkcB,$6)oRxx,W5n#nc ;̎))>k(OXS\hX~hĉqIyZs"U , ރ8Yxsl47 Nc;kgAsLuK&=(t4Ȝ6ϙQ~#~JKNL?xj)7[aJ#Ub˶x^aFzkA :סy ˴IVO) ra0rP>pٳt.Qٿ2U2w%f ͡:>4evRY9&ղ*y#rR6{?)2#[1j:hU;T;/#=qjuFd !M"T,ZbNxz=-Mqt J+l篕i@ Em1egAu>k}S *dLF DaXc1e./__kR(8;/׋[+x>[/E'$xd] p,GFagG(hM:_'y/@ TGnČ7 ؤIfחWzёPR/;0LFZż}tPI"B&G&{F w8fPUbF,7u4P6^1t=^B@(sE}CHV 3oEhYb/ŔvYb$ACו3OuT8 b{ZD0vNaDz7{)PPC/,'BQZ n@Z̝Yy}ZLk,mu%3RJ0fqf|XAo4N U.9ۘ_Ԩt鬏6U0}WJeWHKwn1"- a F61Ai#X.]IoLh߬qX!ɖ[8.eyɊ ."a$gass1*'o7^5$s$^w70_+#%](Olfvxr(Bng?KpnptI%, {VphN#fMuC$ګHid'p=BYA}?p߫5.1>ZF#$wǃc;\K|L2u&+ E[g}|R{3rB?>;غȘMOg0v8952Y0`Y 3`X^!`),r+E2 2*V@$ 0 `EGMx>*eN^ɶd&Cpl*_gы'("J4֤GdT}N[[?8_ɴ]ð PA?Vni(9=_I{#냙%\z.Y BxEAeݣ]-9Rbւ5(ʗ+m5%{vn [x4d6a ^ :f$=}_}nTG(`(oxMJhJl4>MNt%V̸1oޅ/}EB(>/rЏ`v8d2%l(O:BMe]%iيo!.p[MS~Qz1_Ӥr%b'GP|^X}HN; V$Yy%oFyڠhELmGJ“1@jjq1ׯ u.{RtsлQ`'ЛPIa^=ڙ.u=5? Hx^C 䌄aXa)ԝ'4Ptt1b$ ?:!IM5QAX_GN=lJNed^oyBk]j0:6B#TVVZzkN ("}U *j-jcJ˳3ZVV:#*H\QM7Y9[cЬpҚA-%pw$zSӻi7{8Z%@,N E{{7u~WE2( -uyH%B^ 1Gn{KL?wGn*\KDBo:M}LB-/Y;R 9!HbƆlbrW<ƵA&`5cKΚ-/RF^O43nwg3es932Q)vOʰ…C͛zmX􏾨XWYH![ Ov! Θ!Fҷ Q"kYK$:ؕjjvD;rnڹ,a.ፘ8x$7uԩsG.FW,<!K_qauûי oz'w)'潊eih1BqHA'JxFpuoi"ؒ']cId'*jA_: ~P^"oqkxם"Ë",E ~5ml9* UB|22+DaB8 (@6êD]csb=3-=r1Vͺo=qUSXy܂ Ln NaLgS|l[ꨋ9m^ۈ'3 $wa@ƺqbTǚį'o j.3r`2HӜSهt߆TDZ%G:3`B`vd'`_ m:FXf:/~$4bSS~tܧ O8^Pz}ArZJ -Dr7yY|V';iJ04M#P3m:T$ nm8oAzY=x?Bϣ\SGM+~uʤgqk?  )hy-y_$-H[Y]u*Uge݇LN!2k~2`@jψ2 }qβ~zU#bD3%7:7nu"%դ:$Cʈџyx^ƼRluB9Dݴۻx!Fu-IV$هl*XdEDŽ2S^űY8ќgޘ% vg.{kK9ܮ9 @^'@r*1D]̦``E+2 2,"Qb bj Vp[?w(1Mm^؎T'Iµp^SER^c+ lu-M3_rBYN=2t87A_,aoFB˶!̏}1/  ML^lZ(vP%&a*U̙-_9"QS6I5ר9t 3pyS-g'g;yh=k/?{#qZurQK<=0z l\o>D.Aer6qDt|lZhwL3(6nph*:qd 'y 9MU=c$ߐS3ʞoa&+]=:GTsҋuV%~_ylw|I_Y9j{b .u6]`G=[nWCa+xZK$D5G^M/`e(a+`tsj[AZ,wS}C{4RU{ x!j0Ee*ߞjvE XRhiq"$B;aZSPHj5f3S/$Zuz,=:pij{~Y"Ts%B5+ZOh3I묜ˠp9 L8H,`.zu( m6*vtυSQ.h'AP]ɼ:GF$(<42f"w Tj/tyhZyy9iQ,XP681mCk,h% _։ *~3깨"C\Q.P`뷬},jQ,dyo 3I6^$eY#͠ƺ8!fW~]mK0kw⪘д셔wF!4nB䢔W(1WŔ+yN6Eox0"񭱚UB-¤!{-.T0dP=+GY *Kk1z:^Q5p'*IN&K:2RyjsN=5#S}и"ɐ;>q9X,7l7wb}(l4\@h3}t2Ug`I9#EFPD)PUb"r#C 0-uݾIصeJ*!5|D($loV^N([O7yH^%L̊]&D`GV]yg hrtv!16 ea۽06Z hc]掓3Cnjz$E`ki9VIɤ]lZHTMAŽOײ4!"4BU22-V( ]֎K0Tr4\p/ ֓Ɓ [N6Mv)̪ć62Z3 ?ߪaWssoƺ@tԃDfod#,fA~(=x`f[w%/D~3S.K|o:UFpu "7OD1Xu q>KM 7uwb .9ߝDH&gl+rC[襯ǾTfwRj 襗L)573@PܶyzAǡvE`0x:f3h乸[05+hbKq+Q4k֯XMu,Y'dU;UFםMDgTʮg|o/K7=zXzӐFEG@׽R{+a<*D"ci ѵoF))`:T5WW+mzwg`<5YC?=> IP#ӟbBs<g 'auƦZݤ eU %ut/!V;[] Q~Cv0 G*(?rlUB&)b\j c0eJU/V DfV=R~ΟwQ&z t;)fOŠ`er(5:tڽ$bV#eםP $]dSW TtWg&Ἦے)pdn= 8R.m֤lA1D>5a4ԝSm݀,Bu:㸉ϡ8wq Tym%W4a7.fd丮L,Q3U-iei,}-W2{zߥBBvB3#~^7e<ή4~u[TPur*JJp 4H9 v)T4KT9LY4c '%þُ\y;`d~@ʹReѱ ﶖkP#V/΅G`l >/+r/*mR3Q^RͅLۋ aìWWf% F#Iˉq P5d *Ğy8Uondj9k%ԄH|e:2Uv_8!1G]Lhዕ]g!ͮU );UV8$ȋ(ܶ,s@A22/" CdGe9)ӧiugHPIk 72vNB ?} r q%@ORE$fGPt >58C!jԤ]ggiP~li0`D2 20aV@qC!@i1 }Ѥ{6&lƖ4:3T@o#lBhmtu]  ܄^Sf`r<ߩTPϵ'ԟޏw&p\uqNl8=^c1^xҬ6R8xk3N+*lEC{Je*+/2׺#Omͅr.B5`0"c1%\R~<~*P0X i ׷@ib&C O<|`3-2UA 4gG.(l2FwPj-w! F9>ΫW dpZ**p,\7 76T\Ǯd_ shěQg=47ڭ~`?3{ ـ4ys]=< z\[+yhs|~CfQ(qWxzH<ꉬK6Fm Of \SZzR"7T77 D~̼Db҈0:u Q0NrbY½#% C,v} 7 #o@>,ԚX(`Z`d~+Gئtz@ uم64UVUl aH,J(Bc@GJGy+ v?fY*CXL*$Cg/eY[~N|v%G uqCTay$M1ڥ:$5N@n$\=QڵyLBiQ\d:Kfhx[7ls91&pHbxX)U,}-ʨ3__6#5$Xӟƅ5 -ÎT?){x&^NCw̅==æf-NH@h b m߄"io%p raƻ|-+l jeZ6܄#NZPTKddm韠b|y!IS2"!)˟p1+<+HxS۽(|Z;[14nVpKnB9QUCF\r L)Z[X6\l~h3Gp#v^+RrTalZɇƀ,x)a O=-ə8'{~B;U Tc L/M5p?& 40m"Z30){\yKgmE4V[-/swP~yXn\ny-(Fk!)\HT>H-%ϧ^j*Ub?UzmSA!wt.(Mazd~<{ް R H yFBR%V @Wo{IYͫf. .!mtI "ԻEvڈ /$`8zrr "< oF ղC4a]ʢ#^US)FkYSY)6w@+ 8qjJdi5n2su|m!@7łq,c/Zno;\,GBՉf@ܛZP7ZAf/d9B:w-tdˎ`ԊtLUlxJ;9ru &KSƀ,.q: ?2"M:=P‡M%Y|[a#OPoE]'iB-nzh D~ѓ9E?m:cA* NGHo)| wD@HVi4t >;Bm!cx*(TC\ƌpM_ܞyn蕬{Tƕ}zX׋.l7FVnRr 2^H/> C`վ|fZee o%CY0_` U{v`B|md=p%|}>fe78<\e+?.<%qAQp8iF(O{?3mB[/$mRB"+i92$Jb3z6`|tm,kuQqN u!Y^ JN^vxHϚ%u<5i9=YRN4,pes֚hum`i)j Ӈ"$O!2ٲ\~EO?-? g+NgC~ .*@k0_O\ ^ kՌ6>'·b+t,6ien|+ǰ4+TF\$]Q\/x H˱iم맸YOCʨ$}|e:V2h-=Xb*ݱ@>5@f} "I_=x S3~_w .ĵyǁEـ GCpXڂe0x_䒲MVXH&+n_&g_.CژEܕz5l:? Ej6RLnY0}I+P.11'h ۺ- v 7S1@k_nSvWaw9 rg ƴa.%$m}7J'DUG=%Fc@a/WGay{c݊uҶp˴;+gqDxE!;UeQf3\&ذ ?NqҐi;yʵHږ.^u> ⯟mr'Y@+]'.]L۱>vJF`._ZAJP9#Cq 7C$P_+{!!9U\/R;Ajk?U d܈Q]Ynsa~*2eN/݅mq忿EUKc=Q& MY)[cD c(ӂ\5(~,;mȈB"|7#@Vm|9fٳ^ ߩ*fwJy St" e@H!CQA:@{0RIM=8{6\C#t޷B}"OCڧ_B`>[npqSe K4tƆRS@>y zAug=Of6 mf8dU\? o:{&wU -cC+spⶓKqZ"#@iG/6!?U,Q;ΎZַ87z>(9kKHQI*ĥ8n% 25ݬ=Ǵ3\ȨCAV59G Y !θ9yRJ^")w|fłQ[JSLpܙcߐ(C?__5DFh,P4. >8,5*cvS߈dMDֈξlɊۼ95W'YztMLvfIm9MnlDf ҵL3nIäiUdzfcLI90TB0/vU1Ļ#QTa5i^qwy4tȔ\>|N3iR3%ZE[E9K@$%>dǎh8бTDy?k =GDns}W@M9.4ʩnUl>Œ({)dk kt4Mliupl?tZt \y/oʬcrWewZ+G >@re3P<b Eq\ @ @KPv w5E}{JJN#w01"ڽX.6ٽiȥuWD3[TŰv*C/~r@mPx.Wm8/Mx?=lsbA}_DP:M*hkwna/rHڣ.Gݹ`cYcZ\CmN3jFvCޠXi8FI;I-4‚"N)0mfЧ~;HP£w޷r @&ߑ@ 3wq%”>6+!ru@z"n^+`菥,bJ>suyfP221S? W]060;drrdM'5޻̂*]&̈́7@e]O_T{Ff" D#!2rM@h&]%(5rH!tҜWP\46AKXAehxmЮx;jOGRw("q2Jtd~bԪGZFʪ/;\LV&uf&*z\s8oLR;|FXN8]snn!-|{Fb%1yH#W\DRa[C]?%5DKۮ03G=A>H(ej#uVH5q-sʃCVi:s,HtH@KiCi.K._G1 Yt&_McG;pe-6v;;/?1iSگPۇTs: W_u?cD=RS~<qD"p%O.1+F?D%222D ?@ AE(8\8bN'w]\\ć+ *H.]?&wf~Fy==/] tulfROG[n) EڼX2Ip|-_!w}]̛ChBPȮm-`h> Z Nb_bq΅5d:g>e:wKpZ_Bs xЈ{ <}fn 0-O/d@g>d/k'| X(9)*jrR{lT5x(İ(Y rT~ߑuzUӞKuwg1ٯv lc ׂC{|(`͐A+;7UiQ:/ "~EcW#>CY. sk[?Z7'Mk euI n8З sڍ n.Hf2<9GzWl:+^4͹S`g{SDH{xGGaK٫Zha!H͈"pA iprn (Y@c*42_ZKq~b;|ȐLϐw >6Z+ŕ"]JchqbCY_ 2^)Z_-cjy/Tc 8o"{ H]&h&#m-6{)=ˮ|V %U8N"( 1 Tt(W "mP{I8fu"S%e57E `?x{p@Փ5yR '%`g5~[e o,l9̌}o?zruR m PivTc*4fQ6q rMg>ְ1Rˉ'泵<E}JR7ac2k'6WE/NpTڴwNW\&߿'JK?-*N(;IUF??ElG#v&J\Mf湉ziID)HL/zbsa>myḺ]q Y!+U:SBO223"? @bpfၠ;jyϘ ; Y\-zqgӡ<0a_^ 1diA5.@׆i]ʼnI`^!5XYőϝV4Wn ċ&o`,⎩~,C dgs cݙ҇{YL@5z+xL{L?&7 Ξ4䃭<}=ϾpJRf0x6Uxy0Ey2 24S?` B SGϋQdg3I"RfCkxGBnG lLR@).YҮ^N|Fݺҗ^VIHgew>)m I>w~-6q!l4"g)G^7=u? swsW|yÅ4[FQAYToxz29J}8`F&񁙦%Lwm7o`^ve~d!p sЂ:.nab2fqnfYJPb9Z,!wp?@ĩ,(޿^gWՂg^T5Ix0̪GJ\y@?pV]n(T>A КPЂ/Э*j }*L;--Mx5cOO tl SbEdXj݃\@D_FQ"x J>XdQ/lsq:ݸ%HHgǿTߋ7j|iY,*݂M*{Cp{ǻ7M +C,8HW*[ζ Y}<$5 w|Pa A}JR366y8 #hJk9tgg_t 9ߺO"n& U(tZquffo)V7.J)c"?vP#Al~ck9+VHTZ'6/z, ܀|g3hݽhܦ\Heua8m7vYPQiϏ_`y"^J'xƧ㬗u3NQyy$Lj>o7PP٪2P-hLw/ gLbɾ{^ E2F;Tx8WR lY'Fy?!M->*9I_V%j. ܌Cb͟/>ҳ~#IDג>SV(8(V05c\46ЎgLï&&}tut!2)H(V&߲mÀSwpc‘/B 0&^ 3}Eצ OszIoC)=8*#l?0͸0 I <`|&:kMk0>´zM\<8W@d!0J8تkVݯK7m*01B9QA$6]P?kjFdXणB}226"?@$%G.3ϙ;#SI2-4r'W2[) ERLK'3ME0 ד:G 'OOVA3K vN?;_ [ (X ]B{>N-%^+?G5}\i˄Rz;#0! *>Z*h|VyQ$E4עgVᶐpb cP\o@ؔQ63UcoOӠA j ?V[ퟀnA7fNPih(k[\ۦoigFg ם R&݇~y9~ixi*F r dXo1V pۀ8%,Cؕ0cP7(VI*\WqtĸjcӖ dfJ/E}dLn \<1 /lFich5!aӒKhXn ZoWpd2Y҈^:G7-+;ۂL4!W}qs3ԹtXY91I;DHf}V1.N†{?rdIWM{>$U^@227S?$Q&ɴ _0)C+G|i'&L>8^/Â]ja2eg82/FҍIB,c~]. P*W=e"D=uz& 1DC; 228h ?@$pD"@ٕRGM9B@Hƿ\?"\i|LNЇ8 ǵ`$ȊLcS+c*XhTk5 2c1z֓k0+ 䳕r3C6x j~_AH~\m)ćA7\h/#!8yz(!eA{ڰZMA ~)ڸ ;K?;Ni/7Hި>0M#NNPP!rg !fNihKp|M8kK.\r!ڝhñEc:_Gh`o+la6Gέy{ 0jЂPDjtA/ E⟅{t:G?ڴe^ c(>%qlԥja[ 4* MMVZA<)Z_^5w;N!Dr.)Q2xP~my.e3q૛w*̈́58~9E %! 4yyGO;w=¹Pb@.x`SG\K?\R[ NT}(il:*"FU,1>sR$gDݬz7!KO%4t%6"eK|GWB0Īa'!s;'Zvnz|HE8pxl*7nUkq^aC 8Poϡ\a,pk3 _Q]nxj2>˃6.x~I0`Z#582BqLB&Wo;L>``5Bx qZVRaz;Y>sII㻀MfofEx&r_n "h(Љ44Le%#Ia#' !Qq(sL@JWJ׃m87FИk-rszrVaOb(fh D>pٱO~gT? [b>AW+PS89atVh7BU2Fn eWЉZйJ= {.(OH1 _W & pZE꾚5]=+D+qZrĆ&e#n(XYҡhrHɧj~E}5[/.+scwy ŨU؎6lqWL>0@OtE‹BZc5 Conx"f4ͬYdϟGd:CR  >s0?zcBVO@k,5w aNn0Ol&hcgh!Jdr:ȝ؀-!0΄Ǡ޲=Oq?%={!/{("u lXI3mpɑ"T ɴNzm>|m?[yhMYë2c@&T{h hf%PhĸϱYuP$ N qiy8CȌ6|$m$:vtj +S+{&7{Ԭ"BUhN D/lNݜu'Bl%5Y <()(M 8󨷧 Š84R qKx$hTۯơp[L[A@1_5@uR]W!_blqϏ^hp}B:/d]r4uNv͛f_Hwub#;%1:PH+/R;J%s vԆ?3t7Otr0 S.NAj}v Ms" WI5#'6s,Ҡ'WeQb+Uqѩ&yPFWyr<kn9* 犯'qS El?y꿁{Fh/(^FFX,67Q.J$>G UKz*$ #xOd:LJx2BO˝UW5u|dJz2"EƗK-)VϬ3136:ux$=iZV:+T1Mi`ݵq ^6)p(~(mgLFwhg|.Xc6i~gslx+Y=\9hMSx^z4^uxnw1õҽwhXpGܛ+:0 M`ZkMђU֣IJ'V){Rq:/f,pZ,M0!8"ZVvo-lf,GB"B#{֧BW\K-pw]u:8́:OD\8E}<6^ϝA^JK"˓ 6023P"єFgWt{Ig^cSx}4v0|X8 #.Һ pmO,]f7܏JZr~d~c6LWf^EO(vڍ]LDo[B[e3avQU-TU}Rx+D4fW=5,zR'H:Xnc`%9N1k`hZ(RfoxݲOZY uTI!*7{oj4-۳eռP4Z唟7'%ԮfJZu7&L]1SW~ysHXa>d.#iddQ4LEPh%j% "F=qM˱MA{,R+Ϛ:5O^2_u~`X+{De<ax+xMB.7?J_Lֶfhc;~ǭڠ E< dMڀ|gPVP +RnvZ}-/LEx &[9<~2DJ[Qz5̀ҝfr8f}BC#l7v3 QI6M0wzqpotaV&=OtyA.Wg J '֋{Ni`#;㒋>8}J5u47 N:렿Tn_vyV_Z&;壍5Kd-0Ԝgӓ&4\ʯ+v y.۹#2c.ȯj@mÎEyxipӡ>Kz51n :k}ک'D r8EkVgV|ɖ' I6i`i1j+nw3DP%I XywAQ[L"5=41)2hzEB[6)8[໤W{;VR@;/vGK@3VW t_jPA)H;yw8TXLv (R96Hr,t(229DDFA:d҅^\e ;Jlrz> F t\?OX@zMK^Vgd0ͮ D,3_i47 DއEњResO@LH⃫ xt ˤqW?vJlV%.|5,1w{Into:CoS3|PǍS-o,{rbn/3׺6(2V_/>n[deh ]ᳵLS$[ -^#Y[Pǣ05A,YJ@Ra5Y/A騸za#B]sm6 !oe{@YR嚳Taf4!NZNty9$^]z@Zu^ cjSEE DG s22:"@$D`@ @F"e9ֽ9[} RfM<,Іa5{#5[*oE2ANwV޵tL gj&mUM&i@!Fbf#n֝mWj@kK|ѥ<7:^3"1~4 ؝9RHE.ǸX[nLI-9M%ʹh^'<b=%t[q0kV J`VnT.^'T#NNu^2FD\;|* c+IRGMkMr f[o V^͜Z O|/xEd/4n]}LHt \+YaP>1dS=uQq!%@ Y n68;&|_@Y%c]vwҏ#+Z&֔Šu? р0_B`pƾ@z4b5y, & $+*/e>g7S;~mTcJN;znyI܈Fz`>D*ލN;.3mվ"#̀i>Xzfm!Ȝ {ܱ{[VRBmpG\ }ꐊy᱅ӂ ,^y" OTaa&Hs3%bPId 4\_|LA V%qR4 -xt_*4gWB]d3 h|ra܄c 3N&5Is y#ؒy&-5KO񣕟E|/B 22;V  ffNl}!'EҢ^T|y4VlЊN놉4p(k^^K5`πjV]#oov 5R)@k,NZUڡI ʹg 㥤S͉0 ~.0bixO1X$ W>=x&J$ _h5vDa ;Z:<U3#س:Ƈʯ[œe1 Sߋ%m`v955e0=\:s1r= Y^nO$|"~%j_n˵A;L GjUxgiHZ FJ؜%ҳ({EŸ1sޕ&VM}Cf@ @ӌP8X|DH7ě*YeN @t[31.+{1/kiR)-UC7x;2D 2 2<DaD0 1kf眍]tZhUddQ ;EHF 7p4 =PM3LP*f;?3P7$F9l6cpa(ݱ ` Tvv=<(:Sy k7)&JQ=j~[:75 &?e E~I>2Lgx&F=]'>`PpBܮ1_\{OS4봋{FXZi 6!\cIF^ű򁪮nzWm>͙5X£%VITn5sCrN` tz׏:>aíi䵍HUO:5v-\6<|G9`R$qKHS`huB%.K|2YSu:&/XD =YϊB:0yM+#RiԯD3nH/򸵕tW-~xoSoQC~i;59O:a+)BgA2`2 ߶ז}!J$oVVG93 S.AW?B 22="A3Jh";eKlJ-Gԡ`ɣ&+'Ԉi, ƚ\4V&z09ʾEHWWZ\ڔ71xvzE{($o$p&.֢V7={9 ĩL[D>E UAo8~Shv%*U!Zc ;6qN~ vBE:2 &wMe $W[WStRˠa(t3xw@ulS]]zgmVc Q$i~Zgzq8aBFOXAd].IIw 8M{Í*lu +#2.Yœ|#Uh\b*jl,! SEQ~oP+JF`ڵzR1 17U`RI`;8 FHgaI.^Ν<6t1xSBR@@DF 22>V@$q hF(!d`8wQ}*w-T3"1A8u 3n15ke3` ~+KO-}dlQc']hC7NbRox,;GCUпF|"~PɅgYzq?@~-wgg}4kTơao N_ ɝEllDRsl\^b S6BoAAǚ?dT-^ق ٖmh!ތDGW'N ɍ~5q-U C&)- 3 [HeAXɦMKYTҿ3sxe)&q&!P)8}yQHsmDMFCb܄s"i#$XnӉFTYڳh@z>~|o͂b# GL5a~O*Kl׹͞? QAu@TT8FT}2h5 w+Up$s>BEo)D;?d9G)*E{q h51 _SUƦl9$"d, +-S`z^Tmv!ݼ ~ ^/:K#V)VcO|[UNby@Ϥ0}-xb Z%:Bh6З`yEuVBqmkc m'۟ lBM'.D'ičzT'ikz`O-&է0b%>!QSM)QsZ'E[Z(kSi2I H]+X(ZO~]gfi)_D袗)v:6zMف gs?tG(^ީ=~δrsE̥si7?y/$UA$I!g?ۓ}W;'z7Pҝs1XxF`hTiSpkZ>?24 %-F\ _:4rɜYր2^8OPlt8Bx4^>tC%I~Rq:Q<3Vv0>AVknk (mZ8&2W̄o {S2QˉjhY7P SW8 @6 #X*)l^pZ5%'2Bq)v㸔UI:xR\PceE%uoյ{/18 mLբ>p{N=ʿN#qdoHoA)m33u2qz}Dd]2cB ɐ*?fPk;a`@YmàbBdɭU߇BQlƾ1$:k,-֭W+1ua_i_GVI$,V}y X\qLflr9 4 Wmc< XPq! ^q"Ti~(ܼ}af6]_$Ѣa{ k6Ϛ_OgO^Dp*3AQ(e,+4hӰdWKj2uMo?g m׌!ܖ4\#ackRiF*Tۜ\ ;θC=c +rOV94`5k/vNxL6 2ow@^V":1Uu)ZElmvIX|xX+Ӡ&[R.V3|Mzzʛ {r*{@y=)p9 |Oa]Y. 'B0E9A]qW̅u_|Z }[6& ` 1^ ͯZVfw yS 5UPfI~Gż#CV) ^3?RSj3z;k?qnUlRK)`2B1C{$lEsʐoK޲~Ij3IK7;?6u \Z+ǤMhoRpǂ dNwoaX>~"Nc=LZw~dVz}EɽؽP*͈n^kOp tX WǎIzZ:JTWӱ]fNFWnck[[)vk6.˞n|l8T)_ȕxfG_֍Qp|hDfw>SHVdFf!/?.*]g{K;m^V .>_O-ǠBR])e%k pp+ò ^ n` `#B1 . Ĝ{Gq?H@05[_.Gi{Egē?6`[~<ΦZ#}v=ZOClq˰tuZ~#/Y%~BH^'\ 7KPC~*kMXlrj#3OԆƜU:{ͣ&غ2t ^Qd6cgXUqOPXQz/FI$wL7 mrmkT̓>] nIi'.IX}ٲv7gnT>0 ^:կDCƘ70tCk匬1%1s5 aTfE =F7WFXï$h^8DNUPR0*MyF-^9wVom D[]6:m3$Zͻa!EM"a6$)( 9| =\GzaEaCւ0p_*z~hD] tXiSD*!i$aFN=o@Ye"tl3H1~d7*GWXաq)&,%IK`hKljCSX|X{TM^lIy3" Sџ!žy팩e).6x禚v3;14@2DaJS@$or庘U$*3Qս4FLņvkÂ6gjBՒq>!Nq6o f]@G$ 1TLS"g*6 )pIp-OeQq+/ Ү 2:&aJC:~ǽ2Y}; 5_:'xؘk> 6~Uχ) b=$wk<^[\Diu}u-zH)ǞOHPq&ŕH*J}tkxp>ںs\ toN9,c޻Q=mU%Q E϶yf_9Wn2\B9~L>Q∑FQpao!^vĜb:.};>j݅hȇMȢ^S#Gѣow {r ׬D=C4Y-+0;;nefRZB?nʠ:T2 CG]瑂rc۪|'k6z374Ȓ ҃tfsd11^c +-O)Ev;>cУּ8y,&C\P1,n740|d>q onPp[]PaQ+d̦E!U!y#~λ߳,nYHgVKTkᯮZA "O+5ĝ/,RIDssѵ/ru3}'GL:@Ci,D{r&7M׽tPnٮK)5߲>K~ %bU#PG-ӽ^>/ "1mq ^!>GޡE8aLD/mf-]6O8 <@æE94)XєW"J*gJf.αZGNy Ƶ04#~4L}py bmI^hF\fN%j*orH>W3R ?&iƛP`)y orIƞ7 7R%Ʊb\bOu n?C ()b8E"{)-e22A"?$"Z҈8f\0-&_lUH\0^W'i"ʌR\N2 CN2\G ^? o5-_ꡬ]9YjJ*]Z)Ʊf:G&mp)[ʩbdi2`H>*SiG}Gzwɵ!CEi4WU@[9[Y@̿)x>5s]@ۉ  "F5r0,'풰؉sO(6jrq07I=^ZY FMBXq{)nQ ŋ sN&88\dnK29w {۽jБ 1Yި OZׯ<<e 61?COv?{l=</g @ôSG7p>}hǤ؟}-/ђ}H9u~}+@ToOՊ[5)7bne}:drz%6B87sg1鵚fVU =]e$+2x/+`EƁ 2 2BS?@ %x E@TEZ6R0mCڊ>'_@A9epsvނSB36M&<.8/y "40Pq?@".0syC4qf`2`+^lz-2Y:2w`MDU~m6D 6WJ$,ӳR FGL d&Gχb-EC!xCj@2~NjEd+bێꗤƅjUcA7.a &9b' uKQN\" l&WL9k,߆NFq%,F$!ߺ+ w3_4:>(׈ё2&3|]%#3_IvzC?e誰|8(g7Drj|QdN쯔(N0k*$:F+Zݕf֗&K~IoFV];p4intHzd^5f-͐.UCr \=h[Odv@rkrFFs~׻ ̎CBpuo>V(k\>o60G˥9_!(O005q]tuxB=T:U? 'J ejjGO<䝵g/-(> wpg4 V0|Fo83K$ơNNltZeb%+J9 ɦ`Iڊ*q^^NLL#ۮɵH7 |s>|@C 22CD ?DA%@ %!@9!C8Kw‹o < <#m2r> $YVj|cS y8,{=tP#J[>DqVЏ>r_m0eg%_C`F T~JwLHjV};;'Ow'M4?GSR~ccõt` 0 ]2$Qh裔bҔz0=]vkiSŵ}?cq!1cB0s N,T5 n@aI6*cQ^atcׄb^bv}Vet%L9AZs&FADo`ҽ2? _+qgJ.r8!DKyMVl0G ˬ:~^Tqd "P6 L0}WtTMŎʽIL98ub%.8D:4L$@"3*# H~D|\?߳qGj+Zf mږ>>"jpQ*m:RH@Zs-(NiUҚ42%{\B9jL"_2u h|FsxsɆ{(~ Sʃ`yyU'iJ 6:u/n);mYkS nd 8ކI ?;GkKd /yL7ueSEay~qRvjWM/\+L"2sHB1]"(>jԣ)>m'eOCl30oyZe)GS/z}OT¨[<u3ʶQS=ZRG@ |RP9yz0JZn),0(l(SA5$Ţ>NEW+K(҇]s7D\s`A3aqRu'L&h[p,+voȼñ)(۰=z)P.Ob2 r >®MtbGXw ݘ8hehEZTqRF~:'eT/88i7a1n3iꁯ&BssL.gW}EZd [Us)y!^|Ic~,)kJƑcYW))Kn2Č$ ڻ#Y^kș{tly(-wqr /(f]YPǻ[(}m7>@o3Mz@v&@'APFզ$]XXunjUڦ%}x:U7˺X\,f]ts4Gc-^ ~r)4Y+?c8ǭ oQ-4`j, eB;3@ܦ$W]/lA?pD_ìj~fx &tU0NJ0hs˴ԄnVy4m/SXTأB >22ES?Cedt\pqcR ,ZljɵAAG r mc)9@o rMRn y;R Y*,Fy]츸.%#@eQy+E 3ze T IY (L0N7ū H^ R/sS⢵^Q(zr L\U̐Sc̃`C`0JFǧ}ޗ-L>Bb&-8BDwŵÎqd{HpAí&I̎.)Vz d%84шP'v^(˽ Lk48pUq(B@UuK$\r@z{Zڂ3.' aaD&!(̚%N Ĥ!cGs}' B*)> Abub'sM&֬sǁn$2ϴD mE#;ht}I_[TfBF|' =77_o~ dˢv3`Q;-~pŏFkl #ɡ\|ԧHPEGn% 4́DZw6AZxcM%Gr]}UKGS c#|Y0x~ڑ?-l895o1!rHrinpkyg-%[\%.xWڅ% XKywK70PӬj4C,KrԦD h2 2FD ?@,0 `: a@#r";ri4O )tBF(Qױb 1I.ܫ7Ab_ugߊ : \+8ya{:/Z kM4IKKӘYy#3Syvn! )O=ڟnXEȱ0@{%.FΩoQR .X{xdyWbJ\ f=o`/*b̤AHr#&5%3{K> ^T1J?zl4HPA{j9gb]M+H/3LqHqcCIK^ ZK͍J1yHJ#eE#nk'fZ{m34G :T&۩I"a^w?V}աRw^wSẁpD`jTVGU-lR `2Al&G8-HqдE-kE%M?/8dIu!êѠ 3h j!8N#ÑtJc bd$J-8ˋ>ˍ S@.Vk\dRHnG `Cօsfo 4'>q|"g }u|ͤ\u0-#^w5(i7;r *5h-h $Nui 'W"1kHq,Ի8fv{Pp+_*>E{b8#9,LPQ?f :@e:R×ؓ"<0s¸#<6}xS&.Ӥʖ#ۥQTk|؃Z"Bb}Q}CE-t䬟eqԈd Yް"׉70 )}\舥'讇U tBͶ1Yk(B;ڸ&8L/,wk I ݾTW`h*w,)RgP:~ =[ >V%ybM' Hky߸ (|qWVK^7b#؁one%`RAFZ2-M# Ltjy7VZRTG Ay+ rE:D:VўoRB 22G"? &"Y]'hάaZ(?Is3Ρ_p !`;9Nyo[JӐ'ǤUpqBX:0}d翧ʵ5drRg}zzHCӍUwc>7aĢ{n(+y?C8': SU>8T"2@ē^WB䶜.|*l #HGV>uWqawUeQq}d^~{.p^fx$P FBA4MۊH:[ dk3)+y&B^sdTcv_ewUFs3i =e&Bk6M4=^Mhkc`/QЦNRWvu3MNV,{qZLFTwGռ"~nZ{[tDF.Fv6%4] 82 /s=+K uBOl\1㒽Wb~3B$r̍ #3oף/,XɬmxWD@{6|V5e?YÇ=:n U%aD)KC0N4OtPҊzv/foljeY̡SwjF4 2 2HhS?@`((`d@[twղ([68XhBz^%!Yp8{QDk(xbpb;i?i( lr@j1Vo*x `Q{s?DDƝRb|>< ª~;wllPMN@UL֣fXۘEjwR hQFzVGNV+#F!>3 w9Z?tgۙ<ՈinzMnK/FNzU{WJbd2I++縭Q IW.x ^d!^  6t$^Z[N5ODr}2Ďl:Y`jLk~b7ϔ~/\ܪ+r5C^2|l{.l Bҋ fFV"yFT;@ɀ#Gϩ7<]u6_|/{Z9j8G`BcMDHxކsЗo65Hy,۩l#6^6ڽ~^Zpw&IgΘ(6^EK7İWc0\G4J[KoV°T bdpv;+>oOYuRq.,݃E'O1^E +R #%8DD$8&|Me|Df6e+9{# T].k`Bb{v3ڴkWK"->g&^>C3pO[BH}v\͸Ït.zszy//5YYU3.'Y2/La_4FW @XOjz^ R9`+SJ b_<,.TF(-7տX;jѿfڍeʞ1X1[zf8}&lQ}yBQM1t T?%Xy:' /hcF7ͧ5˙<gxrVbcmvH^^0Tq\ 7UURv<ҋM>`~H[+K߭(Jմ< ߥ-q/]P W&T[]cc ~vY^} 7B%ч T7@* FSnS[P91C &d#33ϾNn]{<䇃G-DH7<Xm) *;KpS[expuq6h}5BԘ$''L^3<P;?9z\#g f kz*rД|.dz+bi=G"G)=dyNT<7.yr3_,iJ!VlӔ@ u9/.%K/.\n-K t&tŔa\+<\hFW.(,:Lu^ Hc1Mq]-i {b 5if@j;Ǣ/P(w `84$쓄u_ v>:?+IbAw.kѶ;.z3)4F-C S6II"ޞs2ECP0x(USlJ-8ɤ $9JA󒼉zAO%n0yѽs:uخ+.&9tP [f*1~<.4{@mG1G렁c"i֚)&SVp溺#Σ3 '=k%q:VieNm%ɍNtl4'hHT0jD&yd !$C~;l5HDQh|CύJ1maګIݯ7VKkGO0>2='ΚHwF}7:~zyZ6 avlQu7a9*M[|¦YoQ(̣*U;nY*@Qu YHۉmѥMD-@Rz4S{aﱗl<Eh:h󎆥 hЯP4/U)9v୲͉+\* Aݴ1IɄΒf2תn{G'5%(mQu6b ,*snY))o}O^n&XHwS/In3ۺa&eU߂7:;؛?6^k=dyR;fK녛2rm$GҬv4|0ˑ+xF;)ܸ7f2:*b13f ءP*=c$=sfa$ KT֧ƾnZ.ЇqMQ`ܼdZ"+ny5YXr) 5B%wEѝdy ,u̕P>ʊh?;=K>e1tNbfW'~\('C;lU^̗^Ϩ&$$16R~p}m|ssErD>RLy {l&4'se'oTx䜃h#cɻ֍a3r>iKU؁I$OieJɡĨ?(w"fÉ lw8 I-' c2ehG$ 4W1`޻$`J \SGG|!aH@@(Q8QICZY^8v1Ŭ LxF+01~R- c~7¤V%eѱ-<od}U}"sR"o´<=:~"qj@Jv5Hϣ(6 |fXwuv{ BT!q9ʋ=J~@L uPmzѭ0]=x-ʖO$e9#2fÁTZu<+e '̑ 5p+!KH{Ox۶=n?<๬< hlO[k^(^J?'7!6Q(kW]0kْ=6di<9U.r0^۱'/=f] %!7AȏGL?GU;?v[8qӟG%\lݼVPӂAe^ H;RFN)B3:r2cS00,ʉz=җͦ2IIm52k#Rm[57ĄKfN-}bCuxuSzy)Ñp}PTkbi#Sq'hί ("6ށPt%3fJtT[X1PzRQ/eE6|?ܻCWA5a͞'Ze80WݫB-F lͷ4|`0 ocyA{b̢ݯ+mPP7y:Uw߽/$[2W$WPg!śRA##b>1J82ʽj}o^f~|dLt@/w UrP/^\Cx mΐ.~)AVBBl0v@9s*l#-,U k&R:i@;z=[N7Uw$vP<$^DEiig`+J*b9/UVbh"m&}q6S||.?<./ps5ޠ=,>hU{輍KQąh"سٞ*yG Z4sۜKtV;<2\$F[1fwD*yrt28[ Vԡ&L7;W/GJ`.{7TjWUpW{+ClnNET\z/s3l>@d?3dVY~P7' v\c[H) *gJ9f%;ʝL[cs{}b [NaysuW4e7+Pp&ﮆ_lG4j5J  $v>gIy?v'A5\`22IV jB*P @DJ{ejQuOf @eݞ.nRҙ N:g9Dܸ$`GAE;d/lɔ$]eV5o&ªNNi^s%60v잜{DKTgR?Io%RU OuD~D̘D+{7t;U2}͋7$'zv-m NjAք;gHV߅ WKU/q-opœ;zz6h&CR M[B'}L)rVN:$.,%MɸoWmv0 y5˗0M VMq8yMVdl쎷JZqm$ x zo3?؄!,5q9KGo Z1(X$ Ky 0nwWp$J ؓnO$4G ѕYԈ-SqPZfkDɧuo HHu[hʚ2_+NKXi謇vf*!jwS1iȗ)a 4pr3QłV E} 2 2JD@$pa<(3"|)zKAf٘/U#{Th8w11+PxۃPr0d9ן/~y_mie Cu,iʥ7R|dC> qqǫe!T+o}`:A4LXYE?wr3𶔠~(j7XDe+>I1-jH6*o,HhB'kO}T:"-ΐ~+|+M}ǣr p ne=ANSZs-u%{sY:IL[n:i/pwJ[wff + z3QuǼQ#mLIv]ܫ2ZE=vn #ܴJ̇DAICLi[YRhH .OۇEg6L[0/Pmzܨɚ !kܗ4%Y3=T.p$pە_:AGG[Ԑs+꽑g|`ϸcR4/Z&V78 +]a.5c) ZTr)kiN tRp?SR}~_V'ONGߔ) 1sЋ{x䰏,IwǍٳ":Uh>pX-HJ@JbmIY A,WZIO+hq*`m/햔mԩ-v 8jSыd}IjP|.ztO}L {Y`Bw5ib+L]8e=}6!&ޢԔ6spP: xEbyɾ3}N[# L9\j 3DQFRv=Y/Px-s.{cujnb*O!OrKE3^II T߻ 3>QTaq0w "cxPsvUY<ԐT,lᝪG}I2K7eiAKӵ({$op_q SQ:3D6Ff F}iD! ]6<1D4<_4?BSҡX|έ۽ȵsx%;FWbCssuYqfȞG. &zmwS(- gweXFp"57וrSx IhdnrXo :paCC旤j*p%hcV2khv+7\;?g,\c]팴I2'NDi41cu,?*'$ M$+A݊cSPetmLм}@3 Aj-SȢXɏyEF b2 2LV$A@ 0a*Hf_UN"_ ,<< ;|`Mܰ{<;[zQ3J6X[P]겂mv%h,_(*WxǬd~G_nk+D>Kg'Mn#^B֮y1&qMm.DJH1%fW3߇4j[f4V>?"$b(ӴEL0Lu"^TL;<& pZggaɈf.t'cH_UuTJWW~UD~M 82ij1Yn/5!웇6^)I~X򓢩{hҹ v|*.g'ad6pj:sU0qл@H-0I$2Y =vPtW vsd4=|k.}Xr2VqP8Dkzio@` -[/J,~plw՚`:C9vv]6]pBk 22MD ` j'`8灠jwpt!}{ДD5F,S#|[^YCÌ]b˖P4؏&=򀎖 "p>'(ɷSuLW@oq.`G*./FDU< $#!#2E+?l}x?)qTLc[%D j-Ϭ$ԳJq0YQFx!*yWllPݷxyC)cC(<ÜsN{/YR~XЄ/3YheEugZ+f d(7u?%GoNR #A p vI֫1N2oxYQ8E-g#&<0-{4~.Qȕ#v֎E?]63Moܿ'ƴg"?Zv?c, 1ɆE2}.Lx@TeoջT{Q[Ѡ'p5 V 1ο^-bf"MNqI؜hXO0W3w97]'$¨E70|x-1kHNC@\.Lye+:  ||PnJ懂z %B9)Az𺂊Ն~uֱ"-GYܞ,Ihz)9IT=lAQ73a*{8zoJ{D*O0` bRK2@>_ptqE%`/z,{Ɗ%EN~EeB1"}-q/ ~3vz~Q4rXg>U@v8 %!Z0`,]cdQ+N }|r=RLrզmǸ>]C{VU,Q|'r[|uƔķ,4;ُ1vu8 ^|(<ƀKl@wX[NWp5q)s0ʞ#͒p d}})HJhS: $]gEO *V7 mJm*s2ejU9U&xEiDZ B 22OV0`0 @3@d8Bw~Il-ɛqGgESԩ6YS׬ E%(k NP)6,w5p+^틋9Ɖ%rDNo/ ?32*3b~b aq粙NLݒ${I;M}1bQl7Nv˻5k萍fYT3PB1x[sj_$WؘϣDw5/Y1w#h)Ŗ\-$)=I)L\"bo#&%()>ȋG8WT%;U7P崧E^ l~ X1䌲ƑwMJ1_nd2tr W^/<>ZU#qc@Zwߩً~w6UCD 2 2Pa@`@ p(`&h@Պ#l.ߖjUv &.h:bm.Ȱ[ӈOT1+OŰ`pk@cNyQ8Szpz@q1メ׊zz?JG2L&sR:䑆l\9bIP5լ=#AzG)ӼVgc,ׂ0nif|/ٝCHzyfYܕO' `fXqKp gepR~q&!^`Ё\ڱ[!EQ#FG+AS𞇓X.~Jf4|i<)7ck|ˏ`Ir>! ^AohAm\"2,%1P :@be#f8 #a,ml@Ӣx~]ѯ_j審uQN8e. ?+[+3/d^pf+t<|̻i2$+[ Eug?pV2劳#{:%W :lHiv$ַoƚˬnN;'!.SV.Ĭ•:%*҅cqn{ը+Md;4|!i6])H>` 7t_y#yZăXd/c3Az_.ZpGCAʦ9o ڞw')El㊴$EMV•LBlO Iw>2᪱wTiJ6[> m^A=<O7C2 IdM8ccL+QprR0`Za|f|tWO K 2=IZ^V37wyŦE~{_`ь^C}!!%WHo4 DQ'~mI pƟoa$SS s%`ߠ9h qsO ["5o8g\QȇEya)k^K"n*KYՇDU2fVMԐڰa"֒i:jrHs#{6Ҡ`-|ݾMa=##󋔩S 22!!(˟ 30 C?A DGA _yJm P?Xq`5~"Ȩ Oq: OASgOSƠ8F|bKՀݹi~ɩuV}[#IT_>\:Ԏ݌6VgK~XU,UIKcM aWo=g_JFK14[W dئ>| ~}_\r_901?slNkd~@zwx$^%EΌ05{>8nbmX\ggIj緐\{r#GcD/rH]htW\s>Mf}8tXMOm>\/u][m>2g5/eȞMXKn%K5@9plbiT0|\!PIEIq.T!=N*v(ʯDr6 }L4Tk *ƲݔؿNԁ]$9--}bLz`kSL +Tqmk]MZ+(MMLhTLT{QTxm1To5˵,Ѭ"~HgL*H_je<; i"_#lDKJGw89kpE\eC1tw-Yu@T uQG{*һ5$R)xMNJqͰ,㛃jb݄>'"LDpw7(s3v1 7M S@ z1_%N@Elj0z!g6Zpg6F#w&66i(BtOzPpitkqN$Xesa9`4c*&y\3tېo}Ȣ~.Ej);˙HG?f@p8Rj \d~Bz^?/vSʖrmj#)C<0e AwFಓzTtIr-#a۔͎:;v,[\(-ћV1?OFSca[pބ5ւ%F N)2gKOrWz|"ymx X#aR4e!`\\pvf/@74l}_?P8Dz-Э7T$xL"7mn]P+CBʟ)r(/.),Zڰ˾iBvs0HVZ2Qh$D&t1E_C'tIՔIKBEI. JILX"*B ]m[rV?%Ѭ:ϖ4(x3Rd*W\5 B'F)z,>C&R[#9f5=+\uyjEm, /Y<‹ė聮ҨR%bBe/xJzhXg \@Qv0ט6/˓=M,ʀe̟-VI ,L)YmO3Sd /l 4$׸rrR\'3ȬY;*jd!V9QQ-RA/1~[mYD@kKi_;@x!-"o:nx; d<Z a1{$ǷESmܥΈjv<ޟ{QY ׼Q1X,bhgEq>2|>M|ҸbLv)3wOtN$|VijKESKa_Z'1>$wkaf 빜0^Ƿ;8$ ?A  !ղ6piLkQgTȡF뛦X246D̶?N# .VUpC_DSW*Cp"F='` `^1y֪i3O`g7<`$];ѽo@=ZZY-rqU=mhCGilqZti >%w=60~qpݸ;?, fʋN8Nl6w"J1Fj!Ax<`sx42O٬ lw: lN>YIP U>=Dq:IiU"^,K}SԡpC \9gHp?ڳ\KMЭm8CFŔO7Z]qF#MP+Rħ EYRl$.ׄy ]hK9. kiun;(vt|}A9^&uAjj&dLlA.1a38 -N h\Bd> ,\&kR:Ѳ - CQ"kmN>yJ'BD3"_'U e绺]O܂)v2)yur"t:ㆥnl5%Py{c>ԡ[ lHy@3v pe'SzzO4Ls֦"o/{'i4%?sGۢm/$ӎzZlfZ{*";V (Ԍھ5O׶d{کxfy~"y̻'׼ ]]"*Qs=7U:Y g %\Gp{oyؒ#xE H"Wm`%)wbs)? E1OX2Յ`֐j+$ _@®K: n$ޚUi˻ 4œwE;D R=N^̕yƏ)18͊fmOAi@+! Tz h":r0&&xGvZ\CuMgcWX)Cֆ1m9/thAَ{3cKD$o_pe7NsLr<#Q ,cDl; `7GzU^VUlE7HT2DbFR]7OU >AoS+} u 牬hY<WU:`sRM/u:$aH"GŐHaD/[V`1svKEGb.[ ($R&/3یpJۥ_Q=ݫB^EM_L'@RiK/::=P@z]"z7͌ʆI}`3EXڹWޖ0݆ۆ6렀~ <x Lx# QЇiothN=IA/ig]C~΄ݾ7{wYo@*Gν/ڗda"b#>v1lĦKGT {66tlsݜ!|L t*e4vg L$3p!,ZnAAt-@tRb?^gdhZEP~0aitB,qR`"BÁ 22SS?4qf@ @!"7nL],Y"t9V0^O:V֪<뜦ޫLmׯ8I`ںpL̷¶c+bIiKF^ BP eKE͆@ rhzxbM:ֱɨT$~EWQIIXv]b} cRMkLx7^_g;ݦmZgN `<_ZuO7xLh`e`FɍM ~XHTyGI>Iia_OMZe#|mͨ/xE5r-]qTmՑ!ƊH CޖE6'ӪOСAԖ(R*qy=~c ]Н^~3E 2 2TD ?PhA00 R0W;_a<SĹ)쀍$~Qۙ^`YJB{+* Lx!cHRԹo4 S6$6k/S@& ?Lⴭ\򇲀k{g""ٞ%#f'mѽ0Ń lv{KM%g۸.]u)o i˪m4բԢ0!G*F.F3q??>>;xIytd@w&AX-ջ?KfjE̚^YA ίg J:Q\yRc|bx/Swz0N?s/rY2SHOFDZ#R ViBĢBr,$ܪ/'*-ZPNH6|H' gfn/##lBEJSor<"1 윴4`!1X+X>j9l)ur(ɍ H=M#3(;#J4<Ǫ 2scjrGzX흦Ҹ悐!GnrAƊ]c) {TJiJpYˡ(dCԽǎ)oa dgsih\Co%vc,"1N~QX2 `x^Wh-اۋ`)sT\ٞ4K1^MNSyk$c1l%PNK9,CB^$naeGc$.fZ0C/i"4t߂A|ew>wp.f5Ǩ;xʯa#4Qɣ^ȟg"FA U/qܦ`ؤV:WzכemR@˅/Hxi4̥%Q,am | b+WN=@l*LAy6fZ}Ñf+@Ǭ)R xJi GcКcL"шim(zO@&ڐ&h(Y }L1 ֜r 6b(7cByt\_ KDy]rh A9Rh腫GBڢx-e UcT @C 22U"? B(hc(\ʶ/uI`(_p SL؜Hr.o^$/gIÒhiXd9!zHZHy“2I2E 4D7.wn>?9jчKy7^ȓ>k [Ԫ>\>-gkigUu<#?M~|\ޤ1TAZ7+_rڿPh#I҄*I0|5v(189I}Y,J$ f'sz* 퇧/<2劋 d sgL?(66Y܎1W]l>0ˣ[9aGl|ZDZ;7:=d`06.Rgg H}6i {?N԰˵Ԡ?7wPpx>^ dTD}{'ppVO0=?*P!i)@{U؞N; ZeFT\%p)<~r,;$v,lW[0tO7k=c~^l4HUgkvROt'LU,ߡ$) Y_g/.4@L'>e)R@$)d]ޔW`0?,VFe_@,.V rMo]!V>`r-+t4M|c*AH!LÉ.:Z{Vo-]VF-7J4^Lp˖7z^gaxwTX ;6YɎ^GAJ"HtnҞq+X1ZA8;%=s'7j´}vx|TP>a/m'hnKׯ:SP8s,Jh\؊.^{XwS~ Xl~f/ΏYTcʟMB-22WD ?Dq'd(=&@^|O#vD9T0Mc8^Xr"jm7z*b%#P^R ÇΚGcfҐ$wjcJ?dr,0΋W1bkQ̒/R?qP ;2 I*nZOo0*ߓ)MNag͆ťn^Ei xفWոYc B_-\mꨘ)q"#/Eݧ]?2^%KBrTB qF 4sw]jnװtֵ]x0_37l#pV/y/5rg2J#-uѪ *!)_ZnX7o.CP?I\i`v'lUVrۧCĩ_%X WTΎH/A< 728 LU^ W CE?6x`1:$-Yƴ*:>>Q9D`-&0(j_Eio.~xH!ґo5i*Y*N:> "p&;ǘŒb=8P?1uyYyl=!M*$ucT,gCFYSEV2 2Xh?@ c !@ F2@ p 2+iW#,CNx/&[O,B܅0acKMy)XJPƦ3mmSm_g11BBy>բ엤:hY) ~8](q=L)?)~u7~_*4 KI6'6h/˯9ߔEXt)rA[DScYT5ѝ,6̝)^v?!FN`d*<05 ,cQ-C+^*=&q"qe^b)YN]u,tq4Iƹ3ݯ_O75ʈZc@d̎0@mr}K i/ϾQ2flۻ.Nrao <6Rghxt6ApT+zS%?twlQTIh ܾo5*6Q1O/D1\>Ƅ?XЇ!Ap-j0-ރFII&`x)t: e&'|d&ha$#~ f׌3mKGˆGEXřq߸MCV+o( {Oo4euP1 7 @9 R #0#n VfQ13ൾ? \;X?f9vF4!ܙדUZaV(fun-(^h _wh(Ko@8yŻ9227lڛep-݅*ɋFg3bI[;(,㆚11 2pYnЪ(iT =MTnukfzػޓ 0q2^ɯ-i룢jJ͙nbt|VXw֯rg4V$B9\g [<*邕E.<^e&s&-دIVu2 =\d=ȁ۠7"Q4azvև2*|+uܝU*g;L+I= 0qW`FF$}ЌfO:HDEty>TfyWVBX.@^Q_ԭupA3 4־Bc\WQNEN fN? eo՜$~[C@?/8ԳԦLed>Sz aVުSi,mF x]TT.)MfQ9 {R.xg.p+:3WM^sym@b j 0H᧮mz-[]QeJ A XO!u$& s v0j7;柛ntv +@Gfdc,,P{ķnƁX2-w)23cMWoܶ'c.ezfRH@`RbZǥF\34:yj4Pׄ}TYW |r1j`M'kSڔ;{ߢZF+2X P`3"m:&dn/;ɯj:I_ﮏ&6t^#A-OV%yn'Z4IX<:1k(tx,{D|lBR\Ie/@ļE!Sk@QJ#~sŁi1|3*\e9ŝRiHJ<6qg`"5.D.kbyDKɇt3Y@Hw.ܶKٻFaUóXBď\ _~UJ7y#n 8|Pt$Ƒ6kl >L\I吺@RMYj9õ/;p8F*>CݠvRQU}&}EHd%ů vl cfMD#ϠҚ\;#ɯߙg)~Yh׺@ntt+̡J{8kWM>`*Bfz^\Rղ V_EbuEf lA8 7 (/3j- ;h1\/,q7BP@Q忶]r0*a2:x2zlp@ţp O=?["wNI ?k\cW ~ 9wKxI3ihZ Ա#P.$k֋rK- 5P)xw͠('QWl%IM|"zkVfh+8׿@4C I[LqupFuĨČa#$ _0Pm7{J|Ed`Wm|IjqA޺]ٻ&ju}}cݫY@dp8*$|0eL4c{ p g^L jXQ]Cyͻl-#:Omgܯyk2o!ޘ/fq#\_!$z32&a'kCر!(d0o|qYp`OAO>岹EeZE-D`vUR>%3^ި0۱9P+ڰ5/2 ڒ*A^~z[gy6I#pp\{5 rJJZzMDae*vW%-QNiGCTg 8ia"_oK 8m!}ã ս(wE`u $AHyB/Z.ZPm9ɕxlp1Mbۡ-`R={[4ʖMdjJI#!AjRVa -v߫:k j K}+ĕcN]qH/*a^[FN畦qm~FHؿnR%_zC߀2j ED>}%!Sɴٌs`/kzY.lɗB|Jp*g9d@ (E}$nBj('v¼uP)g`Ktu5fg1p `32,2k 57`Z" r~!HK]3&Zkhuj@ӧ/Gg>,9/cT-A5 z=Uf~ ؋:bg}wś׆~t?Y Ht/m8!ODG;;l"i"[~Ջã%* QeeףswK_\Z~ uB~X3[RIΕ2K o&39Df%6aHT 鮆čH=ZpS0=['5gCޑOtz]9tl z;،䍳> Oo]̜M@A3x!:i&pHd?dq rV ϏeK5܉('8 p?HfIRG"hH/R5s2mɢO1/w &%s؛$+ԕ=~n|\&@ΐ?^3?שAՎ֠iT4 oὑS)zn]2[*iaU!=2tsGq#?tn%\`/OAZsWdž_θ)8X1!2k"|V.)VV'RV1o|V=,t[Z̓{.C|_ (--8TH^b%eUغFgV:-| uLoȦ:څϖQpIҒrG09&Jh YuU(@N ހRA>ѧ-mN (qjж{ȏw4gqZrHjegbD 7?f;AWJ-o4 \rw]3s< W u(a1iFrVBBш YCfSgKt _0o8gUȂgc-KTCEr}l-GjߡCKjZI#6yq[ &_oI]c񪣡 ]hX׾?_3M5Uw~zq(*)oAW^}+JTXQkq-&7l%= z-m^LZm 4XzUK:8k RI滿@/K*00ylg93NP!{u|"dWveUQjf*畊*DkcW#eYOQ>c$ NQK B WD{&8xvkD#:]1|B&hHv䊐 ^ZƩyc$W>T {5 ' h3 7-dRm r>{}.mE$o0C6tu%LJ)0zVa3-|ǻ%6_M2-" 3MwAzc i83<0~(~B/ 1G23˒9AI9\b &k|}~BxPS" Bi22[D@Zg`@'tPcpJdH| ܚ aƿXepo=qs@^vXU)j=3ƬY^~wHxi;r-Υ$l9tHv7e|Sz F{xE ^ԔX^\.=y.,΍3դiMu:Pi+e@uw$-%" s`~$WK Ehz:hw2,Jnh+_)Kelx[4;kuǂR&1=n>tw.5%5K$ | `\I0\G?7kސPWK ' ů ЅލE2 2\" q( *`Pk*NoBL..G fyJ\ݭ/l<}zdf!d PM Ӟݺ^3w0VPl^A` pgC; %t;8x_{N70YЙ{I'ZP$Öb]#nist'yGVˬ=NFK[-g8ϩds>~s(u= Xx|ߪq%q]n؎2}{ex)Hci8G€dG5_!F9P҉tn3ZfUFwn S ^QTk b4m&W*a~3ئCw LEˤuXIzgSGCHg`AwNnՒ'?VRұM1[ޮp9zjxGW`}>17bfրMcƱFPr,ú*5T3#fV?Fp,*bxP@#SrJBLwږ }]Ոi-/lrLv3*Rg^g9_3q[31;z2|ă‘P$r]ߋˆDơY>Qbf]]`ꧺ0uޢ|Cj]h#|='R^" naC&*{kmmWM~c)Am;W2/uj٨+x ^ԟL7?T&ܱ̀{NmQJo³Goz`lj5h/Y:jڛ U#|&/nsE\3N5鴭6L7e}tbqMN5ڙy0LZ.fuUQVAV\{rg]& |t2mcՒךͩG z~$CM 0IpcBLFx -Ѳ XcNNL#wv R"$ef>WH&ypjT,q7eZ`;\&`;$6!M'Ũ^PqMd%MZPIWf+x9[좬2@ aWhhU0 8%J5Jn^c\-{:^4͑-.s2A|EeT(+sl%#,Pp9'Y(=jQk`0geKqcA=}YSRl4_B4'22]V H dSsi . mUVH7#t=)3) P8 3sxsYkB /![kٓʪ}OiQ]@bz3ĞΕi4֝4F jȲ6O:`3eІ_inW, I({c*!tL/3'Cv Zq;d}WJXR.f_BAhH55!B^ڌUPSkrFBҽ\WgEc  tl8M ?-)AT =:$?P>I748=]:/6d) QYt(R܊ee ?^CusN>?b)RMjBCPX3C힠5֞9Vl{c#9޸ÌpuCү$ tŔC;C٘GzcW~GKɐW0[E`h~01{~PV !lUx8uiVG{ڲۿkB}<ޑ]r67(h!zՈCQ22^D@ qC$@D @"@ڮhdfi&Yo>b a6 ƥ<*x2"ԥN2BE 0k"rBI71|*HVE~0l/%3%UZ$wnl$UI$"α#JȻV\)h.(ϥ}i].7Å5^Ç$1)}.riyɇ;2{> NgtY5*UgXn;;6bP2׭rLI,y|z,w`}DW-TB7kDTcP^*|T ib rCG7^2Y#+TH݃ >55@ݣ f1&AOw#FI(:u-^pǏ: Z@:/T >l+6(p92}0J5)0whΑ jj&ȹP-Si)u5`Z_YXPH_r [Wqȗw*SSxnSk2БQ? PxJ)7[*3UMw'|-%9d "=# !_3%| Ȼ\vK^FC[0,H_ur$O^l;T=: -eZNiZJwGBMcY (KLּ>wvTdeݘw?.)`O`0RW;FyJ1a:rGJ'>\᝹T }VXy*Ppx ҉a9e-%;3c`wy#;TA*vaW]:^צƟ}/Ek6]!4s*JDGB,4O늓aG]JvA))U/#+I=({5ݫ[[%%ʂJrJ@5ݺogl=䬉jK9de}Z`[|%;;'ÂOgD0 [ "]PI~*K5R<8;ejFBvt@'"}>=4۱M0U <WR~l" ut^h ' ,vԚ̩ŞH~;J(-4bсpjr]ev㇊ lpY rE8 j3V!T7aZ/bp!.ηonDPOLYi˟Q4L+rGaֽ]6( -A`<[%eKr vي0rqgt؇UG%sQjwvb F`|2X^' DXPrHCI-P,?^l BqeRPW㳵8尣" 3#T2%!0)˟ d 0yB %r8\-Bu?ԡp/W:;/20nu'>/x?L υ"y dE[u˪h-eO2۝8$/p"'ؐq:y,O\x.*}K~H@(!+T j,$u97⨋BE ($UC^xjIoVa{ZEJ-o<]/Ե1 lĢ]̄uX#QQ҅|y`f6&0_Ie/ۻ [Fy0+{U,}F̜z '>*` v/ʍc0'WufPV<}<h)L[=ك(Gk6T2Od {V9DښD6hzut!᪻m h*Lj03eA0=G`j=f3x}Ks5HJĢdB:Y:z٪ s cKDcdyf}۪J>Mar zc#-ŵ(Jx{*|r/=]Rpc}ߛ /=cVbhxhs\Mm<*z}dT!7@M[]Et{OH+qnȁC͊r\r5"a-ˀe,: i^Zm};yJ\u-/d7JVj3Zʽh67/n6Pܸ~\JlOCfGPik3̞ mEΡxuߘTTWNw!)O=y0_C[l@:4_u&!& x[/".>ID }7I9Y; +/Y,iځB2aYe0 jQ7O7BU:m/;iug<,_sdgWw+b`R!eʺ&3h-Yh扭XI"'^}ssQ4Dޓt$; )k(7jQa=?73d4[0U,>@CѾ8A%wiZ2w,KB 6jO9T(>^qۧ/Yߛ`X! ~߹6%U$Ct0|F.d=S3jT#mIw $&o]o>=T m8Zjj(Dm)rw8J4W-{ϮyEgYC% <+i"LŚ:J#SAGW];eZo}eU[D/+,BNfv#X<;.X ƶ6ҁj;8#BDo(*T+1)q _?ծh5cuU"bOq1e*p(*, WLIZ[/o׋E4"b 35=إ[1%f^;RiU[,LMT6YNa~󡗼Se;,FvdIMïyƱ@P͆Ћ|4L:QW80ShIi#9jWM=_jANոIi*H7 PP7raj눢)cZoaLBenAx@ka2 녅A5K%F2`[ ]`Z(,es&KlHy-wHTk2267hE.ل"1 \ttoy 6,OR?} fԯ/_ 07 J=z~@ jmr?͆Qv>T@P44l%h㶼: $Irt`q*Ig'K!ؓ[KwzsDs[!LWY&G,ëZyzrC04nlMLD}UӮYDF<)צLCMD-Sa/vc S6P/:7L*nXi l7{N2#Te$P. ~^[ݘ2*SWEARlP%z ޟ/jl]Ah:`GCt#enyhU!’a15ؾ}V>%r׌ħ,VPC>A1{侧 ~e,hCS$y{И,uYӢ8Т]ѿgZt1%~OR'0 F]c - [ E+'2BM{,_̮$; eb&,]Q[ !- 6vAdN29t!(/ 8m%Tx&~,jʂ}nostn7̋&>"MJ qJ]&1ႏT%SNU]brQ"A a5a>h̞['bPѡk" tsh_3?VUXd/8:YXe[&΋"_\dL</(~@{D;q&G)I ]٧w69ݮ5j N2$AGI'T8C&_z~^#>=#}For(}PK,:mqA R{bƽ.-r} ~3l <`ҕ EhZOHC$ԙ!`vM`-]qGڏ0KG_0@BzRڣW,Vn"AIb> >z'RGà8r(Nq&aS0@@3l#n&Ny$l~4Ǩ=='F@I"^F\n/Qڗ'>=3|z VD}՞-9f*jI*]Ӕ&!Cs WIg*/t{380Dn 4JO]Ȼ#ٹЧ{@ϵ^s~U6Ǥ]bx%=hˤ]Weg'xN~:Ж00<-FT}/,љqס0s:t~\s#n" sRq\KVɔCL*(e">E 1k ش擣qͿ'"OihS/+\OIE~mlVş|2'iESoc_L7pM%9^GPzIRzuOv՚ TC?$@ZN sF;}?z=cuͤj>5YAƠn6oikL>;AZ`["#ziqRl@IaX;lgp5{im~"p5P|"үNzS{e4_a6lHXi+zf*ܔPyӿ@_FUd;eϴpP9gBwK[|;HDlSohr!Foiu7)I͌Ufҥx↻rbE*HKe}x\,ɛd̙4HV-lOQ8 l~F ~%v=OrgFzg԰hעXkR .[A8Rg%U{5]P\~礎 /[ֆj*@cFhorV|S.;jƽ6yn*22aS?$q@6c4Ѿw$yʨnI+]'i+>(gO0aF̤%Ȃ* \[=z(B` ."  Ms/B:6*?ή )#[yݑD4EPw[9A{9yRE$iDT],wAܗ"#W@rK27X٧a%81R~,#d}%ֱl,w,r*b$+̷!8ik+ψ6[ՄJd' 餟hs63"☄N*o<W5"Fe 鶼cnbȀ*P'|}D 0`jBΰVuS^>){*?G~_^Jv*XH7WB~6LKǠyRUᴩ]'TY^݉!Hښ*řųF ՠnh嘱RG aWyaa~p4~,E h7 '*>!T 3o:Ntx (hƐ5VEOZS]ǁ6 #V 1ʞեiӡ@n}Z@MGԘ/6,O]?Y._[tQK׺|Xcw?!U q.DfWT_|i|(.ɃNdkJ, |Ԇ!d s`ʹSrn -cssjO&(},Dfc!G{_BWhOs2[EŹ^(;H.@lpplUI(Pܻm6hgg'I2駻/sGudB?6+C$&Km y'gChxj9% n9̒bPlTig `p1\aA:R *'fxY'3h}wڹNR 4,h{ 0}\;` /E:cگePmDJUjd`_ҽ@D4{dGBy9dzẇGlOHMT |zrAw]%{pטZѧ pϐphr KL{KLO0jGje53VW!*pVl8av;{G34Hx9^umZjI\xI!9Ƃ9)Kq_[3 vpd J%'@.Bf6X(XIb3l1\dW5ƋO&ܯ0>iNGq'W]k0Wz@ņ1}Ouk- D/&0w/|g'޸Fe~}.f/-ҥ> 䮓 Tⶤ{)mK/{0kh;?g$-āl/eL A{)Z? ^EK2 2dS? `% #=@W-;*hott]A鋻!~S! Rf?>]pP=*iM4fI% 38ㅊ;Y\ , X/N $S~BAEТX~. r=tffF1:cV[g@˯#_b.Se\6g%^pm'DKYULJ+p&Ņ" + *#Y* =|F0)KP x0J1PEuS` ̓ƗבvrƌR)s\t=+[#aB}q]lZq8ci 6j4s -BU;c"&GжLdȌgk)`jp~Xcf@ Md1+6.i{%fX0W 5IA"]+1l`y;ήAE(7$Gs5AOOǘjjj%#arpWG[p§cA.G&jvrDyN58"R04/w|96m$3XxܘV.^QP<=8VčC7:+ Ewd}Tz*4tƟ:hb@Ћ3ɸ7k MfIAn@hR5k^a\s'2!<]oɇ//+Œq"8_DI2Z#bF\ka7KŏҫB]X>tF|\nyV,+j; k_֝bquy)1`dXM"zqK 8GS @a 2|#s¯5G0fU/BX)5|ڟr%݈5R6?b8(.{c0=逯)r( OrX;hZ@GG\?6Z!\8_L<3a]-j4z7oS-Wf|]o] >Z*AaC^k-ۯ+vEZ׮B ZsBALKmp`U8|f"S:7[-j/BT9#E|P5+\j;n)-IJ_얙h.ZNȎl=PV5T^Jf)pݨ?iEC]` Bfh%lg65ɕ8Q.DPnY8ªo&¼GprdV?4 xG]0o{HӄF&4oX*ZI4ŵe|A:Tx MP@= w0osxDwu:?Cu22eD ?(p``7dD@Wa ps̡b Z=` c:aM׆e,'BCWn˜j+ӥDD@=/o#$k [GF:`]ODHu$&DPEc J,RZ3իéB(Uز'O#^\qjݬUtuEnz XSNX&c9r`+WnОUv3`q̸ljEMŅQ4okI6m:n`D锏ܣ,Qzo4L8p(GEmz82o@eШ":y$&b =:!_U%o`WLI# lp,y#$xulװCNҫ̗ޚ_uV6a?I:/erANn_k{\sHE-RvG`H@(0HN`zۚx/ b b[ Xy]8`,ֲBHqdK|O3S@Vu5j]Uajb> w6yKV9Y`K-Y"0 XY}9"lH7yՋ*Gg6qUSO܉#vZC ^e)d~~ǟD4dY r;:Nb=T`LyNkqTB E2 2f"?@$rb= 7EyLA>-ȁguń^yR xpBQpo5V{{$+͏t^Jozi3EMtw0NhF b=} )U~,ULw9[eX?IB%=ӕWm?\xxWA웍KDKuPp_ tPJةpmƌGПόp5aO*X)vPH-z{OHP!#5-P*bAi7.Ҽ`,zh#_X7;IB~g.aA 2Dž:U_9 6Y_{[u,t`lb1=67aKpBnܟJfkVl92iFHME; Y}v6k)T>RBA>w)bٔ3% 1g!<ȇ7(K+dᄋGof& O{uf{Ȏ],YW3W T*|yӽJdhhɪqJ>"&>\c,ф|'2#:o )&;l6w% 3x}.n%`+*~I WF\X{qur)?}< 5?.߶uOǽO3 6w7)CdʙvY`f.p_]ÌRIH#uCr99kMd Jg&(0kN< J4diSME**r韖TD\-_Θna+u?zYUرƁs+<WpB##!h(eUBRC-Y5vD2j;jWQB j3(K{(0=EMzD YXOD`(U \ȗ[]ZKSaoMSϵi߃>5 1NDmAYXrjn> .}biIXx4wO FHވO>%fYgPBְb^#GkA,l~WRb>eo˗F̐|+rW\[(^mHF<~oP,A\;OOA![ZԻBw(w\njsZM o)WQ? 1ܮȍq"=F_AA )6D9[%uDDX]&C@22gS?0E pbXa@l~d[Av Uk[9Y-x>AU ,LwM+0oOdD+jq&jbLf K7`w_UU0@a>ds/,#u۵O?nyڟoU>E#g|4Ien]qȸLƯLZ8p)Yi:̜YgXSy2=\GQ͖15 lH|db|juȄYTUkZIid;Чɷw + \l{Jm+l>8CkxO\}/6ȆT+અGJY܈zEZvSX"ˏqUIthW)\W<{5 R]`zo 4JDq1 %-3 rcɦ1XLf#QߠOaRWXh\tO8b7c3VGaa"0[;Ӫm(_lT OvO/Nuƙ|ՈJ*brVRC^Ph&>ٽ:*}ЫqwQTSfCDz&zF%hfEb,*BD9މ<{T Rd3ڿ h$ua]Y[5"bdk䱶{Om9CBOoĬ$.1&n RDfx yɰe I;` |$Γ`FD5 XCE2 2hh ?@(qC0C(`P @Зbg@ݰ1VZ*IE%qpͿ3gq#AC 6BTvRˊ*e#p̙#G<O0}ͮϙG,gk~j#i(jY?.+}u` SkWV0)^)ʐVV`u"`61` mB_.n%_ IY.{1ayfAcdsK\]73ƃr_%b%uZV.N ]ma]O=g^SU֎[C ؄ 6>FU$5N3lW ֠}*6{ XqZf6 N58SEwj+zQ*98o/\ v C5zuIIzk2I6QO`iלW*oZ]gA}~q.c}[?t Z_Pw=_ȨvId^Y$SFdS}҂?)Y)tПI'A(Wup_c;\ʎ_'tƍR3(Ri͟Z ip3T"g6B3tu{&c=l1?8H?+ʷvqT㮻R2r]]?bliDAbHwZxay _ nFY [ hHg0/7H2W:JN/_[8"lhDXs@s:x)}2_]@ypEXNdS^L`#*vra]{nef{o |q'8j)?Oqn/?Lgoj̰u6_\ - Ј+҉ŗIJѾo>#@BKquOr)u oÇQ[͠s꼝KgZ¼B HїeHuJcv$7Zޝ(Q 6E@j!=צvn?'_9ӿo?"utNڑ-ɇWf%NjQ꣼>$?:0.9W!~Mj:՜Zg͠PX1%J1;tqd5_p^IIBݭ:l^ϰ״/dV2%!4J߆ 9p TdSk' CX񔯂,F4F OXi\.`,܇-0އh=KT}uqHšǤgyEFNq&IH ?iTח4sWr!GE_DT9͉C?D(GP3@qf}洂{fUG5izg3}ySxCH-ՒaWgDB;TlDd6!\  z~^(S3kh3H,nIUxNS޸L&;0v#$`2^ cWJ w-hTU_1pKuȈ1M;{$t5`p9||YbC~caDAZ*)a+|pBE 7p^.|PS7er7y!e-Ϲq }ИHK)"ڂT#N@}nroVICZPY|C|u o w*䥖wv@Bt˛h;!9j>tNzeα߂+b@{O ڮ":*FAXP.U~(od 5`Sޜ0b8p{ Z<t-` XR!A8=zCkXDOC2M͆b\U lf);I\v>D]_ԘjK ~hX}# )cXGe4QϨ?_*o&JiqKry V͡o$u'3kuP H/#]*έ+xY0to~7V ´*@GI}TYC V{ &1՘מ c{!}-ëvN@! ltŤI٘@4;VPiSO1s9I5(B}MB){TG+o̍-Z _ .P3si[7׋D"mćՙlVxy\z~/&az?e.9W5K|? s)fNz )~tN}]ˋZko<R&iWg(ingQb2ˠ7Cc>rsBMKk:W#"fK=81nGM`, פL:)H925H?Y1 9>JصJ@n:)SīOsH,:^Kن3e1j2%YB(;/xz;jCԨD" [SL3FDLm_͏"WidSx GzZ](;,V_eۣUu;[ 4bӈXL,荼疨&$=o ulDPg%m6xi%Jߤ#:SL'qsR٠ዸA.nL%H|.7p+-aa+흥@>x he{ ˥ bP7;w(6N\RZ^{abKv SqM=BQtk/ %E?Z Lfd$B$m}{"uc*a.@Q"NfOY,F]V5"_{]1DC0. ]P,4 [U[W(F9}a^9,z9 }¸7Hj:aP6|y"M9ta DQ$\@N>3d:J=]n`r4\z&&-|VKؓI%a_M(g)mRwd#^c}AlTqO4}h4U]+q>ƽi@nuulcщ"ſ-uVyF;\kgܩTdK[fz4n JW(Npsh^&9XM#u=;ϵ­͊jp[f}i/.dUJ[BWW|` 6aÎ/ٚz˜ۆ!ylC>*v0iO8[2G@otꘜ-p̋3RZ -Y[MQ?}CdIG`6L~%yITN4l8NJڪ 6R4>UDacl-M}2Xe:> HRWUcd"fLf!q[F6sM ^$9n}O0x$%>3T1 yjVKrin۪B)a/ש)z>M#F":ݮys^q FB>—[GTݲKjp͇]L~YR x`$5ut,~!G<- r66* u8͛On@8ke9z;,#c>gaq3`>4m+z+:XJB u 0XѿZO|9>9=9s`$ŪֈJ`kmIXk.ϐo+Wcj:% =Y  ܈DDvcvس_:MI  9ڞ*[>hzҮeF@̜|S4ŭaK\eJ#ɗ_$GJ-6*N  "aIͿ}":9̷tFn z1>-,}j!"7}LHUŭ4O;ƫ`YC3A e1!k(UaRCg'2#'Ow Z %w{xb ?dc{j4s%ˤ/!mɜr nlj8`4 Y_CMQIl$_){ԍT34ݘw΂ Qk4vo'AS~"*c0!c qG+ "Ϡ' ͯW(HP:LGWq5RxX*mJB?; >S:"SHSd§1\$%g:ao,?ٷOCns&0lґ*t;CLܳc/]z'ъP-[ ^DX!Gpv`BhZae0:Er1z?<)MeE5ʮb4s&d,;(n~}L? 'bńѾb~n1ܩ-2Ivb5.58{ }BjgMz:58{M?s" qg*~9tx|){*PPh_7q_ Vr[r]d [PgētDQ۠[䚐Kh71yE{\T+%?@fQhp*/xBVG5AΜr oK{`>VQf䦬22iD( @@bX4! 0@g:{}jO{ AˣV#MvXkNI=6V֬]*aiTncM!<$IO5$T=3QQ) i덥tBW t-V5\[*LT w~$fxx- /Ч!}`u]3/c -8όno|Rx8y BGeMak'H>Ϩ$bAE7M5|G}onj xZ4'ٟ(n_1W)/"6 nJ"$L,1L f} NJpe =yyhG} >Rj=xbt{<5`@p<_ɉ ѧ\G12|lQiQohŤ6/!Ձ]x 0\t$uDI,b\;Ûh=)T.c^=gk)HH]usCbʣ8ƒђޡy\ qMEvk %P?HFQTVn(19VL8_4麯LSxf bM\(FwE2 2j"@(qhP58`@:[u) 듬+A ]c5swsms Ns"s@ݕ4[ir}Cm0ϞQUq]36/~z=Màu#7cICOxHU};U\̾s.yHDצ @j31eWŌQ^1W0snnOϰK۟+ _`_: ; Ke 2:ivqp ZZdFޫ`}͋"a |UxmEp/YvE{?Mڢa:kχC1_2ݝ)p6_r yn`hk55xI).4k$/3|?'lLVtaGj-Vdoc G&ߎPwG34~!?8|{ew@ߝۥnS:bIkHk J9 hr`@[X"$+EK?X [[a  O]$=$l[x xmRc miT=(XB*H"2uq jR Ӷs_~&+&~ &ŖB^ڃ;\?Q) \{ΐދ'ABRWKt}+;chb05HP n@}QަPx=!DI!r HpU7QW.H>Y4QL|mo#ץ6Aۦ~ZpAG3f_*WC `%湯IY/uM\6(^nԽ>*l gc#=qCwg;>. &;,Ip{CF==[Wi_P~QxV.f^^+OW) GOg=dҎ_fhZ:w >c^wc ȼ!P.m65"q侟JCuo22kV@h@b `4#J5ѵ򭒧DŽ;yUFtx9oK 4xk>wRdЇ 9lYgˉmE&`I io/F}GJY̪RlHw@ZqORh8wݔR|%WI69bD!F}׏85`E4qBF_72(6T%fکU5$ȩg[#Y@#yX6_]!#gofk-BeRGZc(:/v9ޓT= N90Ęzȍ/їE@=cѽb _@4"(BǮ'GDۺ|T\ _vzR^HPa3UvB"*s)_JCA˳/ț4hzK4ӑ9'#+)=&j9'gw(gl-9tY,.e!yuqpk 4n(VX=kKg:\r I!&ߺ!BJ[fz(3^7nB]R7Apk?}؎ǏSj +.nQd<&1D94Jt[5G py}L$* x{⃠+Oq"w=kL'dJլv &\(Qlrp}f_BG22lD`tdH("erO.݅44~O †"i$Hc3m3#L;%p&aبrtUH-M,<<*0UQUL2? qCDa&X0iBSNB4C-6"OQܡ;'"`rs=$?GU5=~r)нp:Z&H?65|o,!!ECPy:nJg/֕ѶP9-ۭͶyDdS"ŗ[N_zWnmM :5oGVH+CVЄ8ψ9jaX q lƚCNNpGQ6zB" <Īy>r@ Xeӕ '"h]1bzBATtOD,PWP~DKֵ|:L4i}ycU(nw@-6ثNciŰ\ږwu p(]jzihKN¿vf}#RN%ގJ|l0dDy^Z v ډjc K :#gZ'Ԡ phB%Yj>$-WVR:S!.\ߏY଱^ .@x=/`$k-5];ٛ\St"J@BNx3]ǎMpF?a2pA7 9R KxINpPL#篡+-J/).f$;n[jJx@0D+6lzCUa -/܏Oo'Ye:LBw!L߈X=Ŧ'iuoq\Hժ}B&]Ӕ3U .)YiMw!Vꭎ%q.Q$K0An+V4Z:ڣKwY@>)P9RC22m" dcF?zwa)J(j"y!ήG K~/ubxzD*د6ĆA*/ F[`.u*^kz{C"'Y7V"{3\[in2/ȸzᆗT}7kѠwr_g s/"j?UC,,7KjOi(Db`A[r7Vq -"a^2NPH$ŽnjY噱E|h֝_Ջ<#9q'G zM,8۲Ve8/R(2q؞S% :TI~RXGn YDCpLJD[ȉӊt n@ `y_/f9"Ki=Eo_YNnq-.)8Uj::O^eqa^$6VCHykPi1mHPnL7uIMXinT,o-2 D^7r9 F+һ"1N;3*޹#n}K#@/f3𒇞3]SڷF8.ݗ8( mڔLN6`W]8)o?bpZa[qA  ?n(mN?/c0Ĝ*nZwuw|-#Sw_uY!Xm١ a7j,,eVvUkԾq W=,Nck˓Mi bRgbܩb^d-g&J*"\oH4x f@YBI͙7U,;4* y᠄q EƁ2 2nV@(qh(c@@@-.S#' ( ag՛s?y7t YdWNr~};co§b*_ :>MCPJgo4~j!ACAj5rϴtب{Nwɰs2\/7gmQ7Ӱ6hA<+D+k=Ѭ]}T Ye^~Oo3cnFh-a'PlûV,Ә∉p/F`O_׮JS&C}U=K @or~[{}?6?lm܍JpϱYՖ ^-n/q50c!uNA x$ȇ`ka@2 | ԓ^E$FJ2[ݚP  )ksw^:ebm0p6`j/~J@bK٦hb]Yښ96sG-cɅRӜX3 }Bti6hިeW[c^浧U.qSJYơ#e*S|OdqkrR @:cN݋sMkJYVT "^H > [ߵY9龿^ ۔ZOqIa4?̽3OLlWˣ= Ii/qWy M ΅".y`G>Q 6anj_TZ?h`n$=IZmY3,])R ЛͪZjM̦ind*Η1EL,\fdVzqKWNZSHآ LiEvŷuB+x@V&s:A DWc-pM0t:)EJ{58 a)`%"S]#ԆЄg)@v~; `mv{6A)=bMZca_v<d(*m ϟVG{М<繺J8y=|W=OAV~WєAgBMq6)x;4R|'z/avniz+'ܠ-(XJx8zLHk 6R$ T⬳V~کgz&mڽEAd:S[@z1ļg$jBʷ'A#>CQ22oD FaBH1c@rB1ڨeߒ 1-W!qUg c+en[CpkB#ˤ܏|vOQ%Hl[TКTgo*5pO8Nv6)H: Xn߹tjz>os xxhrI1 vN9=:yCʨ'RlF""YOϪxabsp@[J ެf45߮FJ&wk@E3Ikhgʐ5޻#[9!P>J%$"ncEW{7*Hwŭ| ɮ|%l6.vײQm\H?r-jΚpbZv?-MB᠂,‹ f|uۏ[]CFғVn LL; lHVHGl O[1 ),ê S^7>EbbQN R,H9eO|-?%889!hd :?LoGk6 2VutL?X&Ǹ;H)4h0uV:'+w1uB hMyn*cvGֽ5IFl+P -SM4b饊ϐ&%k#ȥJX؂8QLTUu6jmfa5*iW5 * I5#N_GI/TG> ÇRiRꌥÅm{1"DŽ_Ytq>B+shtb`63@RNZKqF'7mGm_k;%>irZ2<ѹw>IX$3Nq9{HOe/r9YvPsלw֥ަA) #}߿Ĺ\.3ў tvej!'D'u3!d6ɖ4bfBmh g$r˱;QmOxq< !#@b#l;}l0f3v& |%1i8( }|FoMߛךAj+z/Bp~ṃ\@u˅m]?Qp YXy.I4hTbİIXzؗ}xUcHyd1C!L\+'DbNkCDpO5O'P_Fcqtq`f w l6e՚!ۿCHgVC>kH!ӥhw) `|lgnxq/(Z 6B*;5>Qbg[`km;zMNj[MC#hQe=x1R,ռHA$ƏQu#2o(r$H(T(,45BoAkqGغ/m0ѓ$n.fļ,зgEa{uY`V[U>L`-L fO6aυ' / 2O=ѽ[x-Cpo@BTw~~ EԦϑ~\ 'mnq%cp6ux7Oh%_nS5ŀAr"+ђ@ !XPiJ:tMDzB"U!f|0@8Am!RZUF\{ 20yL4VL>7I})#a#~Q{& F ~:xi *5d h3C~gAlGlO1-|UpB [sJ,x 5=T3]@ NSkp'K_g<>geR X 3U:X+ J5d<%%cp n_ rd(-G@MO +lBP:@Aϋ[KO" -9 bcx yZ7|G]Oדk/xhz7T`2ҼH,?&b3z"S0׺{?!ή ezSJaEBbAČ'Yp"H>'nן$p8b_x8گ n ;+ I$qH955H]o:H, n滿ȈXQPEXꇓP &"x&OEi1Y sgCkTv!U(x2>ڄځ?  苿 k0IS֓4 џW:e<Җtk@j[ `HĄ3XiKZ\v:^{61ʅp`/whSe!ϲk̋}E3T&zF,LQzia٥ unzeD|=vKY0{aGEm$TWP3M+WPs ?#' 86Tnﳱ-ۡHpX)^]>NLw%eRI qNBE)Q>M Zgpm`g}eŃqtcsh%聁;2RvPeȸ?Wƨp$p )yUj"@@6N(C{y`Y-oD\JiZ`Fckq'T8P6_$t!gh ^h O,)M#B4t pAn=,i" mLRsٺ `$R{ <]r|io O; ,4hVP o&O~]ILMfaF̡зA|Ňf+fzC~:?==u@ReRu ~PھT(ϐśm3E^-tbDSF>&Ț[%ao˛)OOLi#f]ePB]B!R ペ~T )G'dY{!oi_ /5\p Ag jQ);{=QY3Do@Bૹ3} X7iEs脇DxtNO:͑$_B*_*IwL6͎Nwy3Oit٫v0LP5x,$TS…4ߌ32qwA#\ܲo.#d# *o n"!t!$EU7Ta)-bZ-Kdv׺iU*H"\3iù}BV|dtDZ5i7Zv+S Ev+XgBlr Hv>6֬b?FVf܇᢬f,_7t5ҷ'x#G7A6a/sq B3n} 7gr+zj6i@W%៛3`5J;$Ԧ@]5/\s}xRM fsh1o.m0Sv3Zɮ/ he6eLeVrtOoPp'W8򞶅*5qV !2;2/ ~;0Mt8@g))Oio7L8r2rfSC!y#U񟂄"K´( Ͳ(_/aacCwoPTYn ѩhW\})IhZUN&jݤ4WqfM$i}^he| `f 'sri2=c&>ڃ[oLtkM}Щn2ir1<4*)t@ls|dɘFַ`Җw g/sZOGrFr g꒍2>+]M#b걠" *eNR:u!ٺ([z$-ͱWhH{qG h22q"? C@`YZpN@P)7]!xɊNj{ ZwuoS}zEhk{ꥇ=DԸ|zK;{3qkE۾ MR潀,@T 2"*3hh^t 8$-EH]8>4юO6;Uy1"^Gn!ډk+l ŵee]9vQCcLlI6ʫ1 &C5za źO 1 nc[HKй Eωuo^D ~ }vf>̖!;ᬡDM5L46b^ 8? ,Y3SP~\e,P^0H}뇓ϊ+(jk*Tp9і'b} 1^ZYn|N}Wџ܁u(b9 VMG=jŜhp_8CNm^ b ;N9"FqJa)jNj UPM Exz?uD =Y/C>RjAȪv-p VƬ+}mF؏άe:Q$-~3t^ h7_2W v@j<ͧ4P4wu^8x`&়H(*L>Ѝj B()(ߵD5L%xiɅ]zU6!t"0.ذۣĎ Tg*ԈQ_B [V7GDHyB?k C#&yb0`g3Q x^&G'D]zRkih/.@ԀF4?0U95y0tk- ]_ccRMÉ"^.kؘbNj'aqt1.S;7\YDۊ!rJRxJhwR=%LWT<Ӳl)Dq df{ÁalɯnvKI3!-N'r$] tH7i@fRVUKBvݵ |Wad^|C\E%8,B:p.n.aӏ#Ayg9ܞnT)?UΗiL&P۟t8zP[ DToz:2vg O[g`bnF |$7"5YoZm-[v=Swm|nc=35T;\^"1J5B]Y)_s]h(DfX\εab aJ3$(R"kG3Ui?\h]5q >:VQ BF݌bT@h-W ;E=;]DM$( oa.ie{=qi|dˈ[{ (6+ p\uD`D?u)Зtk12uX~RKCAc><[ ?K~_yi,ټ 嘿xs(A%F"CdFe',iɌ×zN0C22sD ?  @ch!% Hf- q mGY͚b)Fwa_"#a^g /sP˃mo2,^c ~ҙ|j?8n2WX[.{ /4m5g:U,Ԑg Om?+l %"\Bdp 5 ǚt>Cq4KX@>R~ KENH,} @]p)̆ı6LZR[8OR2a D̔r e*jo~@c p݂[S.Bi~bQ⢀NS\ØY^=x7CDWnBT焽M(M/-ͫD* R7CÖ"<Qĭ(D F3ZMM *cnފ`GQ~d݀7P 3@78 vemO4-3!vnKΞLHxDئ!S|F @ tAsI`DRPp- s NCuÊ M }&>g5Nڡ;XJ%gF)n[̴_@.ηjdM1w< E{ XqdBr#<``΅{h^D,T=}r *'etTżrOa@fp2|ss1*0%ɒQ01@F2 2t"?q@Yb0* "H-i-^xx*O{L~iƨ]k% 1\œ>S;E1(~MxiP~A<%cYCc#=c=a25{>5 d~ I \@fzXẗG`-㯒\nHW],[k+g¤-JdS\ E>^U.>".(1K/ Do?PʳK :.Ȩy ?pҵ[yey^*1KDVmI͌#?& o_X)!տ7p0-kn"GS+O(pҒr&c@/CMnNY* 210Ȣb_wJƋ\9 V;a-!$w,t@~dFa0[qzњ/YH!i2 Wm*%dŚMԧ/qRs%[ @kxzƬGlK0@!3Ū HS$wՉHs8 a7zywԀ*yIG~:Vva3u0KUJ39T@*:oRc).Ԍ mU'%2U<V?!.1YS]ED;CDz+c,)}a%nGΒ Ȗ'[D*go)_%Fp9ُq9Tr#KIr#T$qW;KU)ƧqΜn0*mԾL~>h.r|*7{٬s܊MHUk zo@o7Yɐ]X!`'++toYhL)(PTVbЅp6'=@I#_Rmۣˬ&R9,),8;Xj#'[g8bԴS[2Iw=A],ݔ G_Y&Ւw"dA Fj({U̽n&K*Hqqb"^d."YĻT*j Yj(gYOtZ2a*e%1AXi]HSLX{| 3U #oF3^JSӗܰf[-9 [d )E$gKHa>P֫?[%lM/6^%5F6 ]hc/`"A;Gٞ/X]&T|'Dwp *jGw>Y:~ɏ EDoUipMIXn<ΦIm&ŠU6n"KVG8#?0yVmNUW609^н9 ;MVN~)%䣢f5lPhίE"bIRI$\9r?@cᾛnpܣKW|88` !q*Ozފ{Zrw,C k}|k"Ck` \mP;%H]a%c.CG ztZqpL1%@Y4EW-70h!dC522uS?$ jCp @Ssy}0&`״c(9B[Y|!g:E,SR!)Whiڨ~R*t٢Hsc0rcᒧ12VmacȆbRUʝ s[w&_Jk:\3kmoU Kń=~)tE\4- /մhӇ-6l{ uP%uOHx~t2:L\r'R3 S;/ }uPkC@*_ $cD@2Woꇅ`o,紅)Zt!T~B[" bd#<C4H"XhW&[`W%\Z~T]Ŀ8&Fxcb[P#HdߚZxrrTN+ @+)L;}#r #2)Rvh7%΄!AZrkF]E !z$J˸ŜyAҀE2@S5w?uPȾLhҜߟ\WևlCov ֺ}xmKI/9BEP-_>Ę{榃̛o$75SfΞo/N3gdK8ӏu0=IEe 9R XѠ!߫ģE:2 2vD ?@ Ek`8b"@LiLa!Wb8*!ؙB*(@cݡ#њ,&)uS6:%uPf5.|ۥ%n<{KQ5=a?* Ho hQNi\˟xofcHaHct|DXE_G8g=cvJm9HTABgpIUńAoveg8IMN!4|36|AN7wR8r=^, u`s}%eG*X[ ˜&quL$+Iwՙ@~5xX3gç9ipRr]pem`(d> ^j|?jѬ;c5s)iU@Ӌ&hɢQMcD %ꉍ>dVVLMܜhqdwKٸ5j!o_`UHm`C<:.))Ś q\GS;Vh,͵fεl'9\Y5ӻ`fހ&h'V7ᇒyW4jؖ7@3CUҰ>phNq<\E#V<uoW1>iBǩ 5&-Q{b8(Wz̫f1s+/RQCgʀtwNJԼ8eXٝN0P[c>¸|4Z،Ex'da$a=649=7]9ymZJaB֨-n)B[2 k{D4}[e-/6\ʇ N6m 0{+Ĺ= 7m- ʾ5UCW?P.1~CpRI)͗$yno$;Pd}dAZyl4a!o\/MrG,bXI]IBG= ~Q2-G986|/z }LPvizԁ˲E'800| A{9LjN?ΠyEW? ^ tkT90]0XF#݀of7kYR&CK2:>p#N6;ܚavr)ghe( (b>=@%jvq?y}SHۓ!64 U|>8Jw O nx]ii/t d pkVjQp I Bڦe+:/ÝF9z/5}|=Qf=}nBܠs-xz4KOC}L!n.HblǾ,77t"}jrg'u|+JʥZFlv*xw"b˰D4Lkka^RqtlUA:5o?z6o@ŗkaXb6Ų*.f-f(q}P&L&\J8Ks++ H{/ڀ/j ,v&"^Bƒ=-@ʗԿ[,\&|?D$n'8& |ReYNL/ťBU,O}y \"RhC MYhDV)1 O۱P}'$eҒ!5~RTߋ}BCNf:f̌xĩ'!u ޑJ9jlGd.U)s"'!`mjf7aMqz]'8'kuIπv]SPYf_vn2G)* I_ DS* d%R(Yqk{57.GxQ9)dQ]w/VK,)-ԫC$hivB90lCK_w܃U ;KV94b&H7& sJIq<57' AOlkRWg*2'!<+J߆ 7 p,p@Egΰq]y-J=tkqymP Qg*o gyN}ZZf`"FG~f*\},O.Csn J4]-e|MdS@ajH*8K:xX9k]|ug/ͥM0C $bVۚ!}Ml<~q$ <.3+ Jj}Ǽ.oWq~pՖioj/'UV5I5#teV`1\x%yg\,jzL?>)ޭ!d{ڟ>5{XX٩}E㴋AUF<>(ZsZ7*+)2/;]XUqi4W㼞-e bRTNì&|$jzPeo>llP˅̧yy=s1y!' Ub i<.i7:,Q(@Tx|g`%>=@4toثJg0NH6ossck4D%h`jXEbD㩔 S-3{RaR) 58 m.;)R5vUР^`>/mf擴nؗdi}Ǯ=eL!J=Cf`҉94\gZG7߫~T1B J%.x Qi*v~q#3L(ȭCQ2wO{qKxgfXGob`NURxiET7}-g7 Ivn&ͽb8!3Tidĥ0s7kkd vxI338,u,QiI'^!j"uԪNzXw\1gJ`~t,{Pcۃ+cMN#,1,dXӬ qe"Ĝ%@J ĥC8lvnd6M+QTb_]v& l\9[*jy'/d]of<6S)Ƽ#B[yN:19CuF˰W-_.h2NQ0'=\$R\&߂ߠ2Hy8x^Wr ?pS@K¬Roxd2 ^+ ׮-~OH^?e:2}!’n)j|:k%CTg&/=*]GNy3dq3O(mKݦ\({xy=nS5P;)YT]+_`l_{CDb} !vy-MԞV. ENCίix>j_]K:3Bf`6Ji޶C%]|a0{t@l'4X )%Rx s줸\`')ߑC=@Y'9haNv<\5<@5ڒ?]/طr]B(#zB OWm UbifV'KzցйOF4K&9 i4Sc9k%g'-&1"_M}`{ƍ$ob+B"M? H.aQCnr8EQcmɹa*%3>X..@R!p@d59EV)Gg? jZ+t&_q H0u""nOx<-FI~RدXfKanjK =F3s%ɬ81;7 u(jr c֎Pc،4= !"n:}:2_w+|buK-)y&f%[M8{>4~3Lqu!m!ҫ[hKK-KwPbk,U^znX N{WWl~ Û@߼j)32t[ļI|ZECv:g>x{:H2)3c/i8 %`Ð9)?e\B[#zO.*.yDbf$,z/ZEmeii.cpLuA@]͌ 0WA&"/K"Ja)L/kae]Y@>M&#[hA)wc!pɤDOP.) VDuTT-je+r vn1W3M5\O9q7`ak%-qzQ‰xYP4~Vs^͓WB4~C2pI5J+uU O\y5}F." ŖRF(`NJyCl$8^zZTR@8QЫw ;[Cp*iô'cLgsّIsKhơpCi^(MHq=2\A5YbRE19Eg>0y;Pk'f<xykwg@O+D_`&Nd5wnR M5U ?e GAc"̃n>?mtYtRo]+RvF,n([\@<*͞6dRQ~v3Rk?%`0M 5ܶFaCe/ĸX`֟7`Ɵ`|)D|3 #md 2(1.ifQlD3ܟH  RLF%|M\Nc?<}5(M\D9^BɼHy-/▵e Ix-nluZ4UBţ ND-#Uϔ7ƃZ-g&T(3gOFN('m ikMGY<{`οY<{U u<Ҡxh ~ |@J$*nHpºo-GƓ^\$;7[S@nTbvj.)c|/Ww_^y`;O/{"+vDbZjӆE$J*iŽI(e |E-[6O_,%4GrRLD{SMKk8JR>wl֋nZXOeXG>4Ng3u''=銙Z8YAVU gBؕOӋ{S9M/#axuDž(vðt1 B9ЮW.jxT5RJC:a?z:ᔏhp1fy҅VNp(F[=>O9g?4(5U[l=aT{ ?#k0 fF>C4d˳'DzN0k35B5ullqih! El*(C&ሳ1y.bvfK'^y 1-%Aʖͩ+Qzͷr0DinJ(T跐r1yn[H`$5},pHO4`$YZ w}LK% eN:=@mLyQ4FOX9Ϊ'e|~n\7}m<]޺ȧy4cƒOl[22yV:X ԏ2ٻ9ǑhV/S+`oee ft 2#45Lt,lv=ߺiE2pHq+nR/Nč_h.SC8Բ_72T,z"D|[5+2kyW41p<u5sdqA#+&2>jrwWบ.ȺmI~rp2n5oaY}Do6{3'@naK?-ȑQ}hxK6/:n{WMyHv%DʆEXIz@p~ϸt410}Hmc{ -xX% a, @ YO RCUN쌄 ="|s:/T@*gL7 D1=R]PƄ.wIk, A/aW.{KP92a_+pŦ^sa;"^ɤ\ iBP`2ǎX+S*96 瓔uKgYq.NZ @.3yi}?m_͐mhT/9x2&Ey3oa>8o;v{ 8wϗW.|fW!RaCГ%Q FSNHeIbiyK`FC1?_|&+?#FCHnw(~:7Z飔pcDWcKhja{emLk]M-3tx<:5P눦1{2v)~ q'Nyh(G *"kj^@O1=joč19-aY RԬCM2čD@9GC>>ٛ Fi I8XR Ԯd!d/tܴk I)nQى}YTg F3+|tx)Eʃmu kl;ew\S$kqS1l_iW]TLK\ar᫿6%8w3|-y<;tCZwnVK\HgkL (˪+mDWL;g 1!<p/Q뢿KEX ~hۨx1yT/ws+?l*o򒡀YK>UZ!o~l& j FSG_ mc%-zb Z͖` SB}22{"@b*F{ @Q"SolMeP?o|0-<_h ApfFdz3cl] ދȬ$fI' :0r.&<˹yNdg9e kރ7awY?R13ؓvv- ӊrdqfvn-Xܒ(*(0uܨX+kA(lHgJ;SPQyxd?҃۽2]<XDHqT7N-Į&jе"T|Pf 0wmv^"V/BJh4p#=*7ώ׽d΁kkNuAGq#P-ڸ6䪻)&McfK%PćG1T-}9j]4>wKI8 IpRhͿݗ]b'Y~8 ]*/k$d'QU _BC#a5>Uez`˶oNJw(õ /@gOP%OG|(yyul _޻]G~,,`P3g8:eƓN.iCٯA˜Z4hs&Q)IϪdb^4H}&6'*LR4p(8ѕF=pFKq*N$ JU$p: ;}~ x \KQӼ~F2 2|V aC @@H(fܡ2'bU| ݿ'>4dNUR@ k_VFNp#8d!;tP-_]/Pb}3G bAEU0S'A~T6Rc*VME淍YDx0ῦx۾ ݹ~b;^z%~UWQ9rX/nRkHYwV̙݆ǿ 2׺2w寉-~5@I-4W:Xv(wǓyÐA_ v{}]ěs*ӻgXʉŰ!iO'vL@hiJ]]ҭ0җጾ ^ KaP͖L+ݦb_eIZW(Уͣy+S6hLgRqYS L:䏃Z }G ==/{gĝ}(0W2(vP7]jK%2 x4%${R$R`Βv,_9gggdahKC⪔5>8ťhepSB 7PY~'NkY7{l&|c6 yGu8ޱr#|Eoe+=9*';@4`%&\k*'kA0R?)kﷂwOȈ(zŐ/Bcm'-3k$u2`uqX1 ԕnPol60Ƚ!v~K \a5@_j 0*Hi-`O;P$knyvhQ|%KLe_W!O@^^c.E$YbjDà T ،ϭևXC<^c.OIv;ۢX]xzUO~u:Ʋ,Σ $ܬ #-m9g޴hb=!F ˀ#CDK]6|a&ZON?~Mުdnk.I_[ga}H޴!2 d{4=\,6Lcb- )?  TU=\־޶M;rl~EQMP_9!sH*`o,%ca&r3+‰cʓL "% R l $ D u5z{{> eEBEaLGjczIF`d9Moǧ+кmn;&%i?fF|_85kq- 6Bx2l ?t/rR4dTǎAW}9/>@^|=-OAh|t9n '㌼ !hg(Çom CF22}D@q$# hFSm=3猤<f|uAȻQE΀bvDsc[>F6prn*퍟-HWn۸ -ݬ4ci}ףL)1-Gʲl+D 0$ظrFtxQ`F؆prX mN/uLd+'OodcI$HO2 =V˒n.,)ݩjJg(T_K~_g܄k)E,D\N9NRr_LR?,/=zÂD͔="I}>woz)ovԮVD Y)d9X**IJfkP9fu>OaEoKW/(.w>w pSBk,Żxer?&lie+ KdE Xa@гG@~՟CS;zT?xmcQ\KOE+NaKW6FƌI~ʌ7Slkh+p_t/]V߽,th\tF?7d"g\Kxf89 !>0Oکk;g!!?0qj1.J j1}Mjyg8 A0,o4ߤ@Q8tQgg+y`Jk@giۂ7PίpwI"-74D)V%7ׇۂvP\:̌y)`K{?wX֌VG+mGvҦ+B_FKML O*Ft>sFI.pЍGުyٙCF3@߃חQvRmR`@ڈw=h^~=Uku>]8ۦ'D7}5䏯HSl| UPDc$ήEvEֵA >Eh^^5vє6z8v(MiN;CUD>o>uhїxifW-b: Q ](,7P3J[n h8CE*\y|4"yC ʹ-<<h;'<$΋JNg0Bwͱ4'-jX_)kJ=cNI8{Wse/O)_vt鬳ۉteǀ\/ǹaSޙ^rxO| ^l uKg֞\5MEv(w Z #V/€B$22VqB0 $G@ -U=dI@l;K06!;rʧGo={e9Kr=j~^a)}񧇄m2= Fm 2H[ ,dq/d=gQy]`LA0@1#-n3F_%BMCblgʋ&e0T' RP7gID{3!(i}̽wU1 "B#cs1c #|SOf#ݭ9_ޣj+qaͱ?Yc2?t23jah0䖋8l5ul@i],p)dq"r,nM܇W6V`4dRY,@ܭr,uɯ hIz}kaÊ]l<5^AI.!_سJ0\A_GaZ sa)%oyN+owbgKQ.3Sx:HnXv؜7?LT``g!S$Gh-@V6v:"hH]52  y LX|vḬ2l׿(Yo]*ZaflH0\.ڈ^JB>U%ǃ݈>ٽ=m$2OŠ21Aո秈hV Nr,:8Et0237k~8 g "Jq6;m6[;o*ma$0VkG= |˲Jf~c 1}cĈ$w/%),ท۲=fQ!=nTtf i4s06)!eNvHonb]]Wt::İKR7[Y`N@ף >1ys6#'$13фF¦`JOkӰ#ˇ跈8/;gׁKLJp[ {IuH:Nr*x[s\YW6F٠ r掖?m>E;CZU:t=/@}8OmLԄf"rҼ!f_ 13;u*_iN,B&gA΂K8:'}tk6rQj] TRrwy:MjQ蔶¾DXL\aߙ_l)GnKU$  /kc,M7<0oSW}O=o=VS%MmP5CܰU݆ ]W\"ȝۗQ-$[+TM"1O#ojc넋n Q˻r]PGbopzgk>Vs _ d4Iу֑1I#[4,"u>:|GlYmw2?*h *4e*JrJ÷L9HE`SàXa+Ր[ Rc]_h O9*t1+3(ODpA6/5݁O){qQ?Tׁw2#!˟7@CB`@MŌgc_"ja?uj$\Hu\X cG#gpc`)i6+X~wӃ䊮?O&N)>|-UϤ5ԅB?P}ȿ1[JB*K""KUKL/pi#(]i $VU`5s>'Ȋ_4@ٍ] Yz;KfD(4d` 1v5> 0@z @aghLİ9tȈ[?p{r[Hp<Ә!a2! ǒ86BENgOCz)˜h !W"{]Zt]Giv7J=cfv}X6l8F &wo=h}nSOӿ|NKիg[`%jXϱAM *j qe>ZOsLLك 'R;5,0ƞ?t ~;wptvFdw1d}=%*P&ZYyϱQ( y}ckEӒX,:!q__ܲd-) ,~{OAwn1>#I.ôhu6|uzIO8SO^YZ%r#]+~7K4U=%$Eڨza$J|Xq3uoX/7{ͷo=_ËhbXF ʊ彪mGd^3U{^;Lf,j%T<ީ{W[݉YS|qfu hPk#B%:my=ز]衷C\SG/4=NGq2ц`yrɶٟeu'תH Fge``[!21 @&΍c?W>=p]m@~/=r =BٖH1lJ-2y7|7J1Xdk_Nw+-qտN_O)e8`.$ %^Vt%X"t#) dl'jڧ]39HD{P=,D%єI'??whg]NoK>zjXeD5S8ݚm+HIH*_#*`||}HE7Fjo߱͆%:ݻ{cRo]H>cϼO e>$Q(Hj]*Q9 5D))'U @ r_mIum1"'(r$GU۹ޮ2I8]k*rI,ϲI-˂t3a-~,]I,伛\R_slM/Ì PN9V "y +(^gSB/͛ӣp| 쨣NI.?!5W`l 7{C ȻѨc!G'UW7ԶTެêna{b^"i$rD6+0ӎnx0=ԁ3I#7.1%.!l`)4Y5[JZ΍>6~^8{ZQtfw{=c m}Zkom6}m7\d4D:CIй" ?$5cX;Ӿo69?r%l)Upw&6 \12"kGIY?!\Uugv p5ւpǝn5ѿ̫^<}iO<~Sgqs:_sJRV<]賫zNV@DŽ?&>7?$F{N|[= @Ouy?"?׭txwi38Mk3 Lܜ,OXNtA7 КTr΄FrIxz j7+k.xcOCS^:1]ŞU +wx\QWѩZb% ٯ,Eg-).q]!bsz-`j\Z V=?o쨨&ˌ}nMФLhHےv3ݠwyqJ6](8E@Joh]+\ς1OuJ< >؞2s[M003vpXUh?ݜ%4$)\<]מo%FAt-65o@o UIŌ_T%?ǢD:V' *c} iqޚ.tF-mB8Q9/ 30O ?V-z&*rSqTq |_C6^1'Ϙ};u}T{#s*iЁ9q?7u`@E$̢v3H@C/==(qcXI6(-U8/d!Xx2b1xڶ&`U_ee]JTXəI\]Ru$-X @5 On e1j~rYa∇5DH ~[ ߟ2@=;za3g}c%sZnɱr{`'C姙w(J570x8,pjلIQ dE}#Oj@ {9Zfݒz:{f"bJnox̕"̈́!SЂ9B]8Bb()0dUkػD ߑVιKxqp VmԛoN+Y[g-`/uoy7Lj4eTVL QHFOS{{PHH&MR,LlܤWmd@EIR_ƛ#Ѥf̍ӣ鬰pyRi}B/>P?26vZfNdbvFfO2Ss9 Ku]ڛMP6JGIx˃sN7߾HKInW"44%д47_>!犕FQQ>"<y\B2d+E>+ŧ8|U:U$8<]`JH睞E਩}~ߎ1\,IƘ4K*-zo"Ufdroc, ㊣v 1/ mm̻; u-tD儜}Dz rH0Nj[j>s~H<wdj`(wZ7t$$&IU,26ɨ,9i2=̩J+z>91N4ydl~d!_yH Է1abͣ'I+/lЂ~":)g-wp5YnvjN|F+Q$e;#C9Ե;䧬6!{k֓xӠs`eψ_ETޔWSbw}c`f*aD]oֻzL s)US$fJGν}Mt\ۡPknډOpПe+~A|Zۃv$xXno/q&drD,\2Ph%vȘ6Y\3ޓa.PϜ{ܔ'tGsşan=aVK\Hn鏑EVK^ԗZh* IBYA! ,bO;6wܩL|Sb">2d)+ի!,6owDK{ 2Kyb,/m2頞ѣSg=CFЦ ۱i)Z *w&FC;PX)3 ]~led8nw-@I~j{1엮[ $sbtZw {4E޵"u)unN{X,ysR+"` `ύ$.:TE¨*:ۗ6c)Sӂ\taaq //eRm}eAEWoGˮ[wCcc^P2Y;UV_FBqطyh82nbgq,X(W[|^X"|B/^Q6@K/-)cE'R 0.#''22D ?@j d4aRߪHmָ/xo"I^9;_ł+;'K ,:Y-9G)|"% u6m䫕 +,m[~&8@=10!3Kt7rȿ*.GbImD`StJ]UWZkH=%{945z@ܺeB!soSey sg|.yH2o#YFKO3+; A^7(id\t5=jS8IV暜xVAya,$!2(uQel+MzHf{ j=ӯۈE]E2 2"?@@0b)+iCn`!lpfd V5!r^:s,Ӳyt5& %-5AGqMo1iҫB.[,DUSK4P(fhB`/XUxGdu#Qzm󆜰Mp69b6|8BN Kb)4r MC 3ǮT"*Ls_Cmt~Xk7 .qG QJfG+"PـF/!'cIx?>vi=Jrr\ۺz9ԇ07oGlh'XoILkAR6älK c'tK~߼^_:-k<P<'|40S}@$ L/VPteLw.(*(Ο(:'#kY ׿W:B ;q ʮg:8rUAy.x7'$1Ϩ}Sf^.KPOIͺ1[#d)t,r5PlH@,ө{ovP H}p<<a\rÊaE%](O"\){!8A 5Q(* A#NTB,<>ߺuelq<*n򝾄 j?mwI;{:Qn%碻B|6Be5anid_%XY0۵0v,94vUg~U*%5kgN{,YDOš5QSWBCrX~XH4< I>'3RhHrqQnUWI9Ucrؖk|9Ebͳ[^+O2F\/a +McЖLy m7y'W/dMh ν0*KW 酆10^Fc[cNB%,B<]ެaK,8;v+l?B}ԖZ"&?/L",~ N".Rd0K۳07%5FM*>c^ĝQ}X_ݴV4^ ` t 6 Oz-6թ 70Nw_SuC}I_x>A;VO4n͊8$! _ZE)VRTeY掰rǔoJJzGw>T9% uWbDiS'voSd ;$+۰q!|/C522S?@"墁qU\OE]ϛ.e't-JTZ" F9#_ʑ_ Dowfbnt-H`H6ly'OerZ\  0ڙD,m$nЎZsa͹X/xj'ӻP:Ԯ)ŭ.GwGԍmDZ9#%R=^Y\Q kwP 7cQ:<̊I&Ph׵q]cJB#w- gG"= $EZgY~`WMMr# (_IPf0;R =iY8SY&/ۣQ:-uO-及[nm9ڨ`hbrXP$QTBNF"P,8qh, V+Qk:K\!%e⦚At4R~wA/xaF4In nb@z&x*BZw0I2B0GGA fMTjP//ȓKA:d(&3bu{z{1mln |-oD`H)v7yƷ0niؐ.+?@|zg#*$B9K <`L0F;ۢ?jK!7T/8ؚ{}<|g;ClT8Mjٚ'KӔS\v>G5pzYd.X*F02 2D ?aDY bE[gL_E,x]kҌԵ^}6mS+{љ#F>WFoIέD ǭ}FA +q]7j%4Sh6|3f G\AYˠ79IBwTTGj3btk5Uw߉ 79jT5ő@KYOqQg?Z%AἦiV# 9$ ?jv8{SmhTeCj!.^EbW$ŠAU-,˼axSgbGݠǧ:Uk2)olf" @;@9̀6({!fϞB`UU|i.6/.Rtl [i.1ܩgbpEJISC 0S F9l,IժE@4-W~ MP.26 (fbKX{b,wyV9&V%4&w]LG5P?kNMBG4bQ57LH`( ?_0[\Ui& PDz@-q: {ϹLT|P2G`ihg71U(԰ň7KGPͦ,tlyFE`]Q78pԌ['_Hn G5 ؠ"B骟z韻V  B22"?$`ڀ23NUHںbF{B}9luk(#هB̺߼wغo$>WL>lvr+w+uxxވ:F 4A8PrRŇ)Z$bXRaS_|$~H3Fo|\3ȮujVYNK\{t6X!e:HVN8z.7w9*i"u%1u{='h샋\@ߨam )A'-x;;ӪR!fa {DT@}ʹ1#2(8Îx5dzb|dDqRqQIL_|9tz|^|>ˬdarl.~nE xRTTOJAKRgS$>2W. ϷN`ΐ r :/F:!+B opMp1c%i"Z4 47GkF` ԝ0"&f5xb7JIUq6"N[Ϸ;a M*^4GGL#c@Y?/ iXkqdd&+S,fQM,o {M?{x+A.B8aڗZՍyRO % T~AR!< kJՋNGn&^Lti^pbBqkhHƁ \ƀc)b8b{)†iQqZN8s]jfZp/M{) )H`Zl@:IE92 gh8"׿p{0e(!E݋T4]lڊWU}!J!tf0iCoXrt}he]( 71>۟Ռ'VG{]ֺ:F^oN9<{3 mÂ|cq4}$ImGPr[CSCJ;d=w8aNˉ;-Gry҇XLs.FzPTLu ?Dہ2 2h?@apb'@On/Y%x[6w錫(q|} pXQذ us|9(Yۤe!ց80D?y$~"eodsE FZkꉂIU\COE5T8LZp}HZLi۹\~m|lNuC$AV*<#Aat,!Hx@h"9e[]%yG8 tQE3G-\=;!/?P׊Jk$ ѕ'4\鹶[nU;F "v_&8!8ֵ'+eBܵJעzfNm L$td"Y=7Cee]L5y<ɋ/.NE=H;',Nե0m# ¬7Ѷf&,:>{C Jn @[Ǒ_1h =W pÝX;fǻ%ڈ I%lw N˴DR0Аs0؏na1c!?,f/BFtk=󭹎zaBQ_â2_Q)V h jz giV#|+7!˽@ls\1^q#[rmd:tl|pأ6vB+f9WPᠯ)S6~T )a1Rڧd0ֻ7|&= {ɼG =u.A&Lgsd c.t|NxLb2B0u!ޞ$ ` K ߏO-*nJ'G8j0"F(ډ9=WG\Dt2}#hnJӃ0)|y4rEL*9<UlD87A,́oyW9)bw\ %;ѷ8so;,LSC@>9 h3 J#'_1oVaߧ1H  kE7mkSGCi+6_6su Sfeo&5#LӜETcƑ6!rjftRn'ʁ#zT)IVҒ_;`3/x}0_VW:3W[G^z^2Y1 (C`فLwt'fgsS~2"!CJ߆ ԰0 sqH0 4)&"5"1]Rt2J+lJ@^V/ZDo0g]O| _Khd7 lݜ>ʧ/ZM"ϭԉA"-6^xl;T`,n+0dB (n9xc;hMJΠ֐{Pp ~ y31T*~tKS u'?O ,xdK65q~!/rPhVDpk"Zd08e Nb(CvD -"Cjֳ 1a=6ꖽ ]ʣ h^8=}kp!r 0D\c:wnͻUE`Yeq&rgΙ/Gyk-km i|fui (5P hwY倡BɎ]".E^n"2Gk_󋭾 ^O'ĺB YW E[Uz$P[gn*L s= $+@.1j7Cx= _P}R[.x7I0:VIn`?M]bWԿ'$?F8ݦ]٬?>#IОYm}VCEf9}NnB yxn-rF^4% \`7jO-˻[7t@!_Яp F3cɳ7pIr\庯%!yRZŨMxqYOwd9)p";|ۏ#:Ŏ/X-9{yF,eCJ _#AJSH,R+:X>w{]LzexV*l%}es`Fa^,҅*8F&詩V/Ϡ/ZB  mlr~3UX '|w?W3f㏟9 Ba_-} tk ִ@Q~Q{l+0K(XRvA㉠,< =e(‰+6(a``VtjfO F.' ՐnH-@u'ۍb(x"i8t!zpZLPQ* b ;Oh3:|3nɋY[b$Y݇D5K%#HQ5 dΊR|U0('e|l$`vV;1n4{ZKr`y@ ~imr s+6;g/:VHa\~'z^^>{yGc*rYIuңa5dij- L`ڣD6~ʩȈ!qy-1s&Yff!؝N`K"*b(F܀ӌ`cK4u0yaVňw6#&~ybՎc pӧ,yOvP33WO?B<(2s5^ [Q+@T;jOh?-C"B,G=<2Gk^?f(.ȕ@E%F_Դˠ1Q+wƈ3&[yҙ3Ҷ6/k[XBidA\)^Tu~ u tߣ F1qR6vpqb:.0d2Vvȍ,T\$^Br3H kٸsg ->6z1${u9S &-pu8+%ln9cռ'nzڥHb4 Ⳟ"",G NKjШOct0W(j*v6 v-Cd~~Ya"dYhAal+iیql98KAgN&QC @ݩ۸d NV#t59v $+ł]B 4yXiiq' EuDsA[3F(<$'s q}(.ʇD({ с?i9F˼pī/jvI QC͔% _d[-qFU^E>%G=YOCu^tӅ%%lhq%CXH#l 0"x[Yls1͍Ta\tR I+M@^>. ߏ`KDFrt=YvՉuPϠ,ɑ(B;fc?h&4y#zs~p-NsihpdXDj,XψU*bH;dτ74UU\t_nA75j4#^2aBy/*j2༊jybQTs9o8]7tlH &iֺxh8jR0&yW68AI^ m]֣JWJo GEBܜ70ўA&J!)PE7ML嚷}%(M d똼-_vE7$ꛕJ\;;76ՈKl=&9: /{=_ ;{)B [Ubi_ *<tvd*=kאFWׇ;Ԋ0m Bx AmIsRB+1XKshR9#+Z2_ڡú:"$טSH/c +;r)ˡ~",Ȗ^w︕P1-{!"%y+&œXЇ?jl)~4OV[W!@a"}5=. bp3Qr™&Pvܭ[P^ņ)\⻤9Ey¨597վ{tNwjlēKmY ֤mseV k ؓ*Od (z]3*,)XF'w[Y$L~1n([z0{9 V/=J*PRTn;RJ MuFBGPu,0*U+O+D΀hx=l |Ib2** l{~g%@/,-B :Khr1qBiv& ?T+p=^o DVF9`RŎlhy=T *A Y뱧[7*zwL-6,/4OK(_?#'rؑ2ԛ? 3|;̯=H,#*\5\XeF|?U4D=>×k[jG ْV,T h5{f9uq3ó4[([5HQodDց2 2 V@D&2xFCd|RʢwIqNGD x;ڧf04({qKU&>˕>m^:W'9}n!CMe ҫecg( Nmb56B 7.| R}mSz%<Q=fQs]J !k ?)hpR;qrK#5m{O$˺$P{+I2^Oj>!IMɄHryJI( rZxXi^׆wxjhkNmv+~0:U]GJ \gP Yg$Q4<*a"a6;?Os%A맷(Dy!*&l(݃htxMNzLjp-yNdx8*W5О{Ri%~5A3_r* y?jZ)7X8JHq]ܙ#ގJal1!]Pr Na=|0V*S<2;,ǛcoÐ+pނ[ Qq[ cSwIH%E8d`vD[ K)ƒn$ \48kFI[2^B^S =4LYHiH;[_?Xpkk550{5-l[:)d(pxẍ́*bԜ o/@׃1%5lC7Cs~%LW@ÕSk-?gHJ yMn&ngF7 Uk$Qq:iwOjs2Z=Rd4}{+?.qu&3Mz7v+Y7RՙH=\4 /-M'WѨ4bcpqpE]਑di6侏)Y#B3(6Hcƾҙ! ȎKI 3Ot]L/00#PxU t['dtuv."l4m؉Ziy$8p,Q:&6e @-]y[~3?t~k{27^1#)薺ǐbr&~۶[ba1`48$YF{kV1`.E2!yFXmT<8 zCuPЈ$f[ 8nY}+Tfk5_Lo|h^l'Юv/uwfG9zNt5\zR8ClݡuuUv %&ՙ'`DP4:l!$Ub`I 6< wT_oXe]+1(w7)ϡ4ě^+قnzЧN(xl7鏾,h ΅q|2YsॽA}FSf5dͰ4U\2MYRzͭ!͝@k57 r3 䥂pWs iS3 aVzi `TWKK_jO)74.SExU 7q⩜mH,Ib j1Ȯ/ T:LX[O5W2Ŗ8& 871 gܫ2Zǥ31٢WlnE ?|>M^>Gݓx,]Fԩr{loYn CI(4~|/AmdYsl5.MKZJDB l22 VXz .,ݿ$T]F } T2Ϝ8ȑP޷#{י}]++?2L31w{1*JG)X|5 +?e耴a`Yv4O>EFKD]qRe?M+ּ&]LJVw$i ?p:y(gN! ?@1箓|DR!(cjvb|YPeXKȊh`$p:MZu[Nj+(hڜ )@z#P >\nAשd}*A`X r"hBE0qmMPBXu/yգ`65i#Bk ˆbωEkj]|A9Pi~+M CU22D@$r |m hצ [i`_d-H gTS!.6.,U`4H* hE cOgw49슫%2(;޴ ۯOloQ՟oK1LjO)0­e/:bey}{?̦sK˚蕹Z~);L=juQt1{_CJ}dAvś^o,50g\V˥A,?\/ɝ6L2pY `'KUaВETSޛ 2H*ЍoD-|^//s}j3b),qOd&G Ӭ5a 2|'+ 2{9@sTLD\xnkw{=-t?jyBBڶ26drڔoNC]k_:8"=Jŗ v1evg2r|*BNmWZW=3ovz+2L{ypGv?{O#NB@¬ $pWP̝0G#ˋlM"Q2.ac>Y ޱD7e 3SfT 4d+Iaadi4͹NJXIk+~'X]YkrK5.ŠIy<6Me—<ê FOLS 2ϴXPΔ َ/7b9?!BNկBڮ'`[~{,f,Mu,Ji0f~c)ħ#ʌ y lie]]#|[xC B<|5U͏3aJv1fdyvpan3q8lڏF!UL E=8u`j#Q2!)˟  rp0p~(}e_C~:u^׽4,puEb`|Ů•YAF>ŭ^gPyDsyQK1 I+O쏝uU<$z6qqzpYStĜF4_8S?ϢsGhĩn<~Eg,Sk\70y_E⇈@"fa K{96dm@37i3ilI]ޓp҃?C-'AСJD$HZHc5{$^@qųMR׀G%8tm/=S7޽vNX{t@A2M.r ASφpdpCjըM]"~:.C+E^p(PFjZ'_1 a쓝#ג6:=_o7Ϩ;rf ¦Z(}^P[ʕ;ェ<yܭ{IۄVA:c8-,S޺ PgReA/wK̪K L|A䋲j- aczw7UװαLlX>>, ?bN@#SrQ̣Aj>)RV w*3wQ /.A‘.oHMbnDS(\MƸUvJ `c]ݠyFAp``l9;ƿ0K^gs3|:PjX%"Zc1 +]z J WI5SV8cQX8,6}?aX+9vg-ceO,)xuz֡"4Ks’/^RVM} ,uFR}txe M;F$:9F6X-Lo9?9罻\SNǶQշ ֘>AQ.9{cG &s 7~F'#wP@M1~F&T Ķkf ڬdFI0<7@ xD1XDtnhT2,I MR1I.'Y! v=ʹaȨXuG 8ylM7_ˌEtⱋO*$(~8MHXwdqॠcs./ҔE!rE~Yu]N̳e5HdB5]ki2p⸔T *5pӓ6,5WSLܟ-x%Ÿfq kRDe#p4T F˝ݞ# A"L8j/tճH+׵M҅r6ؐ>$I2 5 _nn4:w&{AFH29zKYt_H¾&'Jj؃-7 r䢘]ķȻ4!.)À4fYk#=I| {F궫6)&@R@e܃16]e9)Tkasa]B+W^㶉u36O@# im;2:(^#^22^L_>6̈T %-KghzOP>lPTc^ ,jB<|Y4ℜy3-ng{1$A lq Kfheϧ 8|zXLGm`;^z΂eL3Ch4-SLFyE y_?΅\ wObogI ̧3-V/mq\fS (u jRdךKDC{kF`ĬEmc(sP7#rMaVڰ|q ԶL:whD^`:;>91>7!_NYNxJ[[mEٕqiݠ0)yN嵼hh}-ѷihQ1C!2>\]>`L0SBCH@̼yu}3-0,0;aAg: a|p0ƿvS{dK/c#hY }Ưb_swe!oGCd nj?BP5A`͂vX7I]ʗÿ́(N 22S?aB&js7 _m r K\aP3.b|eiͺ v1&w|C}? a '7vć%t!﬉Zq.~Hב =FB3c:]A8HpZv5=P:A>tX.4Xˉ۬WjI׏~,R{..KAj+^)]>d ׮kT!uvT-/ UM=Ŵ< MrʪcĶ_ lKy^ek+.[ltfF4LSZS^д ;RZkKjMhE0SCW$l]hyhCA5wF.B,R)k1Fbӎwid6(W'TYc8j )2|3 {rLJ@Dj<22D ?@<$>/VS uB:IQk_R4\nПsg2?(T ,%%6 VGal^W̉c</.Lן}\\K,/do^V_'3`g x]a!"vy~W%SY΋+;el$j[H6ŇM(7#(̶SYNg~K$:a]ztxȑ)d/ٯ}tL]ȧ%,'j':Uz =!(BE4BIڒfAǷ ͼo?u>'͇LKHscp*D Gs] `]&!mŷEA(60-j zYj A7lvOfh}iy%E`zyǹ<{7ٷfb8^!m5q-"k8Ϸb|=YJ2gqsd )y?5$+nZx(g287icv'Vߠ_`f i<~J$(4H Wm'Hkc DK>Ϳ}2c E3NQ(lGtvV2/Vx.X0l,r^:M8w1r>FNmșz]E$AdJB l_aTLSYF-`Ew+huJn㶛sܱNT ZCY%ΨFE5}Fȍ8OHK BG#?~͑8*GkH knjFgS^aoGG`1lǜg3wc+vdR"pO0K4+"*B=,HBHQ1m%92\?rʖ\!ʴg6x`u$>Si$2FL!PgV3ͤ-Sp{̎th@D22S?1(" ` h(?Dzʪ`ڇ0k}|_H͡ ?ܤkdg(2aÀdJombr]YT]ß~#N{va_u&RtE۶ǞzkZzFcϢugΝ '"7ϔ(.VXWd; {')OMvuK.5ܖ vV aUԨ5tgb#2Of:E`;,gk,X5b/!IJVHZ;Ug]V([+CnVCGT~f5Lj$GEA VMftuwkZ8zl^Y%攴%,PX/5ooX36e: H-]6d-@hpܶ7ob#l85?xm aoU!D= <}sK8˚ϒڻe݈\T\2$8)cN=%*أPo2TvA /t#\X `?y;ssZSIG4REXfg:BRB\g? 2KVf\`^x-73J2o64% ~cVgnnm/M}OEg W/χqĨaj)> Vmr&D]a;q Y9+_Y} n'7}~QfNp B5FV PocK2f _3/A;!dXd&xuK+O(g%A(g[# B22"?@af0"Hf@p 2hY }r`:o.d l̊ טmu۸7QfCq pBe4 oFS]rT BݸдxDakP֬}lv;($ϩ8]~CBA 禗. sm 飒gy,rvΞ\5 ۥ3>Nh6;YarXF@c1(#|1Z"!![+H6}KF |ʨ ?~=c~dcpSHLɾN">AD0jca;zh60iWTPS eĔpjɭ4KpWfUiU>z:UY1"5f_ڸ)./kN o2>ͅZɋxWU8[Aㄆ[8Q&CͻOߕѽqe\/+ܻeg vȗChoK+w>ht V:"A8)!Qa >(sD&722h ?@Q  P UD26(%d3 Tz,Pw? okǹ ℉=pUmq(eF8txblr|vdF]W@#0:O Eݺo|k+yEφ,=Gj<#ypGȫm+z`35yFzDZx |bWAާ 2Ė՘q1Xaq,k'W !NW(Ʉ?I&nsqNQA:$ Ȁ㧔`kysZcn4 K 4ʝ5d "h1(Jq"̥-QaF4&GIeaB4?d<VgP h.+Γl~ڥccDU*HT&Q^hSⰩ< [ eny+|`ϨbN7IyZmSaT"7yeե+`{}*92Y JCnLy?s714]Q"=ዅcY3,X ÏV1k m(w~xykIAMfDH|ZKRVԪ/q= ITXone_@?T,Ĵ\}܂ٌ oј | { >\un]i֟HK )f@A$V [`'y,4Кfw`ZH8o5g.:j,g-b??h*6Oҟ#c{B#V.]nku]^(hvr\׉I&NJPbF8Ks:w,01ȁ7%EN5j.viEayG_٥PI:?Y O?sg|a :E{U BڷN`2! J߆S @F hE!˨DrWJrC69ud-ńXvFM|ߏr1tiG+&|+YSTrs^n"QCX}9&tJ JmE(k D.CQL>[/z9 CˆԌV(wTkXѭ.x1p{#x43vHW?/PV]?./&%ժޮR_h ޚl'SU%[yn\cB.29u|T8{Н_HLڴ5о4/O{h _X(vx}WO;6Dn:-)@bF[uWî{2A? 5z,Q&|Qj xI3xZJ>r- F?e9Azh/ɥQgHbXN5Jy`/vQ6C]9EEjjm:1y*LqӐl|?0, +bW.mITI]ewd/&j4RN!#=1c{ܭ<uu~HdZ 洫X3MCC: wXJ^ڶ}&0llEۏk]u*k.U7[J}az Bכxߢ7SbZ]XqV,Hl=d dZƶ B0WLa{~NLKNM4_ݻ&u!?1ؽք<. y#)e6{2tBE|[t"1dP3JD8JsdH{rڌvi)6Ss9~|uf6cL❽0B4j\SvZ&DTP[A_nNuFF*U?o^Q"p8$Q<YFqD6)ƻ۱C_#R-e/-a|K(܈M/h -l^AgB.c.yi ,NX PoC! `!"Qشr4ooݿ 큼X2K,6cj&+_yICա;?Tnʠ }9cwqA4"1ˡb1}RUp-Xw5hGtvgٲ#vLKRڱ"u0BI*e\Ԡ Ib}3VCCYE6p2 <҃qTP˪kǦW`ﯟ҄<7hgnGdN7Nh졨 ".{EO3zOnXMEer&+oeVm٬]i^Bz榱\x&TfPmeq 7!$A![Ht)"2p\2N1 (|x OYE 6m9::P'P5C63C^Lp`Ap&lS wDX_0wO͸A2}ahC_+mBܪ3=r9C#8="$T%B;VڇKĉO ¸B $TP.P{-C!~pU ;F8</Po9%,ع31tBInzݶn!㉊\yOGyy{/,vϙ5,R0X8fٞ~@UKuz74]=J8e |t4N0f''BVҶ>=^nJRe,R `.PC( eH%ID-%/95˸wCyELfLܺ!g9z4?$A/K@]6E!>I(b;]g`߻["hM3jWz̥zk$u.[`urmWmղ@Ь /w@󸰩1R~jqVFn7$>wuႩ6C[zUէQ &0!͒!'QJ$T CM7IH,E0db&Fp(4@+Y ՟U规d阎\WFIv5қ5W^B;u7ϗNC̸elSW3iƈbS"$IZ7eKć7n5:OhQ{ #oY@F}7OAw^a:~=%o[)-?I|y&/gd]V eQwC[ ~D@f22D,#xyMl1m٪^2ו.W=v)F`0Pq㌃8rw;{>NM_SU s1)X,|gYz-8aVj:aP:'1r<\xkbnm{gʻ˱CLjxOqƺ4L0HʣuH0}}H̙&eSxhj`n1]5Niū Fw FZml-_Dz{ Jߊ3DQP}R zYIdR`yu{jhv|+$@"k8Y.:5Ī_ ƣRx#t ^Qq2K-NbBv %?CxqrA.ю cj5^{gbQ5Ad><Z(!$#3!@.h>̓ʈǨK9 5 `rq~] Y qS/DL22"@$(3`ay"LBRBc,u )[:rNu4WV[{pئ,nP/" Bdbҽivm;Z?JPWCĕP2I7 cjTVx R5^1YhP4F]nF}FNߚD+_h0[fJ7s{I(f䜆v-i:)X;FqoSLXpUiݼ%hl c=S9k*5~pibO> t_҉-{ ܟ[3E#f#?rk'9UXرK#f\Wj^[x+vBk[2V%0S'4OpG\weG!BڃVMtjٮ>תIzQ7*CTuN(7 d4m =l{q#CTˊ_O ݸ7((1\3DiY@$3z"6[ڇ cs [rɢ@i@뉿¯m uTLh쿺͛qD˫F2\gsla}}aP%ݞZ_U 9"d7k)$1DT5[vd;,7i,wCWy)I?V/}ScZ]Ck=%!'E*wIs΢XibŶ"_p8[>mӶ[ģ FqΦ9$ j{P~솛ܫXWq0oF&,w37=Z|6:ټ}nd?$m?PsnNcJ_`Kt0iT ثݑm尲,4A22V p@`p6bnJ}wd&JZFY0zj6-9Xz=Z5&@o-qtBh4=R@Zre!K2Y6/֍'r# s(̣ݏprҐ|qG>i=:]RC%vV&2~D$ >~MΪLvPj;o \30O#ZQNr)l!YV1cx?3gjs/afCwZ~S߽ҹ="`ro?| !6MeIs=+$,8\n4q-Lq{\bHCִ6PW?QplUQhCq22Dp`Pp@`Ei{-]%ύ!| BU̗rWbhYjmevѧ8Ln4~- y]C%sB QJxq 3.5Z?ڼy7nѦ]6vQ]Srd Nycb jϳAJ^\.0d]VG%S=|RЮGN¨UF?xߣ]nuoO0bO4^X\{"ၟg ^E't /:y%RWC&k#37Yk|8S5Pa4o~š33vc4@@_Thg7 (rwFb.VudÔzX˝1B]=r,SDi:DwNYZ*Ġ",qMwc&8۩^Hns@0lA.)Z 8M6H&$L=>M*so@KZI.S!Goxjv!食]4t)'K3B( x!ppxT T$˾|h2!5rVN_.JBT$=E@>T)Px#st`Pt[my3D3J@HZf&繉z1Խ*Ⱄ@>2kHҵgWl~ۿy.P d+Bme* 2jEnt(6m U5_'_fs#u_[=6烏Úh@Ag22"p-=*#IꞙlhZxPjxY ÓpKYy*]a BX$oYB6',mVW劕F/j bgR_TCdOhsX"l-=au:1_ $fc7b!E*r^lS5T9|_TbA@>6.}WYS§>R_.R'd(ST=ax#c]iƞiѢ* t [1ϵ z)~Ôߏ^Tf,YhC{qh&!%j:US˓dA122V@ p `fmIm3GtgB8v:6W-Z@[22D`Y7[ȣSTҤ_ _yp*ɠ/cJ T`=lp%pm/ Wc:%+ǐp }li?X_*GأB22 a@q"#KPV)OE7uwVAc> _.0H2ȭc\g>⛵dO}|G>Bdv;!ۥH gHkD*1Yt.+HB&:cɚ@R?=_ϓ88ue\hD wH5̯3װ5h,TZ|m3vEIP,R¬n@zp$ٖEuxHN_q7 V@02 $?ˊoU ԟ'cD"Xt5ᎰtK~L>Ƙ13b1@0xGUMO%Zd/u-.&k?(CMihfƼ) ٲG8[cV~l '71vG SME5W:y8 ۾@ CE22"S?@B `6;3Fp~q8w&Q c^ܠjΓV gpk19 a@cܬUm]gs]%&M7ھ&hibl)l;a8K,y\-K+H˕4T%sF)qe +Ԝ(w*z%6kǢΠ%|Lڬ<'jҨ&W]<[Q|z'<=dNZzfwxOijTSיz?[Ӫ;֊@?_]psSQ22B:#(iKzoI-2E<+Cf4ade]Y?k"xgrC; Sh64)5-7΂=Զ]߭a]VO` <@5ܔD*[48twՑnxX~:אŝlp}#|q/.V$Pg’TMk9/rQfV\/^1:i}}kKX]kH8$}zRa߈Qu,|050v<(߀mngz[rBwB22#D ?7J3фұֹ,(˄4>&O/@=ڋ10  A7" '㖂NUܱ;\ )dGi|jL N_7^f|FsSN![58d[B"P+y>0!m&i|+oa9>kGSKl uRK~U'B1Q s(ZϬ1Tݕe9d30jG:ȭ5 !/ HR2i{_+-^3p1m^ZXK;,K$63Y շZII=&Spُ؜83+}&,7㨞8{0,SlMc~}AT^Úo0MjVu{vC+%ml_:Ve+*j&_w#Wba>7+O:!{1x(2)eQ@(-`bIDc+22$"?P$`0h0jPt TU@D& {X20Gg"e32˫b-]WmO{L"nR(hZӛ n|ݕ[ONg'y1n1mA %[$=yM(CHR$q*Akg&<oTw5dx Oh,KHB^CD u-)iuُ3v}ljz(gJ-"x~-V-2fWUnksbU EZ̲NQ5{u~CUv9p:$NA : G{O="d_IXU36\"-l-Ʀsr#j6o2%)6[P*Vސe;}pd`i9/G1 @0U݅aFUbV=w]8Wi<V,TږtZfPU'"y.tm&=e:*ױϱ2~,&[\H&T,6Xcς-q+}Mo*~EXc96%qM.G(AU22%S?Q @@@ c ST& T"`_}Y5'ץ8gMr\#"mCr(ʜ^Rx6Łiޥ/&K))3#-r,K~l>o4&! n\AEl%TkVCL{semOdNvm0@EF{`T;reESⰨBd%˹L.1^%zMM󷓂/ #]a5wo=^[1Ҡ_a_?@N1/Ma (FLeINIY:,O>74@Hme1trW'bdAK׋-KեCF.ߥE58 'Qk H8@C22&D ?@$A=!Æ`8%biSv/Gc791P~-I.|_Zk[xb>u1_u%a@Z*!'@n N|.* ]s#s8"Ey8m==og:zMā_Bc-7VsfagI"!fWJ1ߦI& 3WpR0:o"EpĦ9 md <")%m jpJL]D)?ѭ5)ԐK/ьVvTyi*Ax3fFί!"j52ˡE܅fvgaQRsG^ݑkC#pۓH`ċNԸ≠ikƷīt+q\f)ʃؼmeK֘U%i\PhU~ D问4!R*7/!/%V.ڙCaǹ $`)a0xy)~0Wo*~up5&{s'Q:S/0&i [@2m(I*G~t~3psWdKPBg ǩV0 >Plh+Ev ST):U-WGLZA7bqD[P/~)Q^͊.SoBj(hX-t2[fu쫀!Jڵs47i`ԸTRd'BF>f0w?!]Q`%2Y_b)AMyA0'_%k@jN&%5__w(qZMe$$r(vjqm9@с6AZJ[]AmU[lAx22'"? Q 1$'p%Tȣ̒Lc_; Qۭr"\"FZ9=@e(dZ*?"[ClJ8S1V\o:; V!c̤Y~_R P٠ԫ2/ه|_Σk09`ZKFiCSki appstream-generator-0.8.6/tests/samples/test.tar.xz000066400000000000000000000240001416066706400224500ustar00rootroot00000000000000b/0000775000175000017500000000000012757035064010314 5ustar laneylaneyb/a0000664000175000017500000000000612757035064010453 0ustar laneylaneyhello